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

     1  package node
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"log"
     8  	"net"
     9  	"syscall"
    10  	"time"
    11  
    12  	"github.com/nats-io/nats.go"
    13  	"github.com/simpleiot/simpleiot/client"
    14  	"github.com/simpleiot/simpleiot/data"
    15  	"github.com/simpleiot/simpleiot/modbus"
    16  	"github.com/simpleiot/simpleiot/respreader"
    17  	"go.bug.st/serial"
    18  )
    19  
    20  type pointWID struct {
    21  	id    string
    22  	point data.Point
    23  }
    24  
    25  type server interface {
    26  	Close() error
    27  	Listen(func(error), func(), func())
    28  }
    29  
    30  // Modbus describes a modbus bus
    31  type Modbus struct {
    32  	// node data should only be changed through NATS, so that it is only changed in one place
    33  	node    data.NodeEdge
    34  	busNode *ModbusNode
    35  	ios     map[string]*ModbusIO
    36  
    37  	// data associated with running the bus
    38  	nc           *nats.Conn
    39  	sub          *nats.Subscription
    40  	regs         *modbus.Regs
    41  	client       *modbus.Client
    42  	server       server
    43  	serialPort   serial.Port
    44  	ioErrorCount int
    45  
    46  	chDone      chan bool
    47  	chPoint     chan pointWID
    48  	chRegChange chan bool
    49  }
    50  
    51  // NewModbus creates a new bus from a node
    52  func NewModbus(nc *nats.Conn, node data.NodeEdge) (*Modbus, error) {
    53  	bus := &Modbus{
    54  		nc:          nc,
    55  		node:        node,
    56  		ios:         make(map[string]*ModbusIO),
    57  		chDone:      make(chan bool),
    58  		chPoint:     make(chan pointWID),
    59  		chRegChange: make(chan bool),
    60  	}
    61  
    62  	modbusNode, err := NewModbusNode(node)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	bus.busNode = modbusNode
    68  
    69  	// closure is required so we don't get races accessing bus.busNode
    70  	func(id string) {
    71  		bus.sub, err = nc.Subscribe("p."+bus.busNode.nodeID, func(msg *nats.Msg) {
    72  			points, err := data.PbDecodePoints(msg.Data)
    73  			if err != nil {
    74  				// FIXME, send over channel
    75  				log.Println("Error decoding node data:", err)
    76  				return
    77  			}
    78  
    79  			for _, p := range points {
    80  				bus.chPoint <- pointWID{id, p}
    81  			}
    82  		})
    83  	}(bus.busNode.nodeID)
    84  
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	go bus.Run()
    90  
    91  	return bus, nil
    92  }
    93  
    94  // Stop stops the bus and resets various fields
    95  func (b *Modbus) Stop() {
    96  	if b.sub != nil {
    97  		err := b.sub.Unsubscribe()
    98  		if err != nil {
    99  			log.Println("Error unsubscribing from bus:", err)
   100  		}
   101  	}
   102  	for _, io := range b.ios {
   103  		io.Stop()
   104  	}
   105  	b.chDone <- true
   106  }
   107  
   108  // CheckIOs goes through ios on the bus and handles any config changes
   109  func (b *Modbus) CheckIOs() error {
   110  	nodes, err := client.GetNodes(b.nc, b.busNode.nodeID, "all", data.NodeTypeModbusIO, false)
   111  	if err != nil {
   112  		return err
   113  	}
   114  
   115  	found := make(map[string]bool)
   116  
   117  	for _, node := range nodes {
   118  		found[node.ID] = true
   119  		_, ok := b.ios[node.ID]
   120  		if !ok {
   121  			// add ios
   122  			var err error
   123  			ioNode, err := NewModbusIONode(b.busNode.busType, &node)
   124  			if err != nil {
   125  				log.Println("Error with IO node:", err)
   126  				continue
   127  			}
   128  			io, err := NewModbusIO(b.nc, ioNode, b.chPoint)
   129  			if err != nil {
   130  				log.Println("Error creating new modbus IO:", err)
   131  				continue
   132  			}
   133  			b.ios[node.ID] = io
   134  			b.InitRegs(io.ioNode)
   135  		}
   136  	}
   137  
   138  	// remove ios that have been deleted
   139  	for id, io := range b.ios {
   140  		_, ok := found[id]
   141  		if !ok {
   142  			// io was deleted so close and clear it
   143  			log.Println("modbus io removed:", io.ioNode.description)
   144  			io.Stop()
   145  			delete(b.ios, id)
   146  		}
   147  	}
   148  
   149  	return nil
   150  }
   151  
   152  // SendPoint sends a point over nats
   153  func (b *Modbus) SendPoint(nodeID, pointType string, value float64) error {
   154  	// send the point
   155  	p := data.Point{
   156  		Time:  time.Now(),
   157  		Type:  pointType,
   158  		Value: value,
   159  	}
   160  
   161  	return client.SendNodePoint(b.nc, nodeID, p, true)
   162  }
   163  
   164  // WriteBusHoldingReg used to write register values to bus
   165  // should only be used by client
   166  func (b *Modbus) WriteBusHoldingReg(io *ModbusIONode) error {
   167  	unscaledValue := (io.valueSet - io.offset) / io.scale
   168  	switch io.modbusDataType {
   169  	case data.PointValueUINT16, data.PointValueINT16:
   170  		err := b.client.WriteSingleReg(byte(io.id),
   171  			uint16(io.address), uint16(unscaledValue))
   172  		if err != nil {
   173  			return err
   174  		}
   175  	case data.PointValueUINT32:
   176  		regs := modbus.Uint32ToRegs([]uint32{uint32(unscaledValue)})
   177  		err := b.client.WriteSingleReg(byte(io.id),
   178  			uint16(io.address), regs[0])
   179  		if err != nil {
   180  			return err
   181  		}
   182  
   183  		err = b.client.WriteSingleReg(byte(io.id),
   184  			uint16(io.address+1), regs[1])
   185  		if err != nil {
   186  			return err
   187  		}
   188  
   189  	case data.PointValueINT32:
   190  		regs := modbus.Int32ToRegs([]int32{int32(unscaledValue)})
   191  		err := b.client.WriteSingleReg(byte(io.id),
   192  			uint16(io.address), regs[0])
   193  		if err != nil {
   194  			return err
   195  		}
   196  
   197  		err = b.client.WriteSingleReg(byte(io.id),
   198  			uint16(io.address+1), regs[1])
   199  		if err != nil {
   200  			return err
   201  		}
   202  
   203  	case data.PointValueFLOAT32:
   204  		regs := modbus.Float32ToRegs([]float32{float32(unscaledValue)})
   205  		err := b.client.WriteSingleReg(byte(io.id),
   206  			uint16(io.address), regs[0])
   207  		if err != nil {
   208  			return err
   209  		}
   210  
   211  		err = b.client.WriteSingleReg(byte(io.id),
   212  			uint16(io.address+1), regs[1])
   213  		if err != nil {
   214  			return err
   215  		}
   216  
   217  	default:
   218  		return fmt.Errorf("unhandled data type: %v",
   219  			io.modbusDataType)
   220  
   221  	}
   222  
   223  	return nil
   224  }
   225  
   226  // ReadBusReg reads an io value from a reg from bus
   227  // this function modifies io.value
   228  func (b *Modbus) ReadBusReg(io *ModbusIO) error {
   229  	readFunc := b.client.ReadHoldingRegs
   230  	switch io.ioNode.modbusIOType {
   231  	case data.PointValueModbusHoldingRegister:
   232  	case data.PointValueModbusInputRegister:
   233  		readFunc = b.client.ReadInputRegs
   234  	default:
   235  		return fmt.Errorf("ReadBusReg: unsupported modbus IO type: %v",
   236  			io.ioNode.modbusIOType)
   237  	}
   238  	var valueUnscaled float64
   239  	switch io.ioNode.modbusDataType {
   240  	case data.PointValueUINT16, data.PointValueINT16:
   241  		regs, err := readFunc(byte(io.ioNode.id), uint16(io.ioNode.address), 1)
   242  		if err != nil {
   243  			return err
   244  		}
   245  		if len(regs) < 1 {
   246  			return errors.New("Did not receive enough data")
   247  		}
   248  		valueUnscaled = float64(regs[0])
   249  
   250  	case data.PointValueUINT32:
   251  		regs, err := readFunc(byte(io.ioNode.id), uint16(io.ioNode.address), 2)
   252  		if err != nil {
   253  			return err
   254  		}
   255  		if len(regs) < 2 {
   256  			return errors.New("Did not receive enough data")
   257  		}
   258  		v := modbus.RegsToUint32(regs)
   259  
   260  		valueUnscaled = float64(v[0])
   261  
   262  	case data.PointValueINT32:
   263  		regs, err := readFunc(byte(io.ioNode.id), uint16(io.ioNode.address), 2)
   264  		if err != nil {
   265  			return err
   266  		}
   267  		if len(regs) < 2 {
   268  			return errors.New("Did not receive enough data")
   269  		}
   270  		v := modbus.RegsToInt32(regs)
   271  
   272  		valueUnscaled = float64(v[0])
   273  
   274  	case data.PointValueFLOAT32:
   275  		regs, err := readFunc(byte(io.ioNode.id), uint16(io.ioNode.address), 2)
   276  		if err != nil {
   277  			return err
   278  		}
   279  		if len(regs) < 2 {
   280  			return errors.New("Did not receive enough data")
   281  		}
   282  		valueUnscaled = float64(modbus.RegsToFloat32(regs)[0])
   283  
   284  	default:
   285  		return fmt.Errorf("unhandled data type: %v",
   286  			io.ioNode.modbusDataType)
   287  	}
   288  
   289  	value := valueUnscaled*io.ioNode.scale + io.ioNode.offset
   290  
   291  	if value != io.ioNode.value || time.Since(io.lastSent) > time.Minute*10 {
   292  		io.ioNode.value = value
   293  		err := b.SendPoint(io.ioNode.nodeID, data.PointTypeValue, value)
   294  		if err != nil {
   295  			return err
   296  		}
   297  		io.lastSent = time.Now()
   298  	}
   299  
   300  	return nil
   301  }
   302  
   303  // ReadBusBit is used to read coil of discrete input values from bus
   304  // this function modifies io.value. This should only be called from client.
   305  func (b *Modbus) ReadBusBit(io *ModbusIO) error {
   306  	readFunc := b.client.ReadCoils
   307  	switch io.ioNode.modbusIOType {
   308  	case data.PointValueModbusCoil:
   309  	case data.PointValueModbusDiscreteInput:
   310  		readFunc = b.client.ReadDiscreteInputs
   311  	default:
   312  		return fmt.Errorf("ReadBusBit: unhandled modbusIOType: %v",
   313  			io.ioNode.modbusIOType)
   314  	}
   315  	bits, err := readFunc(byte(io.ioNode.id), uint16(io.ioNode.address), 1)
   316  	if err != nil {
   317  		return err
   318  	}
   319  	if len(bits) < 1 {
   320  		return errors.New("Did not receive enough data")
   321  	}
   322  
   323  	value := data.BoolToFloat(bits[0])
   324  
   325  	if value != io.ioNode.value || time.Since(io.lastSent) > time.Minute*10 {
   326  		io.ioNode.value = value
   327  		err := b.SendPoint(io.ioNode.nodeID, data.PointTypeValue, value)
   328  		if err != nil {
   329  			return err
   330  		}
   331  
   332  		io.lastSent = time.Now()
   333  	}
   334  
   335  	io.ioNode.value = value
   336  
   337  	return nil
   338  }
   339  
   340  // ClientIO processes an IO on a client bus
   341  func (b *Modbus) ClientIO(io *ModbusIO) error {
   342  
   343  	if b.client == nil {
   344  		return errors.New("client is not set up")
   345  	}
   346  
   347  	// read value from remote device and update regs
   348  	switch io.ioNode.modbusIOType {
   349  	case data.PointValueModbusCoil:
   350  		err := b.ReadBusBit(io)
   351  		if err != nil {
   352  			return err
   353  		}
   354  
   355  		if !io.ioNode.readOnly && io.ioNode.valueSet != io.ioNode.value {
   356  			vBool := data.FloatToBool(io.ioNode.valueSet)
   357  			// we need set the remote value
   358  			err := b.client.WriteSingleCoil(byte(io.ioNode.id), uint16(io.ioNode.address),
   359  				vBool)
   360  
   361  			if err != nil {
   362  				return err
   363  			}
   364  
   365  			err = b.SendPoint(io.ioNode.nodeID, data.PointTypeValue, io.ioNode.valueSet)
   366  			if err != nil {
   367  				return err
   368  			}
   369  		}
   370  
   371  	case data.PointValueModbusDiscreteInput:
   372  		err := b.ReadBusBit(io)
   373  		if err != nil {
   374  			return err
   375  		}
   376  
   377  	case data.PointValueModbusHoldingRegister:
   378  		err := b.ReadBusReg(io)
   379  		if err != nil {
   380  			return err
   381  		}
   382  
   383  		if !io.ioNode.readOnly && io.ioNode.valueSet != io.ioNode.value {
   384  			// we need set the remote value
   385  			err := b.WriteBusHoldingReg(io.ioNode)
   386  
   387  			if err != nil {
   388  				return err
   389  			}
   390  
   391  			err = b.SendPoint(io.ioNode.nodeID, data.PointTypeValue, io.ioNode.valueSet)
   392  			if err != nil {
   393  				return err
   394  			}
   395  		}
   396  
   397  	case data.PointValueModbusInputRegister:
   398  		err := b.ReadBusReg(io)
   399  		if err != nil {
   400  			return err
   401  		}
   402  
   403  	default:
   404  		return fmt.Errorf("unhandled modbus io type, io: %+v", io)
   405  	}
   406  
   407  	return nil
   408  }
   409  
   410  // ServerIO processes an IO on a server bus
   411  func (b *Modbus) ServerIO(io *ModbusIONode) error {
   412  	// update regs with db value
   413  	switch io.modbusIOType {
   414  	case data.PointValueModbusDiscreteInput:
   415  		err := b.regs.WriteCoil(io.address, data.FloatToBool(io.value))
   416  		if err != nil {
   417  			return err
   418  		}
   419  	case data.PointValueModbusCoil:
   420  		regValue, err := b.regs.ReadCoil(io.address)
   421  		if err != nil {
   422  			return err
   423  		}
   424  
   425  		dbValue := data.FloatToBool(io.value)
   426  
   427  		if regValue != dbValue {
   428  			err = b.SendPoint(io.nodeID, data.PointTypeValue, data.BoolToFloat(regValue))
   429  			if err != nil {
   430  				return err
   431  			}
   432  		}
   433  
   434  	case data.PointValueModbusInputRegister:
   435  		err := b.WriteReg(io)
   436  		if err != nil {
   437  			return err
   438  		}
   439  
   440  	case data.PointValueModbusHoldingRegister:
   441  		v, err := b.ReadReg(io)
   442  		if err != nil {
   443  			return err
   444  		}
   445  
   446  		if io.value != v {
   447  			err = b.SendPoint(io.nodeID, data.PointTypeValue, v)
   448  			if err != nil {
   449  				return err
   450  			}
   451  		}
   452  
   453  	default:
   454  		return fmt.Errorf("unhandled modbus io type: %v", io.modbusIOType)
   455  	}
   456  
   457  	return nil
   458  }
   459  
   460  func regCount(regType string) int {
   461  	switch regType {
   462  	case data.PointValueUINT16, data.PointValueINT16:
   463  		return 1
   464  	case data.PointValueUINT32, data.PointValueINT32,
   465  		data.PointValueFLOAT32:
   466  		return 2
   467  	default:
   468  		log.Println("regCount, unknown data type:", regType)
   469  		// be conservative
   470  		return 2
   471  	}
   472  }
   473  
   474  // InitRegs is used in server mode to initilize the internal modbus regs when a IO changes
   475  func (b *Modbus) InitRegs(io *ModbusIONode) {
   476  	if b.server == nil {
   477  		return
   478  	}
   479  
   480  	// we initialize all values from database, even if they are written from
   481  	// another device so that we preserve the last known state
   482  	switch io.modbusIOType {
   483  	case data.PointValueModbusDiscreteInput:
   484  		b.regs.AddCoil(io.address)
   485  		err := b.regs.WriteCoil(io.address, data.FloatToBool(io.value))
   486  		if err != nil {
   487  			log.Println("Error writing coil:", err)
   488  		}
   489  	case data.PointValueModbusCoil:
   490  		b.regs.AddCoil(io.address)
   491  		err := b.regs.WriteCoil(io.address, data.FloatToBool(io.value))
   492  		if err != nil {
   493  			log.Println("Error writing coil:", err)
   494  		}
   495  	case data.PointValueModbusInputRegister:
   496  		b.regs.AddReg(io.address, regCount(io.modbusDataType))
   497  		err := b.WriteReg(io)
   498  		if err != nil {
   499  			log.Println("Error writing reg:", err)
   500  		}
   501  	case data.PointValueModbusHoldingRegister:
   502  		b.regs.AddReg(io.address, regCount(io.modbusDataType))
   503  		err := b.WriteReg(io)
   504  		if err != nil {
   505  			log.Println("Error writing reg:", err)
   506  		}
   507  	}
   508  }
   509  
   510  // ReadReg reads an value from a reg (internal, not bus)
   511  // This should only be used on server
   512  func (b *Modbus) ReadReg(io *ModbusIONode) (float64, error) {
   513  	var valueUnscaled float64
   514  	switch io.modbusDataType {
   515  	case data.PointValueUINT16, data.PointValueINT16:
   516  		v, err := b.regs.ReadReg(io.address)
   517  		if err != nil {
   518  			return 0, err
   519  		}
   520  		valueUnscaled = float64(v)
   521  	case data.PointValueUINT32:
   522  		v, err := b.regs.ReadRegUint32(io.address)
   523  		if err != nil {
   524  			return 0, err
   525  		}
   526  		valueUnscaled = float64(v)
   527  	case data.PointValueINT32:
   528  		v, err := b.regs.ReadRegInt32(io.address)
   529  		if err != nil {
   530  			return 0, err
   531  		}
   532  		valueUnscaled = float64(v)
   533  	case data.PointValueFLOAT32:
   534  		v, err := b.regs.ReadRegFloat32(io.address)
   535  		if err != nil {
   536  			return 0, err
   537  		}
   538  		valueUnscaled = float64(v)
   539  	default:
   540  		return 0, fmt.Errorf("unhandled data type: %v",
   541  			io.modbusDataType)
   542  	}
   543  	return valueUnscaled*io.scale + io.offset, nil
   544  }
   545  
   546  // WriteReg writes an io value to a reg
   547  // This should only be used on server
   548  func (b *Modbus) WriteReg(io *ModbusIONode) error {
   549  	unscaledValue := (io.value - io.offset) / io.scale
   550  	switch io.modbusDataType {
   551  	case data.PointValueUINT16, data.PointValueINT16:
   552  		err := b.regs.WriteReg(io.address, uint16(unscaledValue))
   553  		if err != nil {
   554  			return err
   555  		}
   556  	case data.PointValueUINT32:
   557  		err := b.regs.WriteRegUint32(io.address,
   558  			uint32(unscaledValue))
   559  		if err != nil {
   560  			return err
   561  		}
   562  	case data.PointValueINT32:
   563  		err := b.regs.WriteRegInt32(io.address,
   564  			int32(unscaledValue))
   565  		if err != nil {
   566  			return err
   567  		}
   568  	case data.PointValueFLOAT32:
   569  		err := b.regs.WriteRegFloat32(io.address,
   570  			float32(unscaledValue))
   571  		if err != nil {
   572  			return err
   573  		}
   574  	default:
   575  		return fmt.Errorf("unhandled data type: %v",
   576  			io.modbusDataType)
   577  	}
   578  	return nil
   579  }
   580  
   581  // LogError ...
   582  func (b *Modbus) LogError(io *ModbusIONode, err error) error {
   583  	busCount := 0
   584  	ioCount := 0
   585  
   586  	if b.busNode.debugLevel >= 1 {
   587  		log.Printf("Modbus %v:%v, error: %v\n",
   588  			b.busNode.portName, io.description, err)
   589  	}
   590  
   591  	// if broken pipe error then close connection
   592  	if errors.Is(err, syscall.EPIPE) {
   593  		if b.busNode.debugLevel >= 1 {
   594  			log.Printf("Broken pipe, closing connection")
   595  		}
   596  		b.ClosePort()
   597  	}
   598  
   599  	errType := modbusErrorToPointType(err)
   600  	switch errType {
   601  	case data.PointTypeErrorCountEOF:
   602  		busCount = b.busNode.errorCountEOF
   603  		ioCount = io.errorCountEOF
   604  		b.busNode.errorCountEOF++
   605  		io.errorCountEOF++
   606  	case data.PointTypeErrorCountCRC:
   607  		busCount = b.busNode.errorCountCRC
   608  		ioCount = io.errorCountCRC
   609  		b.busNode.errorCountCRC++
   610  		io.errorCountCRC++
   611  	default:
   612  		// probably a more general serial port error
   613  		b.ioErrorCount++
   614  		errType = data.PointTypeErrorCount
   615  		busCount = b.busNode.errorCount
   616  		ioCount = io.errorCount
   617  		b.busNode.errorCount++
   618  		io.errorCount++
   619  	}
   620  
   621  	busCount++
   622  	ioCount++
   623  
   624  	p := data.Point{
   625  		Type:  errType,
   626  		Value: float64(busCount),
   627  	}
   628  
   629  	err = client.SendNodePoint(b.nc, b.busNode.nodeID, p, false)
   630  	if err != nil {
   631  		return err
   632  	}
   633  
   634  	p.Value = float64(ioCount)
   635  	return client.SendNodePoint(b.nc, io.nodeID, p, false)
   636  }
   637  
   638  // ClosePort closes both the server and client ports
   639  func (b *Modbus) ClosePort() {
   640  	if b.server != nil {
   641  		err := b.server.Close()
   642  		if err != nil {
   643  			log.Println("Error closing server:", err)
   644  		}
   645  		b.server = nil
   646  	}
   647  
   648  	if b.client != nil {
   649  		err := b.client.Close()
   650  		if err != nil {
   651  			log.Println("Error closing client:", err)
   652  		}
   653  		b.client = nil
   654  	}
   655  }
   656  
   657  // SetupPort sets up io for the bus
   658  func (b *Modbus) SetupPort() error {
   659  	if b.busNode.debugLevel >= 1 {
   660  		log.Println("modbus: setting up modbus transport:", b.busNode.portName)
   661  	}
   662  
   663  	b.ClosePort()
   664  
   665  	var transport modbus.Transport
   666  
   667  	switch b.busNode.protocol {
   668  	case data.PointValueRTU:
   669  		mode := &serial.Mode{
   670  			BaudRate: b.busNode.baud,
   671  		}
   672  
   673  		var err error
   674  		b.serialPort, err = serial.Open(b.busNode.portName, mode)
   675  		if err != nil {
   676  			b.serialPort = nil
   677  			return fmt.Errorf("Error opening serial port: %w", err)
   678  		}
   679  
   680  		port := respreader.NewReadWriteCloser(b.serialPort, time.Millisecond*100, time.Millisecond*20)
   681  
   682  		transport = modbus.NewRTU(port)
   683  	case data.PointValueTCP:
   684  		switch b.busNode.busType {
   685  		case data.PointValueClient:
   686  			sock, err := net.DialTimeout("tcp", b.busNode.uri, 5*time.Second)
   687  			if err != nil {
   688  				return err
   689  			}
   690  			transport = modbus.NewTCP(sock, 500*time.Millisecond,
   691  				modbus.TransportClient)
   692  		case data.PointValueServer:
   693  			// TCPServer does all the setup
   694  		default:
   695  			log.Println("setting up modbus TCP, invalid bus type:", b.busNode.busType)
   696  		}
   697  
   698  	default:
   699  		return fmt.Errorf("Unsupported modbus protocol: %v", b.busNode.protocol)
   700  	}
   701  
   702  	if b.busNode.busType == data.PointValueServer {
   703  		b.regs = &modbus.Regs{}
   704  		if b.busNode.protocol == data.PointValueRTU {
   705  			b.server = modbus.NewServer(byte(b.busNode.id), transport,
   706  				b.regs, b.busNode.debugLevel)
   707  		} else if b.busNode.protocol == data.PointValueTCP {
   708  			var err error
   709  			b.server, err = modbus.NewTCPServer(b.busNode.id, 5,
   710  				b.busNode.portName, b.regs, b.busNode.debugLevel)
   711  			if err != nil {
   712  				b.server = nil
   713  				return err
   714  			}
   715  		} else {
   716  			return errors.New("Modbus protocol not set")
   717  		}
   718  
   719  		go b.server.Listen(func(err error) {
   720  			log.Println("Modbus server error:", err)
   721  		}, func() {
   722  			if b.busNode.debugLevel > 0 {
   723  				log.Println("Modbus reg change")
   724  			}
   725  			b.chRegChange <- true
   726  		}, func() {
   727  			if b.busNode.debugLevel > 0 {
   728  				log.Println("Modbus Listener done")
   729  			}
   730  		})
   731  
   732  		for _, io := range b.ios {
   733  			b.InitRegs(io.ioNode)
   734  		}
   735  	} else if b.busNode.busType == data.PointValueClient {
   736  		b.client = modbus.NewClient(transport, b.busNode.debugLevel)
   737  	}
   738  
   739  	return nil
   740  }
   741  
   742  // Run is routine that runs the logic for a bus. Intended to be run as
   743  // a goroutine
   744  // It assumes an initial dataset is obtained from the database and all updates
   745  // come from NATs
   746  // this routine may need to run fast scan times, so it should be doing
   747  // slow things like reading the database.
   748  func (b *Modbus) Run() {
   749  
   750  	// if we reset any error count, we set this to avoid continually resetting
   751  	scanTimer := time.NewTicker(24 * time.Hour)
   752  
   753  	setScanTimer := func() {
   754  		if b.busNode.busType == data.PointValueClient {
   755  			scanTimer.Reset(time.Millisecond * time.Duration(b.busNode.pollPeriod))
   756  		} else {
   757  			scanTimer.Stop()
   758  		}
   759  	}
   760  
   761  	setScanTimer()
   762  
   763  	checkIoTimer := time.NewTicker(time.Second * 10)
   764  
   765  	log.Println("initializing modbus port:", b.busNode.portName)
   766  
   767  	for {
   768  		select {
   769  		case point := <-b.chPoint:
   770  			p := point.point
   771  			if point.id == b.busNode.nodeID {
   772  				b.node.AddPoint(p)
   773  				var err error
   774  				b.busNode, err = NewModbusNode(b.node)
   775  				if err != nil {
   776  					log.Println("Error updating bus node:", err)
   777  				}
   778  
   779  				switch point.point.Type {
   780  				case data.PointTypeClientServer,
   781  					data.PointTypeID,
   782  					data.PointTypeDebug,
   783  					data.PointTypePort,
   784  					data.PointTypeBaud,
   785  					data.PointTypeURI:
   786  					err := b.SetupPort()
   787  					if err != nil {
   788  						log.Println("Error setting up serial port:", err)
   789  					}
   790  				case data.PointTypePollPeriod:
   791  					setScanTimer()
   792  
   793  				case data.PointTypeErrorCountReset:
   794  					if b.busNode.errorCountReset {
   795  						p := data.Point{Type: data.PointTypeErrorCount, Value: 0}
   796  						err := client.SendNodePoint(b.nc, b.busNode.nodeID, p, true)
   797  						if err != nil {
   798  							log.Println("Send point error:", err)
   799  						}
   800  
   801  						p = data.Point{Type: data.PointTypeErrorCountReset, Value: 0}
   802  						err = client.SendNodePoint(b.nc, b.busNode.nodeID, p, true)
   803  						if err != nil {
   804  							log.Println("Send point error:", err)
   805  						}
   806  					}
   807  
   808  				case data.PointTypeErrorCountCRCReset:
   809  					if b.busNode.errorCountCRCReset {
   810  						p := data.Point{Type: data.PointTypeErrorCountCRC, Value: 0}
   811  						err := client.SendNodePoint(b.nc, b.busNode.nodeID, p, true)
   812  						if err != nil {
   813  							log.Println("Send point error:", err)
   814  						}
   815  
   816  						p = data.Point{Type: data.PointTypeErrorCountCRCReset, Value: 0}
   817  						err = client.SendNodePoint(b.nc, b.busNode.nodeID, p, true)
   818  						if err != nil {
   819  							log.Println("Send point error:", err)
   820  						}
   821  					}
   822  
   823  				case data.PointTypeErrorCountEOFReset:
   824  					if b.busNode.errorCountEOFReset {
   825  						p := data.Point{Type: data.PointTypeErrorCountEOF, Value: 0}
   826  						err := client.SendNodePoint(b.nc, b.busNode.nodeID, p, true)
   827  						if err != nil {
   828  							log.Println("Send point error:", err)
   829  						}
   830  
   831  						p = data.Point{Type: data.PointTypeErrorCountEOFReset, Value: 0}
   832  						err = client.SendNodePoint(b.nc, b.busNode.nodeID, p, true)
   833  						if err != nil {
   834  							log.Println("Send point error:", err)
   835  						}
   836  					}
   837  				}
   838  			} else {
   839  				io, ok := b.ios[point.id]
   840  				if !ok {
   841  					log.Println("modbus received point for unknown node:", point.id)
   842  					// FIXME, we could create a new IO here
   843  					continue
   844  				}
   845  
   846  				valueModified := false
   847  				valueSetModified := false
   848  
   849  				// handle IO changes
   850  				switch p.Type {
   851  				case data.PointTypeID:
   852  					io.ioNode.id = int(p.Value)
   853  				case data.PointTypeDescription:
   854  					io.ioNode.description = p.Text
   855  				case data.PointTypeAddress:
   856  					io.ioNode.address = int(p.Value)
   857  					b.InitRegs(io.ioNode)
   858  				case data.PointTypeModbusIOType:
   859  					io.ioNode.modbusIOType = p.Text
   860  				case data.PointTypeDataFormat:
   861  					io.ioNode.modbusDataType = p.Text
   862  				case data.PointTypeReadOnly:
   863  					io.ioNode.readOnly = data.FloatToBool(p.Value)
   864  				case data.PointTypeScale:
   865  					io.ioNode.scale = p.Value
   866  				case data.PointTypeOffset:
   867  					io.ioNode.offset = p.Value
   868  				case data.PointTypeValue:
   869  					valueModified = true
   870  					io.ioNode.value = p.Value
   871  				case data.PointTypeValueSet:
   872  					valueSetModified = true
   873  					io.ioNode.valueSet = p.Value
   874  				case data.PointTypeDisabled:
   875  					io.ioNode.disabled = data.FloatToBool(p.Value)
   876  				case data.PointTypeErrorCount:
   877  					io.ioNode.errorCount = int(p.Value)
   878  				case data.PointTypeErrorCountEOF:
   879  					io.ioNode.errorCountEOF = int(p.Value)
   880  				case data.PointTypeErrorCountCRC:
   881  					io.ioNode.errorCountCRC = int(p.Value)
   882  				case data.PointTypeErrorCountReset:
   883  					io.ioNode.errorCountReset = data.FloatToBool(p.Value)
   884  					if io.ioNode.errorCountReset {
   885  						p := data.Point{Type: data.PointTypeErrorCount, Value: 0}
   886  						err := client.SendNodePoint(b.nc, io.ioNode.nodeID, p, true)
   887  						if err != nil {
   888  							log.Println("Send point error:", err)
   889  						}
   890  
   891  						p = data.Point{Type: data.PointTypeErrorCountReset, Value: 0}
   892  						err = client.SendNodePoint(b.nc, io.ioNode.nodeID, p, true)
   893  						if err != nil {
   894  							log.Println("Send point error:", err)
   895  						}
   896  					}
   897  
   898  				case data.PointTypeErrorCountEOFReset:
   899  					io.ioNode.errorCountEOFReset = data.FloatToBool(p.Value)
   900  					if io.ioNode.errorCountEOFReset {
   901  						p := data.Point{Type: data.PointTypeErrorCountEOF, Value: 0}
   902  						err := client.SendNodePoint(b.nc, io.ioNode.nodeID, p, true)
   903  						if err != nil {
   904  							log.Println("Send point error:", err)
   905  						}
   906  
   907  						p = data.Point{Type: data.PointTypeErrorCountEOFReset, Value: 0}
   908  						err = client.SendNodePoint(b.nc, io.ioNode.nodeID, p, true)
   909  						if err != nil {
   910  							log.Println("Send point error:", err)
   911  						}
   912  					}
   913  
   914  				case data.PointTypeErrorCountCRCReset:
   915  					io.ioNode.errorCountCRCReset = data.FloatToBool(p.Value)
   916  					if io.ioNode.errorCountCRCReset {
   917  						p := data.Point{Type: data.PointTypeErrorCountCRC, Value: 0}
   918  						err := client.SendNodePoint(b.nc, io.ioNode.nodeID, p, true)
   919  						if err != nil {
   920  							log.Println("Send point error:", err)
   921  						}
   922  
   923  						p = data.Point{Type: data.PointTypeErrorCountCRCReset, Value: 0}
   924  						err = client.SendNodePoint(b.nc, io.ioNode.nodeID, p, true)
   925  						if err != nil {
   926  							log.Println("Send point error:", err)
   927  						}
   928  					}
   929  				default:
   930  					log.Println("modbus: unhandled io point:", p)
   931  				}
   932  
   933  				if valueModified && b.busNode.busType == data.PointValueServer {
   934  					err := b.ServerIO(io.ioNode)
   935  					if err != nil {
   936  						err := b.LogError(io.ioNode, err)
   937  						if err != nil {
   938  							log.Println("Error logging error:", err)
   939  						}
   940  					}
   941  				}
   942  
   943  				if valueSetModified && (b.busNode.busType == data.PointValueClient) &&
   944  					(io.ioNode.modbusDataType == data.PointValueModbusCoil ||
   945  						io.ioNode.modbusDataType == data.PointValueModbusHoldingRegister) &&
   946  					(io.ioNode.value != io.ioNode.valueSet) {
   947  					err := b.ClientIO(io)
   948  					if err != nil {
   949  						err := b.LogError(io.ioNode, err)
   950  						if err != nil {
   951  							log.Println("Error logging error:", err)
   952  						}
   953  					}
   954  				}
   955  			}
   956  		case <-b.chRegChange:
   957  			// this only happens on modbus servers
   958  			for _, io := range b.ios {
   959  				err := b.ServerIO(io.ioNode)
   960  				if err != nil {
   961  					err := b.LogError(io.ioNode, err)
   962  					if err != nil {
   963  						log.Println("Error logging modbus error:", err)
   964  					}
   965  				}
   966  			}
   967  
   968  		case <-checkIoTimer.C:
   969  			var portError error
   970  			if b.serialPort != nil {
   971  				// the following handles cases where serial port
   972  				// may have been unplugged and plugged back in
   973  				_, portError = b.serialPort.GetModemStatusBits()
   974  			}
   975  
   976  			if b.busNode.disabled {
   977  				b.ClosePort()
   978  			} else {
   979  				if (b.client == nil && b.server == nil) ||
   980  					b.ioErrorCount > 10 || portError != nil {
   981  					if b.busNode.debugLevel >= 1 {
   982  						log.Printf("Re-initializing modbus port, err cnt: %v, portError: %v\n", b.ioErrorCount, portError)
   983  					}
   984  					b.ioErrorCount = 0
   985  					// try to set up port
   986  					if err := b.SetupPort(); err != nil {
   987  						log.Println("SetupPort error:", err)
   988  					}
   989  				}
   990  				if err := b.CheckIOs(); err != nil {
   991  					log.Println("CheckIOs error:", err)
   992  				}
   993  			}
   994  
   995  		case <-scanTimer.C:
   996  			if b.busNode.busType == data.PointValueClient && !b.busNode.disabled {
   997  				for _, io := range b.ios {
   998  					if io.ioNode.disabled {
   999  						continue
  1000  					}
  1001  					// for scanning, we only need to process client ios
  1002  					err := b.ClientIO(io)
  1003  					if err != nil {
  1004  						err := b.LogError(io.ioNode, err)
  1005  						if err != nil {
  1006  							log.Println("Error logging modbus error:", err)
  1007  						}
  1008  					}
  1009  				}
  1010  			}
  1011  		case <-b.chDone:
  1012  			log.Println("Stopping client IO for:", b.busNode.portName)
  1013  			b.ClosePort()
  1014  			return
  1015  		}
  1016  	}
  1017  }
  1018  
  1019  func modbusErrorToPointType(err error) string {
  1020  	switch err {
  1021  	case io.EOF:
  1022  		return data.PointTypeErrorCountEOF
  1023  	case modbus.ErrCRC:
  1024  		return data.PointTypeErrorCountCRC
  1025  	default:
  1026  		return ""
  1027  	}
  1028  }