comparison service/laundry/laundry.go @ 109:868e759aeb74

strings for all hardware values. rewrite rdf graph output. gofmt Ignore-this: 9344993fdda111d34191c11e3cda17ec
author drewp@bigasterisk.com
date Sun, 01 Sep 2013 01:35:36 -0700
parents 08eb981b6cf5
children 94e26e1e0668
comparison
equal deleted inserted replaced
108:64ad5ad17dc1 109:868e759aeb74
1 package main 1 package main
2 2
3 import ( 3 import (
4 "bitbucket.org/ww/goraptor"
4 "encoding/json" 5 "encoding/json"
6 "errors"
7 "github.com/mrmorphic/hwio"
8 "github.com/stretchr/goweb"
9 "github.com/stretchr/goweb/context"
5 "log" 10 "log"
6 "net" 11 "net"
7 "net/http" 12 "net/http"
8 "strconv" 13 "runtime"
9 "fmt"
10 "time" 14 "time"
11 "runtime"
12 "github.com/mrmorphic/hwio"
13 "github.com/stretchr/goweb"
14 "github.com/stretchr/goweb/context"
15 "bitbucket.org/ww/goraptor"
16 ) 15 )
17 16
18 /* 17 /*
19 hwio.DebugPinMap() wrote this: 18 hwio.DebugPinMap() wrote this:
20 19
40 Pin 26: CE1N,GPIO7 cap:output,input,input_pullup,input_pulldown 39 Pin 26: CE1N,GPIO7 cap:output,input,input_pullup,input_pulldown
41 */ 40 */
42 41
43 type Hardware struct { 42 type Hardware struct {
44 InMotion, InSwitch3, InSwitch1, InSwitch2, OutLed, OutSpeaker, InDoorClosed, OutStrike hwio.Pin 43 InMotion, InSwitch3, InSwitch1, InSwitch2, OutLed, OutSpeaker, InDoorClosed, OutStrike hwio.Pin
45 LastOutLed, LastOutStrike int 44 LastOutLed, LastOutStrike string
45 }
46
47 func DigitalRead(p hwio.Pin) int {
48 v, err := hwio.DigitalRead(p)
49 if err != nil {
50 panic(err)
51 }
52 return v
53 }
54
55 func (h *Hardware) GetMotion() string {
56 if DigitalRead(h.InMotion) == 0 {
57 return "motion"
58 } else {
59 return "noMotion"
60 }
61 }
62
63 func (h *Hardware) GetDoor() string {
64 if DigitalRead(h.InDoorClosed) == 1 {
65 return "closed"
66 } else {
67 return "open"
68 }
69 }
70
71 func (h *Hardware) GetSwitch(which string) string {
72 var level int
73 switch which {
74 case "1":
75 level = DigitalRead(h.InSwitch1)
76 case "2":
77 level = DigitalRead(h.InSwitch2)
78 case "3":
79 level = DigitalRead(h.InSwitch3)
80 }
81 if level == 0 {
82 return "closed"
83 } else {
84 return "open"
85 }
86 }
87
88 func (h *Hardware) GetLed() string {
89 return h.LastOutLed
90 }
91
92 func (h *Hardware) GetStrike() string {
93 return h.LastOutStrike
94 }
95
96 func (h *Hardware) SetLed(state string) {
97 switch state {
98 case "on":
99 hwio.DigitalWrite(h.OutLed, 1)
100 case "off":
101 hwio.DigitalWrite(h.OutLed, 0)
102 default:
103 panic(errors.New("unknown state"))
104 }
105 h.LastOutLed = state
106 }
107
108 func (h *Hardware) SetStrike(state string) {
109 switch state {
110 case "unlocked":
111 hwio.DigitalWrite(h.OutStrike, 1)
112 case "locked":
113 hwio.DigitalWrite(h.OutStrike, 0)
114 default:
115 panic(errors.New("unknown state"))
116 }
117 h.LastOutStrike = state
46 } 118 }
47 119
48 // hwio.GetPin with a panic instead of an error return 120 // hwio.GetPin with a panic instead of an error return
49 func GetPin(id string) hwio.Pin { 121 func GetPin(id string) hwio.Pin {
50 p, e := hwio.GetPin(id) 122 p, e := hwio.GetPin(id)
52 panic(e) 124 panic(e)
53 } 125 }
54 return p 126 return p
55 } 127 }
56 128
57 func DigitalRead(p hwio.Pin) int {
58 v, err := hwio.DigitalRead(p)
59 if err != nil {
60 panic(err)
61 }
62 return v
63 }
64
65 func SetupIo() Hardware { 129 func SetupIo() Hardware {
66 // return Hardware{} 130 // return Hardware{}
67 pins := Hardware{ 131 pins := Hardware{
68 InMotion: GetPin("GPIO2"), // pi rev2 calls it GPIO2 132 InMotion: GetPin("GPIO2"), // pi rev2 calls it GPIO2
69 InSwitch3: GetPin("GPIO3"), // pi rev2 calls it GPIO3 133 InSwitch3: GetPin("GPIO3"), // pi rev2 calls it GPIO3
70 InSwitch1: GetPin("GPIO4"), 134 InSwitch1: GetPin("GPIO4"),
71 InSwitch2: GetPin("GPIO17"), 135 InSwitch2: GetPin("GPIO17"),
72 OutLed: GetPin("GPIO27"), // pi rev2 calls it GPIO27 136 OutLed: GetPin("GPIO27"), // pi rev2 calls it GPIO27
73 OutSpeaker: GetPin("GPIO22"), 137 OutSpeaker: GetPin("GPIO22"),
74 InDoorClosed: GetPin("GPIO10"), 138 InDoorClosed: GetPin("GPIO10"),
75 OutStrike: GetPin("GPIO9"), 139 OutStrike: GetPin("GPIO9"),
76 } 140 }
77 141
78 if err := hwio.PinMode(pins.InMotion, hwio.INPUT_PULLUP); err != nil { panic(err) } 142 if err := hwio.PinMode(pins.InMotion, hwio.INPUT_PULLUP); err != nil {
79 if err := hwio.PinMode(pins.InSwitch1, hwio.INPUT_PULLUP); err != nil { panic(err) } 143 panic(err)
80 if err := hwio.PinMode(pins.InSwitch2, hwio.INPUT_PULLUP); err != nil { panic(err) } 144 }
81 if err := hwio.PinMode(pins.InSwitch3, hwio.INPUT_PULLUP); err != nil { panic(err) } 145 if err := hwio.PinMode(pins.InSwitch1, hwio.INPUT_PULLUP); err != nil {
82 if err := hwio.PinMode(pins.InDoorClosed, hwio.INPUT_PULLUP); err != nil { panic(err) } 146 panic(err)
83 if err := hwio.PinMode(pins.OutLed, hwio.OUTPUT); err != nil { panic(err) } 147 }
84 if err := hwio.PinMode(pins.OutSpeaker, hwio.OUTPUT); err != nil { panic(err) } 148 if err := hwio.PinMode(pins.InSwitch2, hwio.INPUT_PULLUP); err != nil {
85 if err := hwio.PinMode(pins.OutStrike, hwio.OUTPUT); err != nil { panic(err) } 149 panic(err)
150 }
151 if err := hwio.PinMode(pins.InSwitch3, hwio.INPUT_PULLUP); err != nil {
152 panic(err)
153 }
154 if err := hwio.PinMode(pins.InDoorClosed, hwio.INPUT_PULLDOWN); err != nil {
155 panic(err)
156 }
157 if err := hwio.PinMode(pins.OutLed, hwio.OUTPUT); err != nil {
158 panic(err)
159 }
160 if err := hwio.PinMode(pins.OutSpeaker, hwio.OUTPUT); err != nil {
161 panic(err)
162 }
163 if err := hwio.PinMode(pins.OutStrike, hwio.OUTPUT); err != nil {
164 panic(err)
165 }
166 pins.SetLed("off")
167 pins.SetStrike("locked")
86 return pins 168 return pins
87 } 169 }
88 170
89 func serializeGowebResponse( 171 func serializeGowebResponse(
90 c context.Context, 172 c context.Context,
93 serializer := goraptor.NewSerializer(syntaxName) 175 serializer := goraptor.NewSerializer(syntaxName)
94 defer serializer.Free() 176 defer serializer.Free()
95 177
96 str, err := serializer.Serialize(statements, "") 178 str, err := serializer.Serialize(statements, "")
97 if err != nil { 179 if err != nil {
98 panic(err); 180 panic(err)
99 } 181 }
100 c.HttpResponseWriter().Header().Set("Content-Type", 182 c.HttpResponseWriter().Header().Set("Content-Type",
101 goraptor.SerializerSyntax[syntaxName].MimeType) 183 goraptor.SerializerSyntax[syntaxName].MimeType)
102 return goweb.Respond.With(c, 200, []byte(str)) 184 return goweb.Respond.With(c, 200, []byte(str))
103 } 185 }
104 186
105 func namespace(ns string) (func(string) *goraptor.Uri) { 187 func namespace(ns string) func(string) *goraptor.Uri {
106 return func (path string) *goraptor.Uri { 188 return func(path string) *goraptor.Uri {
107 var u goraptor.Uri = goraptor.Uri(ns + path) 189 var u goraptor.Uri = goraptor.Uri(ns + path)
108 return &u 190 return &u
109 } 191 }
110 } 192 }
111 193
125 panic(err) 207 panic(err)
126 } 208 }
127 return literal(string(rfc3999Time[:]), XS("dateTime")) 209 return literal(string(rfc3999Time[:]), XS("dateTime"))
128 } 210 }
129 211
130 func twoState(
131 graph *goraptor.Uri,
132 subject *goraptor.Uri,
133 test interface{},
134 trueVal interface{}, trueObject *goraptor.Uri,
135 falseVal interface{}, falseObject *goraptor.Uri,
136 ) *goraptor.Statement {
137 ROOM := namespace("http://projects.bigasterisk.com/room/")
138 var motionState goraptor.Term
139 if test == trueVal {
140 motionState = trueObject
141 } else if test == falseVal {
142 motionState = falseObject
143 } else {
144 motionState = literal(fmt.Sprintf("%v", test), nil)
145 }
146 return &(goraptor.Statement{
147 subject, ROOM("state"), motionState, graph})
148 }
149
150 func main() { 212 func main() {
151 pins := SetupIo() 213 pins := SetupIo()
152 214
153 goweb.MapStatic("/static", "static") 215 goweb.MapStatic("/static", "static")
154 216
155 // this one needs to fail if the hardware is broken in 217 // this one needs to fail if the hardware is broken in
156 // any way that we can determine, though I'm not sure 218 // any way that we can determine, though I'm not sure
157 // what that will mean on rpi 219 // what that will mean on rpi
158 goweb.MapStaticFile("/", "index.html") 220 goweb.MapStaticFile("/", "index.html")
159 221
160 goweb.Map("GET", "/status", func(c context.Context) error { 222 goweb.Map("GET", "/status", func(c context.Context) error {
161 jsonEncode := json.NewEncoder(c.HttpResponseWriter()) 223 jsonEncode := json.NewEncoder(c.HttpResponseWriter())
162 jsonEncode.Encode(map[string]int{ 224 jsonEncode.Encode(map[string]interface{}{
163 "motion": DigitalRead(pins.InMotion), 225 "motion": pins.GetMotion(),
164 "switch1": DigitalRead(pins.InSwitch1), 226 "switch1": pins.GetSwitch("1"),
165 "switch2": DigitalRead(pins.InSwitch2), 227 "switch2": pins.GetSwitch("2"),
166 "switch3": DigitalRead(pins.InSwitch3), 228 "switch3": pins.GetSwitch("3"),
167 "doorClosed": DigitalRead(pins.InDoorClosed), 229 "doorClosed": pins.GetDoor(),
168 "led": pins.LastOutLed, 230 "led": pins.LastOutLed,
169 "strike": pins.LastOutStrike, 231 "strike": pins.LastOutStrike,
170 }) 232 })
171 return nil 233 return nil
172 }) 234 })
173 235
174 goweb.Map("GET", "/graph", func(c context.Context) error { 236 goweb.Map("GET", "/graph", func(c context.Context) error {
175 DC := namespace("http://purl.org/dc/terms/") 237 DC := namespace("http://purl.org/dc/terms/")
176 ROOM := namespace("http://projects.bigasterisk.com/room/") 238 ROOM := namespace("http://projects.bigasterisk.com/room/")
177 239
178 statements := make(chan *goraptor.Statement, 100) 240 statements := make(chan *goraptor.Statement, 100)
179 241
180 graph := ROOM("laundrySensors") 242 graph := ROOM("laundryDoor")
181 243
182 _, thisFile, _, _ := runtime.Caller(0) 244 _, thisFile, _, _ := runtime.Caller(0)
183 statements <- &(goraptor.Statement{ 245 statements <- &(goraptor.Statement{
184 graph, DC("creator"), literal(thisFile, nil), graph}) 246 graph, DC("creator"), literal(thisFile, nil), graph})
185 statements <- &(goraptor.Statement{ 247 statements <- &(goraptor.Statement{
186 graph, DC("modified"), nowLiteral(), graph}) 248 graph, DC("modified"), nowLiteral(), graph})
187 249
188 statements <- twoState(graph, ROOM("laundryDoorMotion"), 250 for subj, state := range map[*goraptor.Uri]*goraptor.Uri{
189 DigitalRead(pins.InMotion), 251 ROOM("laundryDoorMotion"): ROOM(pins.GetMotion()),
190 1, ROOM("motion"), 252 ROOM("laundryDoorOpen"): ROOM(pins.GetDoor()),
191 0, ROOM("noMotion")) 253 ROOM("laundryDoorSwitch1"): ROOM(pins.GetSwitch("1")),
192 254 ROOM("laundryDoorSwitch2"): ROOM(pins.GetSwitch("2")),
193 statements <- twoState(graph, ROOM("laundryDoorOpen"), 255 ROOM("laundryDoorSwitch3"): ROOM(pins.GetSwitch("3")),
194 DigitalRead(pins.InDoorClosed), 256 ROOM("laundryDoorLed"): ROOM(pins.GetLed()),
195 1, ROOM("closed"), 257 ROOM("laundryDoorStrike"): ROOM(pins.GetStrike()),
196 0, ROOM("open")) 258 } {
197 259 statements <- &(goraptor.Statement{subj, ROOM("state"), state, graph})
198 for i, p := range map[string]hwio.Pin{
199 "1": pins.InSwitch1,
200 "2": pins.InSwitch2,
201 "3": pins.InSwitch3} {
202 statements <- twoState(
203 graph, ROOM("laundryDoorSwitch" + i),
204 DigitalRead(p),
205 1, ROOM("closed"),
206 0, ROOM("open"))
207 } 260 }
208 261
209 statements <- twoState(graph, ROOM("laundryDoorLed"),
210 pins.LastOutLed, 1, ROOM("on"), 0, ROOM("off"))
211
212 statements <- twoState(graph, ROOM("laundryDoorStrike"),
213 pins.LastOutLed, 1, ROOM("unlocked"), 0, ROOM("locked"))
214
215 close(statements) 262 close(statements)
216 // type should be chosen with accept header. trig is 263 // type should be chosen with accept header. trig is
217 // causing segfaults. 264 // causing segfaults.
218 return serializeGowebResponse(c, "nquads", statements) 265 return serializeGowebResponse(c, "nquads", statements)
219 }) 266 })
221 goweb.Map("PUT", "/led", func(c context.Context) error { 268 goweb.Map("PUT", "/led", func(c context.Context) error {
222 body, err := c.RequestBody() 269 body, err := c.RequestBody()
223 if err != nil { 270 if err != nil {
224 panic(err) 271 panic(err)
225 } 272 }
226 273
227 var level int 274 pins.SetLed(string(body))
228 if string(body) == "on" {
229 level = 1
230 } else if string(body) == "off" {
231 level = 0
232 } else {
233 return goweb.Respond.With(c, http.StatusBadRequest,
234 []byte("body must be 'on' or 'off'"))
235 }
236
237 hwio.DigitalWrite(pins.OutLed, level)
238 pins.LastOutLed = level
239 return goweb.Respond.WithStatusText(c, http.StatusAccepted) 275 return goweb.Respond.WithStatusText(c, http.StatusAccepted)
240 }) 276 })
241 277
242 setStrike := func (level int) {
243 hwio.DigitalWrite(pins.OutStrike, level)
244 pins.LastOutStrike = level
245 }
246
247 goweb.Map("PUT", "/strike", func(c context.Context) error { 278 goweb.Map("PUT", "/strike", func(c context.Context) error {
248 body, err := c.RequestBody() 279 body, err := c.RequestBody()
249 if err != nil { 280 if err != nil {
250 panic(err) 281 panic(err)
251 } 282 }
252 283
253 level, err := strconv.Atoi(string(body[:])) 284 pins.SetStrike(string(body))
254 if err != nil {
255 return goweb.Respond.With(c, http.StatusBadRequest,
256 []byte("body must be '0' or '1'"))
257 }
258
259 setStrike(level)
260 return goweb.Respond.WithStatusText(c, http.StatusAccepted) 285 return goweb.Respond.WithStatusText(c, http.StatusAccepted)
261 }) 286 })
262 287
263 goweb.Map( 288 goweb.Map(
264 "PUT", "/strike/temporaryUnlock", 289 "PUT", "/strike/temporaryUnlock",
265 func(c context.Context) error { 290 func(c context.Context) error {
266 type TemporaryUnlockRequest struct { 291 type TemporaryUnlockRequest struct {
267 Seconds float64 292 Seconds float64
275 } 300 }
276 301
277 // This is not correctly reentrant. There should be a 302 // This is not correctly reentrant. There should be a
278 // stack of temporary effects that unpop correctly, 303 // stack of temporary effects that unpop correctly,
279 // and status should show you any running effects. 304 // and status should show you any running effects.
280 setStrike(1) 305 pins.SetStrike("unlocked")
281 go func() { 306 go func() {
282 time.Sleep(time.Duration(req.Seconds * 307 time.Sleep(time.Duration(req.Seconds *
283 float64(time.Second))) 308 float64(time.Second)))
284 setStrike(0) 309 pins.SetStrike("locked")
285 }() 310 }()
286 return goweb.Respond.WithStatusText( 311 return goweb.Respond.WithStatusText(
287 c, http.StatusAccepted) 312 c, http.StatusAccepted)
288 }) 313 })
289 314
290 goweb.Map("PUT", "/speaker/beep", func(c context.Context) error { 315 goweb.Map("PUT", "/speaker/beep", func(c context.Context) error {
291 // queue a beep 316 // queue a beep
292 return goweb.Respond.WithStatusText(c, http.StatusAccepted) 317 return goweb.Respond.WithStatusText(c, http.StatusAccepted)
293 }) 318 })
294 319
295
296 address := ":8081" 320 address := ":8081"
297 321
298 s := &http.Server{ 322 s := &http.Server{
299 Addr: address, 323 Addr: address,
300 Handler: goweb.DefaultHttpHandler(), 324 Handler: goweb.DefaultHttpHandler(),
301 ReadTimeout: 10 * time.Second, 325 ReadTimeout: 10 * time.Second,
302 WriteTimeout: 10 * time.Second, 326 WriteTimeout: 10 * time.Second,
303 } 327 }
304 328
305 log.Printf("Listening on port %s", address) 329 log.Printf("Listening on port %s", address)
306 log.Printf("%s", goweb.DefaultHttpHandler()) 330 log.Printf("%s", goweb.DefaultHttpHandler())
307 listener, listenErr := net.Listen("tcp", address) 331 listener, listenErr := net.Listen("tcp", address)