github.com/simpleiot/simpleiot@v0.18.3/node/onewire.go (about) 1 package node 2 3 import ( 4 "log" 5 "os" 6 "path" 7 "path/filepath" 8 "time" 9 10 "github.com/nats-io/nats.go" 11 "github.com/simpleiot/simpleiot/client" 12 "github.com/simpleiot/simpleiot/data" 13 ) 14 15 type oneWire struct { 16 node data.NodeEdge 17 owNode *oneWireNode 18 ios map[string]*oneWireIO 19 20 // data associated with running the bus 21 nc *nats.Conn 22 sub *nats.Subscription 23 24 chDone chan bool 25 chPoint chan pointWID 26 } 27 28 func newOneWire(nc *nats.Conn, node data.NodeEdge) (*oneWire, error) { 29 ow := &oneWire{ 30 nc: nc, 31 node: node, 32 ios: make(map[string]*oneWireIO), 33 chDone: make(chan bool), 34 chPoint: make(chan pointWID), 35 } 36 37 oneWireNode, err := newOneWireNode(node) 38 if err != nil { 39 return nil, err 40 } 41 42 ow.owNode = oneWireNode 43 44 // closure is required so we don't get races accessing ow.busNode 45 func(id string) { 46 ow.sub, err = nc.Subscribe("p."+ow.owNode.nodeID, func(msg *nats.Msg) { 47 points, err := data.PbDecodePoints(msg.Data) 48 if err != nil { 49 // FIXME, send over channel 50 log.Println("Error decoding node data:", err) 51 return 52 } 53 54 for _, p := range points { 55 ow.chPoint <- pointWID{id, p} 56 } 57 }) 58 }(ow.owNode.nodeID) 59 60 if err != nil { 61 return nil, err 62 } 63 64 go ow.run() 65 66 return ow, nil 67 } 68 69 // stop stops the bus and resets various fields 70 func (ow *oneWire) stop() { 71 if ow.sub != nil { 72 err := ow.sub.Unsubscribe() 73 if err != nil { 74 log.Println("Error unsubscribing from bus:", err) 75 } 76 } 77 for _, io := range ow.ios { 78 io.stop() 79 } 80 ow.chDone <- true 81 } 82 83 // CheckIOs goes through ios on the bus and handles any config changes 84 func (ow *oneWire) CheckIOs() error { 85 nodes, err := client.GetNodes(ow.nc, ow.owNode.nodeID, "all", data.NodeTypeModbusIO, false) 86 if err != nil { 87 return err 88 } 89 90 found := make(map[string]bool) 91 92 for _, node := range nodes { 93 found[node.ID] = true 94 _, ok := ow.ios[node.ID] 95 if !ok { 96 // add ios 97 var err error 98 ioNode, err := newOneWireIONode(&node) 99 if err != nil { 100 log.Println("Error with IO node:", err) 101 continue 102 } 103 io, err := newOneWireIO(ow.nc, ioNode, ow.chPoint) 104 if err != nil { 105 log.Println("Error creating new modbus IO:", err) 106 continue 107 } 108 ow.ios[node.ID] = io 109 } 110 } 111 112 // remove ios that have been deleted 113 for id, io := range ow.ios { 114 _, ok := found[id] 115 if !ok { 116 // io was deleted so close and clear it 117 log.Println("modbus io removed:", io.ioNode.description) 118 io.stop() 119 delete(ow.ios, id) 120 } 121 } 122 123 return nil 124 } 125 126 // checkIOs goes through ios on the bus and handles any config changes 127 func (ow *oneWire) checkIOs() error { 128 nodes, err := client.GetNodes(ow.nc, ow.owNode.nodeID, "all", data.NodeTypeOneWireIO, false) 129 if err != nil { 130 return err 131 } 132 133 found := make(map[string]bool) 134 135 for _, node := range nodes { 136 found[node.ID] = true 137 _, ok := ow.ios[node.ID] 138 if !ok { 139 // add ios 140 var err error 141 ioNode, err := newOneWireIONode(&node) 142 if err != nil { 143 log.Println("Error with IO node:", err) 144 continue 145 } 146 io, err := newOneWireIO(ow.nc, ioNode, ow.chPoint) 147 if err != nil { 148 log.Println("Error creating new modbus IO:", err) 149 continue 150 } 151 ow.ios[node.ID] = io 152 } 153 } 154 155 // remove ios that have been deleted 156 for id, io := range ow.ios { 157 _, ok := found[id] 158 if !ok { 159 // io was deleted so close and clear it 160 log.Println("modbus io removed:", io.ioNode.description) 161 io.stop() 162 delete(ow.ios, id) 163 } 164 } 165 166 return nil 167 } 168 169 func (ow *oneWire) detect() { 170 // detect one wire busses 171 dirs, _ := filepath.Glob("/sys/bus/w1/devices/28-*") 172 173 for _, dir := range dirs { 174 f, _ := os.Stat(dir) 175 if f.IsDir() { 176 id := path.Base(dir) 177 found := false 178 for _, io := range ow.ios { 179 if io.ioNode.id == id { 180 found = true 181 break 182 } 183 } 184 185 if !found { 186 log.Println("adding 1-wire IO:", id) 187 188 n := data.NodeEdge{ 189 Type: data.NodeTypeOneWireIO, 190 Parent: ow.owNode.nodeID, 191 Points: data.Points{ 192 data.Point{ 193 Type: data.PointTypeID, 194 Text: id, 195 }, 196 data.Point{ 197 Type: data.PointTypeDescription, 198 Text: "New IO, please edit", 199 }, 200 }, 201 } 202 203 err := client.SendNode(ow.nc, n, "") 204 if err != nil { 205 log.Println("Error sending new 1-wire IO:", err) 206 } 207 } 208 } 209 } 210 } 211 212 func (ow *oneWire) run() { 213 // if we reset any error count, we set this to avoid continually resetting 214 scanTimer := time.NewTicker(24 * time.Hour) 215 216 setScanTimer := func() { 217 pollPeriod := ow.owNode.pollPeriod 218 if pollPeriod <= 0 { 219 pollPeriod = 3000 220 } 221 scanTimer.Reset(time.Millisecond * time.Duration(pollPeriod)) 222 } 223 224 setScanTimer() 225 226 for { 227 select { 228 case point := <-ow.chPoint: 229 p := point.point 230 if point.id == ow.owNode.nodeID { 231 ow.node.AddPoint(p) 232 var err error 233 ow.owNode, err = newOneWireNode(ow.node) 234 if err != nil { 235 log.Println("Error updating OW node:", err) 236 } 237 238 switch point.point.Type { 239 case data.PointTypePollPeriod: 240 setScanTimer() 241 case data.PointTypeErrorCountReset: 242 if ow.owNode.errorCountReset { 243 p := data.Point{Type: data.PointTypeErrorCount, Value: 0} 244 err := client.SendNodePoint(ow.nc, ow.owNode.nodeID, p, true) 245 if err != nil { 246 log.Println("Send point error:", err) 247 } 248 249 p = data.Point{Type: data.PointTypeErrorCountReset, Value: 0} 250 err = client.SendNodePoint(ow.nc, ow.owNode.nodeID, p, true) 251 if err != nil { 252 log.Println("Send point error:", err) 253 } 254 } 255 } 256 continue 257 } 258 259 io, ok := ow.ios[point.id] 260 if !ok { 261 log.Println("1-wire received point for unknown node:", point.id) 262 continue 263 } 264 265 err := io.point(p) 266 if err != nil { 267 log.Println("Error updating node point") 268 } 269 270 case <-ow.chDone: 271 return 272 case <-scanTimer.C: 273 if ow.owNode.disabled { 274 continue 275 } 276 277 err := ow.checkIOs() 278 if err != nil { 279 log.Println("Error checking 1-wire ios:", err) 280 } 281 ow.detect() 282 for _, io := range ow.ios { 283 err := io.read() 284 if err != nil { 285 if ow.owNode.debugLevel > 0 { 286 log.Printf("Error reading 1-wire io %v: %v\n", 287 io.ioNode.id, err) 288 } 289 busCount := ow.owNode.errorCount + 1 290 ioCount := io.ioNode.errorCount + 1 291 292 err = client.SendNodePoint(ow.nc, ow.owNode.nodeID, data.Point{ 293 Type: data.PointTypeErrorCount, 294 Value: float64(busCount), 295 }, false) 296 if err != nil { 297 log.Println("Error sending point:", err) 298 } 299 300 err = client.SendNodePoint(ow.nc, io.ioNode.nodeID, data.Point{ 301 Type: data.PointTypeErrorCount, 302 Value: float64(ioCount), 303 }, false) 304 if err != nil { 305 log.Println("Error sending point:", err) 306 } 307 } 308 } 309 } 310 } 311 }