serial.go 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. // Copyright 2014 Quoc-Viet Nguyen. All rights reserved.
  2. // This software may be modified and distributed under the terms
  3. // of the BSD license. See the LICENSE file for details.
  4. package modbus
  5. import (
  6. "io"
  7. "log"
  8. "sync"
  9. "time"
  10. "github.com/goburrow/serial"
  11. )
  12. const (
  13. // Default timeout
  14. serialTimeout = 5 * time.Second
  15. serialIdleTimeout = 60 * time.Second
  16. )
  17. // serialPort has configuration and I/O controller.
  18. type serialPort struct {
  19. // Serial port configuration.
  20. serial.Config
  21. Logger *log.Logger
  22. IdleTimeout time.Duration
  23. mu sync.Mutex
  24. // port is platform-dependent data structure for serial port.
  25. port io.ReadWriteCloser
  26. lastActivity time.Time
  27. closeTimer *time.Timer
  28. }
  29. func (mb *serialPort) Connect() (err error) {
  30. mb.mu.Lock()
  31. defer mb.mu.Unlock()
  32. return mb.connect()
  33. }
  34. // connect connects to the serial port if it is not connected. Caller must hold the mutex.
  35. func (mb *serialPort) connect() error {
  36. if mb.port == nil {
  37. port, err := serial.Open(&mb.Config)
  38. if err != nil {
  39. return err
  40. }
  41. mb.port = port
  42. }
  43. return nil
  44. }
  45. func (mb *serialPort) Close() (err error) {
  46. mb.mu.Lock()
  47. defer mb.mu.Unlock()
  48. return mb.close()
  49. }
  50. // close closes the serial port if it is connected. Caller must hold the mutex.
  51. func (mb *serialPort) close() (err error) {
  52. if mb.port != nil {
  53. err = mb.port.Close()
  54. mb.port = nil
  55. }
  56. return
  57. }
  58. func (mb *serialPort) logf(format string, v ...interface{}) {
  59. if mb.Logger != nil {
  60. mb.Logger.Printf(format, v...)
  61. }
  62. }
  63. func (mb *serialPort) startCloseTimer() {
  64. if mb.IdleTimeout <= 0 {
  65. return
  66. }
  67. if mb.closeTimer == nil {
  68. mb.closeTimer = time.AfterFunc(mb.IdleTimeout, mb.closeIdle)
  69. } else {
  70. mb.closeTimer.Reset(mb.IdleTimeout)
  71. }
  72. }
  73. // closeIdle closes the connection if last activity is passed behind IdleTimeout.
  74. func (mb *serialPort) closeIdle() {
  75. mb.mu.Lock()
  76. defer mb.mu.Unlock()
  77. if mb.IdleTimeout <= 0 {
  78. return
  79. }
  80. idle := time.Now().Sub(mb.lastActivity)
  81. if idle >= mb.IdleTimeout {
  82. mb.logf("modbus: closing connection due to idle timeout: %v", idle)
  83. mb.close()
  84. }
  85. }