forked from tbrandon/mbserver
-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.go
109 lines (89 loc) · 2.53 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// Package mbserver implments a Modbus server (slave).
package mbserver
import (
"io"
"net"
"sync"
"github.com/goburrow/serial"
)
// Server is a Modbus slave with allocated memory for discrete inputs, coils, etc.
type Server struct {
// Debug enables more verbose messaging.
Debug bool
listeners []net.Listener
ports []serial.Port
portsWG sync.WaitGroup
portsCloseChan chan struct{}
requestChan chan *Request
function [256](func(*Server, Framer) ([]byte, *Exception))
DiscreteInputs []byte
Coils []byte
HoldingRegisters []uint16
InputRegisters []uint16
}
// Request contains the connection and Modbus frame.
type Request struct {
conn io.ReadWriteCloser
frame Framer
}
// NewServer creates a new Modbus server (slave).
func NewServer() *Server {
s := &Server{}
// Allocate Modbus memory maps.
s.DiscreteInputs = make([]byte, 65536)
s.Coils = make([]byte, 65536)
s.HoldingRegisters = make([]uint16, 65536)
s.InputRegisters = make([]uint16, 65536)
// Add default functions.
s.function[1] = ReadCoils
s.function[2] = ReadDiscreteInputs
s.function[3] = ReadHoldingRegisters
s.function[4] = ReadInputRegisters
s.function[5] = WriteSingleCoil
s.function[6] = WriteHoldingRegister
s.function[15] = WriteMultipleCoils
s.function[16] = WriteHoldingRegisters
s.requestChan = make(chan *Request)
s.portsCloseChan = make(chan struct{})
go s.handler()
return s
}
// RegisterFunctionHandler override the default behavior for a given Modbus function.
func (s *Server) RegisterFunctionHandler(funcCode uint8, function func(*Server, Framer) ([]byte, *Exception)) {
s.function[funcCode] = function
}
func (s *Server) handle(request *Request) Framer {
var exception *Exception
var data []byte
response := request.frame.Copy()
function := request.frame.GetFunction()
if s.function[function] != nil {
data, exception = s.function[function](s, request.frame)
response.SetData(data)
} else {
exception = &IllegalFunction
}
if exception != &Success {
response.SetException(exception)
}
return response
}
// All requests are handled synchronously to prevent modbus memory corruption.
func (s *Server) handler() {
for {
request := <-s.requestChan
response := s.handle(request)
request.conn.Write(response.Bytes())
}
}
// Close stops listening to TCP/IP ports and closes serial ports.
func (s *Server) Close() {
for _, listen := range s.listeners {
listen.Close()
}
close(s.portsCloseChan)
s.portsWG.Wait()
for _, port := range s.ports {
port.Close()
}
}