github.com/simpleiot/simpleiot@v0.18.3/node/node.go (about)

     1  package node
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"log"
     8  	"text/template"
     9  	"time"
    10  
    11  	"github.com/nats-io/nats.go"
    12  
    13  	"github.com/simpleiot/simpleiot/client"
    14  	"github.com/simpleiot/simpleiot/data"
    15  	"github.com/simpleiot/simpleiot/system"
    16  )
    17  
    18  // Manager is responsible for maintaining node state, running rules, etc
    19  type Manager struct {
    20  	nc             *nats.Conn
    21  	appVersion     string
    22  	osVersionField string
    23  	modbusManager  *ModbusManager
    24  	rootNodeID     string
    25  	oneWireManager *oneWireManager
    26  	chStop         chan struct{}
    27  }
    28  
    29  // NewManger creates a new Manager
    30  func NewManger(nc *nats.Conn, appVersion, osVersionField string) *Manager {
    31  	return &Manager{
    32  		nc:             nc,
    33  		appVersion:     appVersion,
    34  		osVersionField: osVersionField,
    35  		chStop:         make(chan struct{}),
    36  	}
    37  }
    38  
    39  // Init initializes some node managers
    40  func (m *Manager) init() error {
    41  	var rootNode data.NodeEdge
    42  
    43  	rootNodes, err := client.GetNodes(m.nc, "root", "all", "", false)
    44  
    45  	if err != nil {
    46  		return fmt.Errorf("Error getting root node: %v", err)
    47  	}
    48  
    49  	if len(rootNodes) > 0 {
    50  		rootNode = rootNodes[0]
    51  	} else {
    52  		return fmt.Errorf("No nodes returned for root node")
    53  	}
    54  
    55  	m.rootNodeID = rootNode.ID
    56  
    57  	appVer, ok := rootNode.Points.Find(data.PointTypeVersionApp, "")
    58  	if !ok || appVer.Text != m.appVersion {
    59  		log.Println("Setting app version:", m.appVersion)
    60  		err := client.SendNodePoint(m.nc, rootNode.ID, data.Point{
    61  			Type: data.PointTypeVersionApp,
    62  			Text: m.appVersion,
    63  		}, true)
    64  
    65  		if err != nil {
    66  			log.Println("Error setting app version")
    67  		}
    68  	}
    69  
    70  	// check if OS version is current
    71  	osVer, err := system.ReadOSVersion(m.osVersionField)
    72  	if err != nil {
    73  		log.Println("Error reading OS version:", err)
    74  	} else {
    75  		log.Println("OS version:", osVer)
    76  		osVerStored, ok := rootNode.Points.Find(data.PointTypeVersionOS, "")
    77  		if !ok || osVer.String() != osVerStored.Text {
    78  			log.Println("Setting os version:", osVer)
    79  			err := client.SendNodePoint(m.nc, rootNode.ID, data.Point{
    80  				Type: data.PointTypeVersionOS,
    81  				Text: osVer.String(),
    82  			}, true)
    83  
    84  			if err != nil {
    85  				log.Println("Error setting OS version")
    86  			}
    87  		}
    88  
    89  	}
    90  
    91  	m.modbusManager = NewModbusManager(m.nc, m.rootNodeID)
    92  	m.oneWireManager = newOneWireManager(m.nc, m.rootNodeID)
    93  
    94  	return nil
    95  }
    96  
    97  // Start manager
    98  func (m *Manager) Start() error {
    99  	if err := m.init(); err != nil {
   100  		return fmt.Errorf("Error initializing nodes: %v", err)
   101  	}
   102  
   103  	t := time.NewTimer(time.Millisecond)
   104  
   105  	// TODO: this will not scale and needs to be made event driven
   106  	// on the creation of new nodes
   107  	for {
   108  		select {
   109  		case <-m.chStop:
   110  			return errors.New("node manager stopping")
   111  		case <-t.C:
   112  			if m.modbusManager != nil {
   113  				_ = m.modbusManager.Update()
   114  			}
   115  			if m.oneWireManager != nil {
   116  				_ = m.oneWireManager.update()
   117  			}
   118  			t.Reset(time.Second * 20)
   119  		}
   120  	}
   121  
   122  	/* the following code needs redone, so commenting out for now
   123  	for {
   124  		// TODO: this will not scale and needs to be made event driven
   125  		nodes, err := m.db.Nodes()
   126  		if err != nil {
   127  			log.Println("Error getting nodes:", err)
   128  			time.Sleep(10 * time.Second)
   129  			continue
   130  		}
   131  
   132  		for _, node := range nodes {
   133  			// update node state
   134  			state, changed := node.GetState()
   135  			if changed {
   136  				p := data.Point{
   137  					Time: time.Now(),
   138  					Type: data.PointTypeSysState,
   139  					Text: state,
   140  				}
   141  
   142  				err := client.SendNodePoint(m.nc, node.ID, p, false)
   143  				if err != nil {
   144  					log.Println("Error updating node state:", err)
   145  				}
   146  			}
   147  		}
   148  
   149  		time.Sleep(30 * time.Second)
   150  	}
   151  	*/
   152  }
   153  
   154  // Stop manager
   155  func (m *Manager) Stop(_ error) {
   156  	close(m.chStop)
   157  }
   158  
   159  type nodeTemplateData struct {
   160  	ID          string
   161  	Description string
   162  	Ios         map[string]float64
   163  }
   164  
   165  func renderNotifyTemplate(node *data.Node, msgTemplate string) (string, error) {
   166  	// build map of IO values so they are easy to reference by type or ID in template
   167  	dtd := nodeTemplateData{
   168  		ID:          node.ID,
   169  		Description: node.Desc(),
   170  		Ios:         make(map[string]float64),
   171  	}
   172  
   173  	for _, io := range node.Points {
   174  		if io.Type != "" {
   175  			dtd.Ios[io.Type] = io.Value
   176  		}
   177  	}
   178  
   179  	buf := new(bytes.Buffer)
   180  
   181  	tmpl, err := template.New("msg").Parse(msgTemplate)
   182  
   183  	if err != nil {
   184  		return "", err
   185  	}
   186  
   187  	err = tmpl.Execute(buf, dtd)
   188  
   189  	if err != nil {
   190  		return "", err
   191  	}
   192  
   193  	return buf.String(), nil
   194  
   195  }