modbus_converter.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. """
  2. @File : modbus_converter.py
  3. @Author: lee
  4. @Date : 2022/7/12/0012 8:55:13
  5. @Desc : 此类为mosbus协议通用解析器,用于:insitu水质传感器,新气象传感器,新水质传感器
  6. """
  7. import binascii
  8. import math
  9. import struct
  10. from converter import Converter
  11. from event_storage import EventStorage
  12. from logging_config import modbus_converter as logger
  13. from tools.format_value import format_value
  14. class ModbusConverter(Converter):
  15. def __init__(self, name):
  16. self._storage = EventStorage()
  17. self._name = name
  18. def convert(self, config, data):
  19. """
  20. data: [1, {112: 16801, 113: 50856}]
  21. [站号, {地址:值,地址:值 。。。}]
  22. """
  23. if data:
  24. device_id = data[0]
  25. data = data[1]
  26. format_data_dict = {} # 列表数据转换字典数字
  27. # [2, {37: 16646, 38: 30230}]
  28. try:
  29. for index in config:
  30. if int(index["device_id"]) == int(device_id):
  31. # addr_type : 'D' 或 'X';
  32. # addr_list: [10] 或 [10, 5]
  33. addr_type, addr_list = addr_parsing(index['address'])
  34. register_addr = addr_list[0] # 数据地址 example:'X10.5' -> 10
  35. # parameter_id = addr_list[1]
  36. # print(parameter_id)
  37. if register_addr > 40000:
  38. register_addr = register_addr - 40001
  39. # if register_addr in data and data[register_addr + 4] == parameter_id:
  40. if register_addr in data:
  41. logger.info(f"----------------------------")
  42. if addr_type == 'X':
  43. bit_offset_addr = addr_list[1] # 位偏移地址 example:'X10.5' -> 5
  44. if index['data_type'] == "BOOL":
  45. data_bin = bin(65536 | data[register_addr]) # 和65536做或运算,为了留开头0
  46. data_bin = list(data_bin[3:][::-1]) # 去除开头字符 0 b 1并反转字符串
  47. data_bin = list(map(int, data_bin)) # 字符列表转整型列表
  48. if index['modbus_mode'] == "BA":
  49. data_bin = data_bin[8:16] + data_bin[0:8]
  50. if index['negation'] == 1:
  51. return_data = int(bool(1 - data_bin[bit_offset_addr])) # 取反运算
  52. else:
  53. return_data = data_bin[bit_offset_addr]
  54. elif addr_type == 'D':
  55. if index['data_type'] == "FLOAT16":
  56. return_data = data[register_addr]
  57. elif index['data_type'] == "INT16":
  58. if index['negation'] == 1:
  59. return_data = int(bool(1 - data[register_addr])) # 取反运算
  60. else:
  61. if data[register_addr] > 32767:
  62. return_data = data[register_addr] - 65536
  63. else:
  64. return_data = data[register_addr]
  65. elif index['data_type'] == "INT32":
  66. return_data_H = data[register_addr]
  67. return_data_L = data[register_addr + 1]
  68. if index['modbus_mode'] == "CDAB":
  69. return_data = data[register_addr] * 65536 + data[register_addr + 1]
  70. else:
  71. return_data = data[register_addr + 1] * 65536 + data[register_addr]
  72. elif index['data_type'] == "FLOAT32":
  73. t1 = hex(data[register_addr])[2:]
  74. t2 = hex(data[register_addr + 1])[2:]
  75. if len(t1) < 4:
  76. t1 = (4 - len(t1)) * "0" + t1
  77. if len(t2) < 4:
  78. t2 = (4 - len(t2)) * "0" + t2
  79. if index['modbus_mode'] == "CDAB":
  80. return_data = struct.unpack('>f', binascii.unhexlify((t2 + t1).replace(' ', '')))[0]
  81. elif index['modbus_mode'] == "DCBA":
  82. t1_l, t1_r = t1[0:2], t1[2:4]
  83. t2_l, t2_r = t2[0:2], t2[2:4]
  84. t1, t2 = t1_r + t1_l, t2_r + t2_l
  85. return_data = struct.unpack('>f', binascii.unhexlify((t2 + t1).replace(' ', '')))[0]
  86. else:
  87. return_data = struct.unpack('>f', binascii.unhexlify((t1 + t2).replace(' ', '')))[0]
  88. # 电导率转盐度
  89. if index['io_point_name'] == "盐度" and self._name == 'shuzhi_new':
  90. return_data = self.cal_salty(return_data)
  91. elif index['data_type'] == "BELZ_FLOAT32":
  92. llj_data = []
  93. for x in range(0, 6, 2):
  94. t1 = hex(data[register_addr + x])[2:]
  95. t2 = hex(data[register_addr + x + 1])[2:]
  96. if len(t1) < 4:
  97. t1 = (4 - len(t1)) * "0" + t1
  98. if len(t2) < 4:
  99. t2 = (4 - len(t2)) * "0" + t2
  100. t = struct.unpack('>f', binascii.unhexlify((t1 + t2).replace(' ', '')))[0]
  101. llj_data.append(t)
  102. return_data = llj_data[0] * 10 ** 6 + llj_data[1] + llj_data[2] / 10 ** 6 # 总累计量
  103. elif index['data_type'] == "UINT16":
  104. return_data = data[register_addr]
  105. name = 'c' + str(index['serial_number'])
  106. # 格式化数据
  107. format_data_dict[name] = format_value(index, return_data)
  108. logger.info(f"[{self._name}]: {index['io_point_name']}: {format_data_dict}")
  109. return format_data_dict
  110. except Exception as e:
  111. logger.error(f"{self._name}:{repr(e)}")
  112. return "error"
  113. def cal_salty(self, conductivity):
  114. """盐度转换:需要温度和深度参数"""
  115. sql = f"SELECT serial_number FROM data_point_tbl WHERE device_name = '{self._name}' AND io_point_name = '温度'"
  116. sql1 = f"SELECT serial_number FROM data_point_tbl WHERE device_name = '{self._name}' AND io_point_name = '深度'"
  117. res = self._storage.execute_sql(sql)
  118. res1 = self._storage.execute_sql(sql1)
  119. serial_number_temperature = 'c' + str(res[0]['serial_number'])
  120. serial_number_deep = 'c' + str(res1[0]['serial_number'])
  121. data = self._storage.get_real_data([serial_number_temperature, serial_number_deep])
  122. a0, a1, a2, a3, a4, a5 = 0.0080, -0.1692, 25.3851, 14.0941, -7.0261, 2.7081
  123. b0, b1, b2, b3, b4, b5 = 0.0005, -0.0056, -0.0066, -0.0375, 0.0636, -0.0144
  124. c0, c1, c2, c3, c4 = 0.6766097, 2.00564e-2, 1.104259e-4, -6.9698e-7, 1.0031e-9
  125. d1, d2, d3, d4 = 3.426e-2, 4.464e-4, 4.215e-1, -3.107e-3
  126. e1, e2, e3 = 2.070e-5, -6.370e-10, 3.989e-15
  127. k = 0.0162
  128. R = conductivity / 42.914
  129. try:
  130. if data[serial_number_temperature] and data[serial_number_deep]:
  131. t = float(data[serial_number_temperature]) * 1.00024
  132. p = float(data[serial_number_deep])
  133. rt = c0 + c1 * t + c2 * t * t + c3 * t * t * t + c4 * t * t * t * t
  134. Rp = 1 + p * (e1 + e2 * p + e3 * p * p) / (1 + d1 * t + d2 * t * t + (d3 + d4 * t) * R)
  135. Rt = R / (Rp * rt)
  136. # print("\nRt=",Rt,'\n')
  137. S = (t - 15) * (b0 + b1 * math.sqrt(Rt) + b2 * Rt + b3 * Rt * math.sqrt(Rt) + b4 * Rt * Rt + b5 * Rt * Rt * math.sqrt(Rt)) / (1 + k * (t - 15))
  138. Salinity = a0 + a1 * math.sqrt(Rt) + a2 * Rt + a3 * Rt * math.sqrt(Rt) + a4 * Rt * Rt + a5 * Rt * Rt * math.sqrt(Rt) + S
  139. return Salinity
  140. else:
  141. return None
  142. except Exception as e:
  143. logger.error(f"{self._name}:{repr(e)}")
  144. return 0.00
  145. def addr_parsing(addr_smash):
  146. """
  147. :param addr_smash: [X15.1] or [D15]
  148. :return: X, [15, 1] or D [15]
  149. """
  150. # addr_smash_1 = 'D2'
  151. # addr_smash = list(addr_smash) # 拆分字符串为list ['X', '6', '.', '2']
  152. addr_type = addr_smash[0] # 地址类型 : 'D' 或 'X'
  153. addr_smash = addr_smash[1:] # 地址部分: '10' 或 '10.5'
  154. addr_list = list(map(int, addr_smash.split("."))) # 用“.”分割字符串转换为整型存入列表
  155. return addr_type, addr_list