modbus.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. import _modbus
  2. import PikaStdDevice
  3. import time
  4. import random
  5. CONFIG_DEBUG = False
  6. CONFIG_FAKE_DATA = False
  7. def debug(*info):
  8. if CONFIG_DEBUG:
  9. print("[Debug]", *info)
  10. def error(*info):
  11. print("[Error]", *info)
  12. def info(*info):
  13. print("[Info]", *info)
  14. class ModBus(_modbus._ModBus):
  15. """
  16. A subclass of _modbus._ModBus that provides methods for serializing and sending modbus messages.
  17. """
  18. _option: str = None
  19. def serializeWriteBits(self, addr: int, src: list) -> bytes:
  20. """Serialize a write multiple coils request.
  21. Args:
  22. addr (int): The starting address of the coils to be written.
  23. src (list): A list of boolean values (0 or 1) to be written to the coils.
  24. Returns:
  25. bytes: The serialized message as a bytes object.
  26. """
  27. lenth = super().serializeWriteBits(addr, len(src), bytes(src))
  28. return self.sendBuff[0:lenth]
  29. def serializeWriteRegisters(self, addr: int, src: list) -> bytes:
  30. """Serialize a write multiple registers request.
  31. Args:
  32. addr (int): The starting address of the registers to be written.
  33. src (list): A list of integer values (0-65535) to be written to the registers.
  34. Returns:
  35. bytes: The serialized message as a bytes object.
  36. """
  37. _src = bytes(2 * len(src))
  38. for i in range(len(src)):
  39. _src[2 * i] = src[i] % 256
  40. _src[2 * i + 1] = src[i] // 256
  41. lenth = super().serializeWriteRegisters(addr, len(src), _src)
  42. return self.sendBuff[0:lenth]
  43. def serializeReadBits(self, addr: int, nb: int) -> bytes:
  44. """Serialize a read coils request.
  45. Args:
  46. addr (int): The starting address of the coils to be read.
  47. nb (int): The number of coils to be read.
  48. Returns:
  49. bytes: The serialized message as a bytes object.
  50. """
  51. lenth = super().serializeReadBits(addr, nb)
  52. return self.sendBuff[0:lenth]
  53. def serializeReadInputBits(self, addr: int, nb: int) -> bytes:
  54. """Serialize a read discrete inputs request.
  55. Args:
  56. addr (int): The starting address of the discrete inputs to be read.
  57. nb (int): The number of discrete inputs to be read.
  58. Returns:
  59. bytes: The serialized message as a bytes object.
  60. """
  61. lenth = super().serializeReadInputBits(addr, nb)
  62. return self.sendBuff[0:lenth]
  63. def serializeReadRegisters(self, addr: int, nb: int) -> bytes:
  64. """Serialize a read holding registers request.
  65. Args:
  66. addr (int): The starting address of the holding registers to be read.
  67. nb (int): The number of holding registers to be read.
  68. Returns:
  69. bytes: The serialized message as a bytes object.
  70. """
  71. lenth = super().serializeReadRegisters(addr, nb)
  72. return self.sendBuff[0:lenth]
  73. def serializeReadInputRegisters(self, addr: int, nb: int) -> bytes:
  74. """Serialize a read input registers request.
  75. Args:
  76. addr (int): The starting address of the input registers to be read.
  77. nb (int): The number of input registers to be read.
  78. Returns:
  79. bytes: The serialized message as a bytes object.
  80. """
  81. lenth = super().serializeReadInputRegisters(addr, nb)
  82. return self.sendBuff[0:lenth]
  83. def serializeWriteBit(self, addr: int, status: int) -> bytes:
  84. """Serialize a write single coil request.
  85. Args:
  86. addr (int): The address of the coil to be written.
  87. status (int): The value (0 or 1) to be written to the coil.
  88. Returns:
  89. bytes: The serialized message as a bytes object.
  90. """
  91. lenth = super().serializeWriteBit(addr, status)
  92. return self.sendBuff[0:lenth]
  93. def serializeWriteRegister(self, addr: int, value: int) -> bytes:
  94. """Serialize a write single register request.
  95. Args:
  96. addr (int): The address of the register to be written.
  97. value (int): The value (0-65535) to be written to the register.
  98. Returns:
  99. bytes: The serialized message as a bytes object.
  100. """
  101. lenth = super().serializeWriteRegister(addr, value)
  102. return self.sendBuff[0:lenth]
  103. def serializeMaskWriteRegister(self,
  104. addr: int,
  105. andMask: int,
  106. orMask: int) -> bytes:
  107. """Serialize a mask write register request.
  108. Args:
  109. addr (int): The address of the register to be modified.
  110. andMask (int): The AND mask to be applied to the current value of the register.
  111. orMask (int): The OR mask to be applied to the result of the AND operation.
  112. Returns:
  113. bytes: The serialized message as a bytes object.
  114. """
  115. lenth = super().serializeMaskWriteRegister(addr, andMask, orMask)
  116. return self.sendBuff[0:lenth]
  117. def serializeReportSlaveId(self) -> int:
  118. """Serialize a report slave ID request.
  119. Returns:
  120. int: The length of the serialized message in bytes.
  121. """
  122. lenth = super().serializeReportSlaveId()
  123. return self.sendBuff[0:lenth]
  124. def deserializeReadRegisters(self, msg: bytes) -> list:
  125. """Deserialize a read holding registers response.
  126. Args:
  127. msg (bytes): The received message as a bytes object.
  128. Returns:
  129. list: A list of integer values (0-65535) read from the registers.
  130. """
  131. self.readBuff = msg
  132. dest = super().deserializeReadRegisters(len(msg))
  133. if dest is None:
  134. return None
  135. ret = []
  136. for i in range(0, len(dest), 2):
  137. ret.append(int(dest[i]) + int(dest[i + 1]) * 256)
  138. return ret
  139. def deserializeReadBits(self, msg: bytes) -> list:
  140. """Deserialize a read coils response.
  141. Args:
  142. msg (bytes): The received message as a bytes object.
  143. Returns:
  144. list: A list of boolean values (True or False) read from the coils.
  145. """
  146. self.readBuff = msg
  147. length = len(msg)
  148. dest = super().deserializeReadBits(length)
  149. if dest is None:
  150. return None
  151. return list(dest)
  152. def deserializeReadInputBits(self, msg: bytes) -> list:
  153. """Deserialize a read discrete inputs response.
  154. Args:
  155. msg (bytes): The received message as a bytes object.
  156. Returns:
  157. list: A list of boolean values (True or False) read from the discrete inputs.
  158. """
  159. self.readBuff = msg
  160. length = len(msg)
  161. dest = super().deserializeReadInputBits(length)
  162. if dest is None:
  163. return None
  164. return list(dest)
  165. def deserializeReadInputRegisters(self, msg: bytes) -> list:
  166. """Deserialize a read input registers response.
  167. Args:
  168. msg (bytes): The received message as a bytes object.
  169. Returns:
  170. list: A list of integer values (0-65535) read from the input registers.
  171. """
  172. self.readBuff = msg
  173. length = len(msg)
  174. dest = super().deserializeReadInputRegisters(length)
  175. if dest is None:
  176. return None
  177. ret = []
  178. for i in range(0, len(dest), 2):
  179. ret.append(int(dest[i]) + int(dest[i + 1]) * 256)
  180. return ret
  181. def deserializeWriteAndReadRegisters(self, msg: bytes) -> list:
  182. """Deserialize a write and read registers response.
  183. Args:
  184. msg (bytes): The received message as a bytes object.
  185. Returns:
  186. list: A list of integer values (0-65535) written to and read from the registers.
  187. """
  188. self.readBuff = msg
  189. length = len(msg)
  190. dest = super().deserializeWriteAndReadRegisters(length)
  191. if dest is None:
  192. return None
  193. ret = []
  194. for i in range(0, len(dest), 2):
  195. ret.append(int(dest[i]) + int(dest[i + 1]) * 256)
  196. return ret
  197. def deserializeWriteBit(self, msg: bytes) -> int:
  198. """Deserialize a write single coil response.
  199. Args:
  200. msg (bytes): The received message as a bytes object.
  201. Returns:
  202. int: The address of the coil written to.
  203. """
  204. self.readBuff = msg
  205. length = len(msg)
  206. dest = super().deserializeWriteBit(length)
  207. if dest is None:
  208. return None
  209. return dest
  210. def deserializeWriteRegister(self, msg: bytes) -> int:
  211. """Deserialize a write single register response.
  212. Args:
  213. msg (bytes): The received message as a bytes object.
  214. Returns:
  215. int: The address of the register written to.
  216. """
  217. self.readBuff = msg
  218. length = len(msg)
  219. dest = super().deserializeWriteRegister(length)
  220. if dest is None:
  221. return None
  222. return dest
  223. def deserializeWriteBits(self, msg: bytes) -> int:
  224. """Deserialize a write multiple coils response.
  225. Args:
  226. msg (bytes): The received message as a bytes object.
  227. Returns:
  228. int: The number of coils written to.
  229. """
  230. self.readBuff = msg
  231. length = len(msg)
  232. dest = super().deserializeWriteBits(length)
  233. if dest is None:
  234. return None
  235. return dest
  236. def deserializeWriteRegisters(self, msg: bytes) -> int:
  237. """Deserialize a write multiple registers response.
  238. Args:
  239. msg (bytes): The received message as a bytes object.
  240. Returns:
  241. int: The number of registers written to.
  242. """
  243. self.readBuff = msg
  244. length = len(msg)
  245. dest = super().deserializeWriteRegisters(length)
  246. if dest is None:
  247. return None
  248. return dest
  249. def deserializeMaskWriteRegister(self, msg: bytes) -> int:
  250. """Deserialize a mask write register response.
  251. Args:
  252. msg (bytes): The received message as a bytes object.
  253. Returns:
  254. int: The address of the register modified.
  255. """
  256. self.readBuff = msg
  257. length = len(msg)
  258. dest = super().deserializeMaskWriteRegister(length)
  259. if dest is None:
  260. return None
  261. return dest
  262. def deserializeReportSlaveId(self, msg: bytes) -> bytes:
  263. """Deserialize a report slave ID response.
  264. Args:
  265. msg (bytes): The received message as a bytes object.
  266. Returns:
  267. bytes: The slave ID.
  268. """
  269. self.readBuff = msg
  270. length = len(msg)
  271. dest = super().deserializeReportSlaveId(length, 0)
  272. if dest is None:
  273. return None
  274. return dest
  275. OP_CODE_MAP = {
  276. 'x01': 'ReadBits',
  277. 'x02': 'ReadInputBits',
  278. 'x03': 'ReadRegisters',
  279. 'x04': 'ReadInputRegisters',
  280. 'x05': 'WriteBit',
  281. 'x06': 'WriteRegister',
  282. }
  283. class ModBusRTU(ModBus):
  284. _uart: PikaStdDevice.UART = None
  285. _receive_msg: bytes = None
  286. _request_callback_before = None
  287. _request_callback_after = None
  288. _send_callback_before = None
  289. _send_callback_after = None
  290. def __init__(self,
  291. sendBuffSize=128,
  292. readBuffSize=128,
  293. uart:PikaStdDevice.UART = None
  294. ):
  295. """Initialize a Modbus RTU protocol instance.
  296. Args:
  297. sendBuffSize (int): The size of the send buffer in bytes.
  298. readBuffSize (int): The size of the read buffer in bytes.
  299. """
  300. self.__init__rtu(sendBuffSize, readBuffSize)
  301. if uart is not None:
  302. self.setUart(uart)
  303. def recvCallback(self, signal):
  304. msg = self._uart.readBytes(128)
  305. debug('uart origin recive', msg)
  306. self._receive_msg = msg
  307. def setUart(self, uart: PikaStdDevice.UART):
  308. self._uart = uart
  309. debug('set uart', uart)
  310. uart.setCallback(self.recvCallback, uart.SIGNAL_RX)
  311. def recv(self, count: int = 10):
  312. """
  313. 获取uart返回的数据
  314. :param count:
  315. :return:
  316. """
  317. if count > 255 or count < 1:
  318. count = 10
  319. i = 0
  320. while i < count:
  321. time.sleep(0.2)
  322. if self._receive_msg:
  323. res = self._receive_msg
  324. self._receive_msg = None
  325. return res
  326. i += 1
  327. error('get result timeout')
  328. return None
  329. def setBaudRate(self, baud: int):
  330. # 重新设置 波特率
  331. if baud and type(baud) == int and baud > 0 and baud != self._baud:
  332. debug('重新设置波特率', baud)
  333. self._uart.setBaudRate(baud)
  334. self._baud = baud
  335. def send(self, msg: bytes):
  336. """
  337. 发送485十六进制消息
  338. :param msg: modbus rtu 消息
  339. :param band: 波特率
  340. :return:
  341. """
  342. # 放置 待发送消息
  343. self._transport_msg = msg
  344. if self._send_callback_before is not None:
  345. self._send_callback_before()
  346. if str(type(msg)) == "<class 'bytes'>":
  347. debug('sending msg by bytes', msg)
  348. self._uart.writeBytes(msg, len(msg))
  349. else:
  350. debug('sending msg by other', msg)
  351. self._uart.write(msg)
  352. if self._send_callback_after is not None:
  353. self._send_callback_after()
  354. def setOption(self, op_code:str):
  355. self._option = op_code
  356. def _getOperation(self, prefix:str, op_code:str):
  357. name = prefix + OP_CODE_MAP[op_code]
  358. if hasattr(self, name):
  359. return getattr(self, name)
  360. debug('not found operation', name)
  361. return None
  362. def getSerializer(self, op_code:str):
  363. return self._getOperation('serialize', op_code)
  364. def getDeserializer(self, op_code:str):
  365. return self._getOperation('deserialize', op_code)
  366. def serializeRequest(self,
  367. op_code:str,
  368. addr:int,
  369. arg,
  370. slave:int = None)->bytes:
  371. """
  372. 序列化modbus rtu请求
  373. :param op_code: 操作码
  374. :param addr: 寄存器地址
  375. :param arg: 参数 读取时为数量 写入时为值
  376. :param slave: 从机地址
  377. :return: 序列化后的数据
  378. """
  379. if slave is not None:
  380. self.setSlave(slave)
  381. self.setOption(op_code)
  382. serializer = self.getSerializer(op_code)
  383. msg = serializer(addr, arg)
  384. return msg
  385. def deserializeResponse(self, op_code:str, msg:bytes):
  386. """
  387. 反序列化modbus rtu响应
  388. :param op_code: 操作码
  389. :param msg: 响应消息
  390. :return: 解码后的数据
  391. """
  392. deserializer = self.getDeserializer(op_code)
  393. ret = deserializer(msg)
  394. return ret
  395. def _do_request(self, op_code:str, addr:int, arg, slave:int = None):
  396. """
  397. 发送modbus rtu请求
  398. :param op_code: 操作码
  399. :param addr: 寄存器地址
  400. :param arg: 参数 读取时为数量 写入时为值
  401. :param slave: 从机地址
  402. :return: 解码后的数据
  403. """
  404. if CONFIG_FAKE_DATA:
  405. info("using fake data")
  406. ret = random.randint(0, 65535)
  407. return ret
  408. msg = self.serializeRequest(op_code, addr, arg, slave)
  409. self.send(msg)
  410. res = self.recv()
  411. if res is None:
  412. error('request timeout')
  413. return None
  414. ret = self.deserializeResponse(op_code, res)
  415. debug('request', 'result:', ret)
  416. return ret
  417. def request(self, op_code:str, addr:int, arg, slave:int = None):
  418. """
  419. 发送modbus rtu请求
  420. :param op_code: 操作码
  421. :param addr: 寄存器地址
  422. :param arg: 参数 读取时为数量 写入时为值
  423. :param slave: 从机地址
  424. :return: 解码后的数据
  425. """
  426. if self._request_callback_before is not None:
  427. self._request_callback_before()
  428. ret = self._do_request(op_code, addr, arg, slave)
  429. if self._request_callback_after is not None:
  430. self._request_callback_after()
  431. return ret
  432. def set_request_callback(self, cb_before, cb_after):
  433. self._request_callback_before = cb_before
  434. self._request_callback_after = cb_after
  435. def set_send_callback(self, cb_before, cb_after):
  436. self._send_callback_before = cb_before
  437. self._send_callback_after = cb_after
  438. class ModBusTCP(ModBus):
  439. def __init__(self, sendBuffSize: int, readBuffSize: int):
  440. """Initialize a Modbus TCP protocol instance.
  441. Args:
  442. sendBuffSize (int): The size of the send buffer in bytes.
  443. readBuffSize (int): The size of the read buffer in bytes.
  444. """
  445. self.__init__tcp(sendBuffSize, readBuffSize)