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 }