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  }