forked from SebastianHanz/ModBusGateway
-
Notifications
You must be signed in to change notification settings - Fork 0
/
modbus-gateway.py
89 lines (80 loc) · 4 KB
/
modbus-gateway.py
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
#/usr/bin/env python
import fcntl
import struct
import ConfigParser
import SocketServer
import serial.rs485
import logging
import logging.handlers
import crc16
class ModbusGateway(SocketServer.BaseRequestHandler):
def setup(self):
self.load_config()
self.serial = serial.rs485.RS485(
port=self.config.get("ModbusRTU", "port"),
baudrate=self.config.getint("ModbusRTU", "baudrate"),
timeout=self.config.getint("ModbusRTU", "timeout"))
self.serial.rs485_mode = serial.rs485.RS485Settings(False,True)
self.serial_connect()
logger.info("Serial port {} is connected: {}".format(self.serial.port,self.serial.isOpen()))
def load_config(self):
self.config = ConfigParser.RawConfigParser()
self.config.read('modbus-gateway.cfg')
def serial_connect(self):
if not self.serial.isOpen():
self.serial.open()
def handle(self):
# check if serial port is open, open if not
if not self.serial.isOpen():
self.serial_connect()
logger.info("Connection established with {}".format(self.client_address[0]))
while True:
# receive the ModbusTCP request
tcp_request = self.request.recv(128)
if not tcp_request or len(tcp_request) ==0:
logger.info("Connection closed")
break
logger.debug("TCP Request {}".format(":".join("{:02X}".format(ord(c)) for c in tcp_request)))
# convert ModbusTCP request into a ModbusRTU request
rtu_request = tcp_request[6:] + crc16.calculate(tcp_request[6:])
logger.debug("RTU Request {}".format(":".join("{:02X}".format(ord(c)) for c in rtu_request)))
# make sure that the input buffer is clean
self.serial.flushInput()
# send the ModbusRTU request
self.serial.write(rtu_request)
# read first three bytes of the response to check for errors
rtu_response = self.serial.read(3)
if not rtu_response:
logger.warning("RTU Timeout")
else:
if ord(rtu_response[1]) > 0x80:
logger.debug("RTU Error Response {}".format(":".join("{:02X}".format(ord(c)) for c in rtu_response)))
tcp_response = tcp_request[0:5] + chr(3) + rtu_response
logger.debug("TCP Error Response {}".format(":".join("{:02X}".format(ord(c)) for c in tcp_response)))
self.request.sendall(tcp_response)
else:
# if no error, read number of bytes indicated in RTU response or fixed if response to a write command
bytes_to_read = ord(rtu_response[2]) + 2 if ord(rtu_response[1]) < 0x5 else 8-3
rtu_response += self.serial.read(bytes_to_read)
logger.debug("RTU Response {}".format(":".join("{:02X}".format(ord(c)) for c in rtu_response)))
# convert ModbusRTU response into a Modbus TCP response
tcp_response = tcp_request[0:5] + chr(len(rtu_response)-2) + rtu_response[0:-2]
logger.debug("TCP Response {}".format(":".join("{:02X}".format(ord(c)) for c in tcp_response)))
# return converted TCP response
self.request.sendall(tcp_response)
def finish(self):
self.serial.close()
if __name__ == "__main__":
config = ConfigParser.RawConfigParser()
config.read('modbus-gateway.cfg')
logging.basicConfig(level=config.get("Logging", "Level"), format='%(asctime)s: %(levelname)-8s - %(message)s')
logger = logging.getLogger('Modbus Gateway')
handler = logging.handlers.SysLogHandler(address=(config.get("Logging", "Syslog"),514))
logger.addHandler(handler)
address = (config.get("ModbusTCP", "host"), config.getint("ModbusTCP","port"))
server = SocketServer.TCPServer(address, ModbusGateway)
try:
server.serve_forever()
except KeyboardInterrupt:
pass
server.server_close()