s7data.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. import snap7, csv, threading, warnings, time, ctypes
  2. from utils.statepoint import *
  3. class TS7DataItem(ctypes.Structure):
  4. _fields_ = [
  5. ('Area', ctypes.c_int),
  6. ('WordLen', ctypes.c_int),
  7. ('Result', ctypes.c_int),
  8. ('DBNumber', ctypes.c_int),
  9. ('Start', ctypes.c_int),
  10. ('Amount', ctypes.c_int),
  11. ('pdata', ctypes.c_void_p)
  12. ]
  13. class S7Client(snap7.client.Client):
  14. def multi_db_read_py(self, db_number: list, start: list, size: list):
  15. count = len(size)
  16. buffers = [ctypes.create_string_buffer(i) for i in size]
  17. params = []
  18. for i in range(count):
  19. params.append(TS7DataItem(snap7.type.Areas.DB, snap7.type.WordLen.Byte, 0, db_number[i], start[i], size[i], ctypes.cast(buffers[i], ctypes.c_void_p)))
  20. array_type = TS7DataItem * count
  21. param_array = array_type(*params)
  22. result = self.read_multi_vars(param_array)
  23. if result[0]:
  24. raise RuntimeError("多组读取失败")
  25. res_rtn = [bytearray(i) for i in buffers]
  26. return res_rtn
  27. class S7data:
  28. def __init__(self, csvfile):
  29. self.logger = None
  30. self.S7Client = None
  31. self.lock = threading.Lock()
  32. self.thread_run = False
  33. self.threads = []
  34. self.nodes = {}
  35. self.node_data = {}
  36. self.groups = {}
  37. self.target_from_name = {}
  38. with open(csvfile) as f:
  39. for i in csv.DictReader(f):
  40. if i['name'] in self.nodes:
  41. raise Exception(f"S7配置文件节点名称重复:{i['name']}")
  42. else:
  43. self.nodes[i['name']] = i
  44. self.node_data[i['name']] = bytearray(int(i['size']))
  45. if i['group'] not in self.groups:
  46. self.groups[i['group']] = []
  47. self.groups[i['group']].append(i['name'])
  48. def set_logger(self, logger):
  49. self.logger = logger
  50. def set_S7Client(self, s7c: S7Client):
  51. self.S7Client = s7c
  52. def get_S7Client(self):
  53. return self.S7Client
  54. def send(self, name):
  55. if self.nodes[name]['type'] == 'int':
  56. data = snap7.util.get_int(self.node_data[name], 0)
  57. elif self.nodes[name]['type'] == 'dint':
  58. data = snap7.util.get_dint(self.node_data[name], 0)
  59. elif self.nodes[name]['type'] == 'bool':
  60. data = snap7.util.get_bool(self.node_data[name], 0, int(self.nodes[name]['offset']))
  61. elif self.nodes[name]['type'] == 'boollist':
  62. data = [(self.node_data[name][0] >> i) & 1 for i in range(8)]
  63. elif self.nodes[name]['type'] == 'real':
  64. data = snap7.util.get_real(self.node_data[name], 0)
  65. elif self.nodes[name]['type'] == 'string':
  66. data = self.node_data[name][2:2+int.from_bytes(self.node_data[name][1:2])].decode('gbk')
  67. elif self.nodes[name]['type'] == 'wstring':
  68. data = self.node_data[name][4:].decode(encoding='utf-16be')
  69. elif self.nodes[name]['type'] == 'int_list':
  70. data = []
  71. for i in range(0, int(self.nodes[name]['size']), 2):
  72. data.append(snap7.util.get_int(self.node_data[name], i))
  73. else:
  74. warnings.warn('暂不支持的类型:' + self.nodes[name]['type'])
  75. if self.logger:
  76. self.logger.error('暂不支持的类型:' + self.nodes[name]['type'])
  77. return None
  78. if name in self.target_from_name:
  79. for i in self.target_from_name[name]:
  80. i.inject(data)
  81. if self.nodes[name]['type'] == 'boollist' and name + '*' in self.target_from_name:
  82. for i in range(8):
  83. for j in self.target_from_name[name+'*'][i]:
  84. j.inject(data[i])
  85. def update(self, name):
  86. nodeinfo = self.nodes[name]
  87. try:
  88. while True:
  89. if not self.thread_run:
  90. return None
  91. self.lock.acquire()
  92. if not self.S7Client.get_connected():
  93. warnings.warn('S7Client连接中断')
  94. if self.logger:
  95. self.logger.error('S7Client连接中断')
  96. self.thread_run = False
  97. self.lock.release()
  98. return None
  99. tmp = self.S7Client.db_read(int(nodeinfo['db']), int(nodeinfo['start']), int(nodeinfo['size']))
  100. self.lock.release()
  101. if self.node_data[name] != tmp:
  102. self.node_data[name] = tmp
  103. self.send(name)
  104. time.sleep(float(nodeinfo['frequency']) / 1000)
  105. except RuntimeError as reason:
  106. warnings.warn(reason)
  107. if self.logger:
  108. self.logger.error(reason)
  109. self.thread_run = False
  110. self.lock.release()
  111. def start_auto_update(self):
  112. if self.thread_run:
  113. return None
  114. self.threads = []
  115. if self.S7Client == None:
  116. warnings.warn('未初始化S7Client')
  117. if self.logger:
  118. self.logger.error('未初始化S7Client')
  119. return None
  120. if not self.S7Client.get_connected():
  121. warnings.warn('S7Client未连接')
  122. if self.logger:
  123. self.logger.error('S7Client未连接')
  124. return None
  125. for key, value in self.nodes.items():
  126. if value['read_allow'].upper() != 'FALSE':
  127. self.threads.append(threading.Thread(target=self.update, args=(value['name'],)))
  128. self.thread_run = True
  129. for i in self.threads:
  130. i.start()
  131. def update_group(self, group_name):
  132. nodesname = self.groups[group_name]
  133. db_number = []
  134. start = []
  135. size = []
  136. for name in nodesname:
  137. nodeinfo = self.nodes[name]
  138. db_number.append(int(nodeinfo['db']))
  139. start.append(int(nodeinfo['start']))
  140. size.append(int(nodeinfo['size']))
  141. while True:
  142. if not self.thread_run:
  143. return None
  144. tmp = False
  145. read_valid = True
  146. with self.lock:
  147. if not self.S7Client.get_connected():
  148. warnings.warn('S7Client连接中断')
  149. if self.logger:
  150. self.logger.error('S7Client连接中断')
  151. self.thread_run = False
  152. return None
  153. try:
  154. tmp = self.S7Client.multi_db_read_py(db_number, start, size)
  155. except RuntimeError as reason:
  156. warnings.warn(reason)
  157. read_valid = False
  158. if self.logger:
  159. self.logger.error(reason)
  160. self.thread_run = False
  161. if read_valid and tmp:
  162. for i in range(len(tmp)):
  163. if self.node_data[nodesname[i]] != tmp[i]:
  164. self.node_data[nodesname[i]] = tmp[i]
  165. self.send(nodesname[i])
  166. def auto_update_group(self):
  167. if self.thread_run:
  168. return None
  169. self.threads = []
  170. if self.S7Client == None:
  171. warnings.warn('未初始化S7Client')
  172. if self.logger:
  173. self.logger.error('未初始化S7Client')
  174. return None
  175. if not self.S7Client.get_connected():
  176. warnings.warn('S7Client未连接')
  177. if self.logger:
  178. self.logger.error('S7Client未连接')
  179. return None
  180. for group in self.groups.keys():
  181. self.threads.append(threading.Thread(target=self.update_group, args=(group,)))
  182. self.thread_run = True
  183. for i in self.threads:
  184. i.start()
  185. def end_auto_update(self):
  186. self.thread_run = False
  187. for i in self.threads:
  188. i.join()
  189. def make_point(self, name):
  190. index = -1
  191. solvedname = name
  192. if len(name) > 3 and name[-3] == '[' and name[-1] == ']' and name[-2].isdigit() and 0 <= int(name[-2]) < 8:
  193. index = int(name[-2])
  194. name = name[:-3]
  195. solvedname = name + '*'
  196. if name not in self.nodes:
  197. raise ValueError("创建了未配置的点")
  198. if solvedname not in self.target_from_name:
  199. if index == -1:
  200. self.target_from_name[solvedname] = []
  201. else:
  202. self.target_from_name[solvedname] = [[],[],[],[],[],[],[],[]]
  203. res = Statepoint()
  204. if index == -1:
  205. self.target_from_name[solvedname].append(res)
  206. else:
  207. self.target_from_name[solvedname][index].append(res)
  208. self.send(name)
  209. return res