serial_device_controller.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798
  1. import random
  2. import threading
  3. import time
  4. from typing import Optional
  5. import Levenshtein
  6. import numpy as np
  7. from flask import jsonify, Flask, request, Response
  8. from flask_cors import CORS
  9. from program_public_tools import ProgramPublicTools
  10. from serial_com_service import SerialComService
  11. class SerialDevice(object):
  12. def __init__(self, pubtools: ProgramPublicTools):
  13. self.__pubtools: ProgramPublicTools = pubtools
  14. self.cnc_attenuator: CNCAttenuator = CNCAttenuator(self.__pubtools)
  15. self.plc_sender: PLCDeviceSender = PLCDeviceSender(self.__pubtools, "PLC-Sender")
  16. self.plc_receiver: PLCDeviceReceiver = PLCDeviceReceiver(self.__pubtools, "PLC-Receiver")
  17. self.plc_receiver2: PLCDeviceReceiver = PLCDeviceReceiver(self.__pubtools, "PLC-Receiver2")
  18. self.enable_multi_receiver: bool = False
  19. self.__enable_match_cnc: bool = False
  20. self.com_usr_config_success = False
  21. def match_connection(self, com_service: SerialComService, enable_auto_match_plc_device: bool = False) \
  22. -> tuple[bool, bool]:
  23. matched: bool = False
  24. cnc_matched: bool = False
  25. if (self.cnc_attenuator.com is None) and (self.__enable_match_cnc is True):
  26. matched = self.cnc_attenuator.match_connection(com_service)
  27. if matched:
  28. cnc_matched = True
  29. if enable_auto_match_plc_device is True:
  30. if matched is False:
  31. if self.plc_sender.com is None:
  32. matched = self.plc_sender.match_connection(com_service)
  33. elif self.plc_receiver.com is None:
  34. matched = self.plc_receiver.match_connection(com_service)
  35. else:
  36. matched = False
  37. return matched, cnc_matched
  38. def check(self):
  39. if self.cnc_attenuator.com is not None:
  40. serial_device_list = ["CNC-Attenuator"]
  41. not_connect_list = []
  42. else:
  43. serial_device_list = []
  44. not_connect_list = ["CNC-Attenuator"]
  45. return serial_device_list, not_connect_list
  46. class SerialDeviceController(object):
  47. def __init__(self, pubtools: ProgramPublicTools):
  48. self.__pubtools: ProgramPublicTools = pubtools
  49. self.__port_list = SerialComService(self.__pubtools).port_scan()
  50. self.services: SerialDevice = SerialDevice(self.__pubtools)
  51. self.__baud_rate: int = 115200
  52. self.__connection_lib: dict = {}
  53. self.__tester: Optional[TestService] = None
  54. def get_tester(self):
  55. if self.__tester is None:
  56. self.__tester = TestService()
  57. return self.__tester
  58. def auto_connect(self) -> tuple[list[str], list[str], bool]:
  59. return self.auto_match(self.get_port_list())
  60. def get_port_list(self) -> list:
  61. return self.__port_list
  62. def auto_match(self, device_port_list: list[str]) -> tuple[list[str], list[str], bool]:
  63. not_matched_port_list: list[str] = []
  64. matched_port_list: list[str] = []
  65. cnc_match_result: bool = False
  66. for port_name in device_port_list:
  67. com_service = self.try_build_connection(port_name)
  68. match_flag: bool = False
  69. if com_service is not None:
  70. if com_service.connection is not None:
  71. match_result, cnc_match_result = self.services.match_connection(com_service)
  72. if match_result is True:
  73. match_flag = True
  74. else:
  75. com_service.port_disconnect()
  76. if match_flag is True:
  77. matched_port_list.append(port_name)
  78. else:
  79. not_matched_port_list.append(port_name)
  80. return matched_port_list, not_matched_port_list, cnc_match_result
  81. def try_build_connection(self, port_name: str, if_add_source_to_lib: bool = False) -> Optional[SerialComService]:
  82. if port_name in self.__connection_lib:
  83. return self.__connection_lib[port_name]
  84. else:
  85. com_service: SerialComService = SerialComService(self.__pubtools)
  86. connection_result = False
  87. try:
  88. connection_result: bool = com_service.port_connect(port_name, self.__baud_rate)
  89. except Exception as e:
  90. self.__pubtools.debug_output(f"Error when attempting to connect port [{port_name}]: {e}")
  91. if connection_result is True:
  92. if if_add_source_to_lib is True:
  93. self.__connection_lib[port_name] = com_service
  94. return com_service
  95. else:
  96. return None
  97. class PLCDevice(object):
  98. def __init__(self, pubtools: ProgramPublicTools, name: str):
  99. self.pubtools: ProgramPublicTools = pubtools
  100. self.com: Optional[SerialComService] = None
  101. self.name: str = name
  102. self.frame_std: bytes = 200 * b'#123456789#X' + b'123456789#\n'
  103. def match_connection(self, com: SerialComService) -> bool:
  104. self.com = com
  105. return True
  106. class PLCDeviceSender(PLCDevice):
  107. def __init__(self, pubtools: ProgramPublicTools, name: str):
  108. super().__init__(pubtools, name)
  109. self.send_pkg_number: int = 0
  110. self.send_pkg_count: int = 0
  111. self.send_pkg_length: int = len(self.frame_std)
  112. self.__s_server: FloatServer = FloatServer("Sender")
  113. self.__is_running: bool = False
  114. self.server_port = self.pubtools.find_available_server_port(number=1, address="0.0.0.0", start_port=8393)[0]
  115. self.server_url = f"sender: http://localhost:{self.server_port}{self.__s_server.name}"
  116. self.__pause: bool = False
  117. self.sleep_sec_every_step: float = 0
  118. self.sleep_sec_every_ten_step: float = 0.01
  119. self.__ten_step_counter: int = 0
  120. self.pull_up_signal_when_sending: bool = False
  121. self.__is_resetting: bool = False
  122. def clear(self):
  123. self.send_pkg_number = 0
  124. self.send_pkg_count = 0
  125. self.__ten_step_counter = 0
  126. def keep_send(self):
  127. self.__pause = False
  128. if self.__is_running is not True:
  129. self.__start_server()
  130. self.__is_running = True
  131. return self.__return_server_info()
  132. def pause(self):
  133. self.__pause = True
  134. def pause_and_reset_frame_std_sender(self, frame_std_new):
  135. if self.__is_resetting is False:
  136. self.__is_resetting = True
  137. self.pause()
  138. self.frame_std = frame_std_new
  139. self.send_pkg_length = len(self.frame_std)
  140. self.__is_resetting = False
  141. @staticmethod
  142. def __start_server():
  143. return None
  144. def __start_server_origin(self):
  145. threading.Thread(target=self.__send_thread_main).start()
  146. threading.Thread(target=self.__sender_server_threading_main, args=(self.server_port,)).start()
  147. print(self.server_url)
  148. return self.__return_server_info()
  149. @staticmethod
  150. def __return_server_info():
  151. return None, None
  152. def __return_server_info_origin(self):
  153. return self.server_port, self.server_url
  154. def __sender_server_threading_main(self, port):
  155. self.__s_server.run("0.0.0.0", port)
  156. def __send_thread_main(self):
  157. if self.sleep_sec_every_step != 0:
  158. print(f"[Tester] Using config: Waiting [{self.sleep_sec_every_step}] seconds every message.")
  159. if self.sleep_sec_every_step != 0:
  160. print(f"[Tester] Using config: Waiting [{self.sleep_sec_every_step}] seconds every ten message.")
  161. while True:
  162. if self.__pause is False:
  163. time.sleep(self.sleep_sec_every_step)
  164. self.__ten_step_counter += 1
  165. if self.__ten_step_counter == 10:
  166. self.__ten_step_counter = 0
  167. time.sleep(self.sleep_sec_every_ten_step)
  168. self.__s_server.push(self.send_pkg_count)
  169. else:
  170. time.sleep(0.1)
  171. def send_once(self):
  172. print(f"WRITE {self.frame_std}")
  173. self.com.connection.write(self.frame_std)
  174. self.pull_up_signal_when_sending = True
  175. self.send_pkg_number += 1
  176. self.send_pkg_count = self.send_pkg_number * self.send_pkg_length
  177. class PLCDeviceReceiver(PLCDevice):
  178. def __init__(self, pubtools: ProgramPublicTools, name: str):
  179. super().__init__(pubtools, name)
  180. self.error_count: int = 0
  181. self.receive_pkg: int = 0
  182. self.receive_bytes: int = 0
  183. self.receive_bytes_this_circle: int = 0
  184. self.__r_server: FloatServer = FloatServer("Receiver")
  185. self.server_port = self.pubtools.find_available_server_port(number=1, address="0.0.0.0", start_port=8353)[0]
  186. self.server_url = f"receive: http://localhost:{self.server_port}{self.__r_server.name}"
  187. self.__is_running: bool = False
  188. self.__pause: bool = False
  189. self.pull_up_signal_when_receiving: bool = False
  190. self.auto_pause_when_receiving = False
  191. self.__update_frame_std: bool = False
  192. self.__is_resetting: bool = False
  193. def clear(self):
  194. self.error_count = 0
  195. self.receive_pkg = 0
  196. self.receive_bytes = 0
  197. self.receive_bytes_this_circle: int = 0
  198. def keep_receive(self):
  199. self.__pause = False
  200. if self.__is_running is not True:
  201. self.__start_server()
  202. self.__is_running = True
  203. return self.__return_info()
  204. def pause(self):
  205. self.__pause = True
  206. def __start_server(self):
  207. threading.Thread(target=self.__receive_thread_main).start()
  208. threading.Thread(target=self.__receiver_server_threading_main, args=(self.server_port,)).start()
  209. print(self.server_url)
  210. return self.__return_info()
  211. def __return_info(self):
  212. return self.server_port, self.server_url
  213. def __receiver_server_threading_main(self, port):
  214. self.__r_server.run("0.0.0.0", port)
  215. def pause_and_reset_frame_std_receiver(self, frame_std_new):
  216. if self.__is_resetting is False:
  217. self.__is_resetting = True
  218. self.frame_std = frame_std_new
  219. self.pause()
  220. self.__update_frame_std = True
  221. def __receive_thread_main(self):
  222. frame_std: bytes = self.frame_std
  223. frame_std_len: int = len(frame_std)
  224. while True:
  225. if self.__update_frame_std is True:
  226. frame_std: bytes = self.frame_std
  227. frame_std_len: int = len(frame_std)
  228. self.__update_frame_std = False
  229. self.__is_resetting = False
  230. if self.__pause is False:
  231. self.receive_bytes_this_circle = 0
  232. line = self.com.connection.readline()
  233. if line:
  234. self.receive_pkg += 1
  235. self.receive_bytes = self.receive_bytes + len(line)
  236. self.receive_bytes_this_circle = len(line)
  237. self.pull_up_signal_when_receiving = True
  238. get_error_bytes = self.calculate_ber_optimized(frame_std, line)
  239. if get_error_bytes > frame_std_len:
  240. self.error_count += frame_std_len
  241. else:
  242. self.error_count += get_error_bytes
  243. self.__r_server.push(self.error_count)
  244. if self.auto_pause_when_receiving is True:
  245. self.pause()
  246. else:
  247. time.sleep(0.1)
  248. @staticmethod
  249. def calculate_ber_optimized(pkg_send, pkg_receive):
  250. assert isinstance(pkg_send, bytes) and isinstance(pkg_receive, bytes), "Input should be bytes"
  251. assert len(pkg_send) > 0 and len(pkg_receive) > 0, "Input bytes should not be empty"
  252. min_length = min(len(pkg_send), len(pkg_receive))
  253. bit_errors = 0
  254. for send_byte, receive_byte in zip(pkg_send[:min_length], pkg_receive[:min_length]):
  255. if send_byte != receive_byte:
  256. send_bits = np.unpackbits(np.array([send_byte], dtype=np.uint8))
  257. receive_bits = np.unpackbits(np.array([receive_byte], dtype=np.uint8))
  258. bit_errors += np.sum(send_bits != receive_bits)
  259. return np.int_(bit_errors / 8)
  260. class CNCAttenuator(object):
  261. def __init__(self, pubtools: ProgramPublicTools):
  262. self.__pubtools: ProgramPublicTools = pubtools
  263. self.com: Optional[SerialComService] = None
  264. self.name: str = "CNC-Attenuator"
  265. def match_connection(self, com: SerialComService) -> bool:
  266. for _ in range(0, 2):
  267. com.connection.write(b'att-000.00\r\n')
  268. com.connection.timeout = 1
  269. data = com.connection.readline().decode('utf-8')
  270. if data.lower() == "attOK".lower():
  271. self.com = com
  272. self.__pubtools.debug_output("Successfully connect to CNC-Attenuator.")
  273. return True
  274. return False
  275. def set(self, num: float) -> bool:
  276. if self.com is None:
  277. return False
  278. else:
  279. try:
  280. formatted_num_str = "{:06.2f}".format(num)
  281. formatted_byte_str = b'att-' + formatted_num_str.encode('utf-8') + b'\r\n'
  282. self.com.connection.write(formatted_byte_str)
  283. except Exception as e:
  284. return False
  285. else:
  286. return True
  287. class FloatServer:
  288. def __init__(self, name: Optional[str] = None):
  289. self.name: Optional[str] = name
  290. self.__app = Flask(__name__)
  291. CORS(self.__app)
  292. self.__value = 0.0
  293. self.__instrument_on = False # 新增的仪器状态标志
  294. self.__setup_routes()
  295. def __setup_routes(self):
  296. if self.name is None:
  297. self.name = f"/float_default_{time.time()}"
  298. else:
  299. self.name = f"/{self.name}"
  300. @self.__app.route(self.name, methods=['GET'])
  301. def get_value():
  302. return jsonify({'value': self.__value})
  303. # 新增的端点来获取仪器的状态
  304. @self.__app.route(f"{self.name}/instrument_status", methods=['GET'])
  305. def get_instrument_status():
  306. return jsonify({'status': self.__instrument_on})
  307. # 新增的端点来更改仪器的状态
  308. @self.__app.route(f"{self.name}/set_instrument_status", methods=['POST'])
  309. def set_instrument_status():
  310. status = request.json.get('status', None)
  311. if status is not None:
  312. self.__instrument_on = status
  313. return Response("Instrument status updated", status=200)
  314. else:
  315. return Response("Invalid request format", status=400)
  316. def run(self, host, port):
  317. self.__app.run(host=host, port=port)
  318. def push(self, new_value):
  319. self.__value = new_value
  320. class TestService(object):
  321. def __init__(self):
  322. self.config_sleep_sec_every_step: float = 0
  323. self.config_sleep_sec_every_ten_step: float = 0
  324. self.error_rate_percent: float = 0
  325. self.error_rate_percent2: float = 0
  326. self.__is_tester_running: bool = False
  327. self.__threading = None
  328. self.__sender = None
  329. self.__receiver = None
  330. self.__receiver2 = None
  331. self.__task_reset_error_rate_percent: bool = False
  332. self.__speed_timer: float = 0
  333. self.speed_kbps: float = 0
  334. self.speed_kbps2: float = 0
  335. self.__rev_timer_sec: float = time.time()
  336. self.__rev_timeout_sec: float = 2
  337. self.__is_resetting_frame_std_and_restarting: bool = False
  338. self.send_package_1kbit: bytes = 25 * b'12345' + b'\n'
  339. self.send_package_16kbit: bytes = 400 * b'12345' + b'\n'
  340. self.test_service_init_complete_flag: bool = False
  341. self.have_test_service_task_in_circle: bool = False
  342. self.have_task_reset_frame_std: bool = False
  343. self.task_lock_reset_frame_std_and_restart: bool = False
  344. self.__frame_std_new: bytes = self.send_package_16kbit
  345. self.__nullable_circle_completed_hook = None
  346. self.motor_msg_list: list[bytes] = [bytes.fromhex('FF FF FE 07 03 2A 00 00 03 E8 E2'),
  347. bytes.fromhex('FF FF FE 07 03 2A 02 00 03 E8 E0'),
  348. bytes.fromhex('FF FF FE 07 03 2A 04 00 03 E8 DE'),
  349. bytes.fromhex('FF FF FE 07 03 2A 08 00 03 E8 DA')]
  350. self.relay_msg_list: list[bytes] = [bytes.fromhex('68 09 00 FF 12 00 01 12 16'),
  351. bytes.fromhex('68 09 00 FF 12 00 00 11 16')]
  352. self.__motor_and_relay_msg_list: list[bytes] = self.motor_msg_list + self.relay_msg_list
  353. self.__motor_and_relay_msg_list_length: int = len(self.__motor_and_relay_msg_list)
  354. self.__default_msg_queue: list[bytes] = self.motor_msg_list
  355. self.__using_msg_queue: list[bytes] = self.__default_msg_queue
  356. self.__using_msg_queue_counter_index: int = 0
  357. self.__using_msg_queue_counter_index_max: int = 0
  358. self.__msg_convert_interval_time_sec: float = 0.8
  359. self.__circle_hook_init: bool = False
  360. self.__pause_receive_under_hook_mode: bool = False
  361. self.__msg_once_send_buffer: Optional[bytes] = None
  362. self.__msg_once_send_completed: bool = False
  363. self.__buffer_index_set_circle_hook_to_msg_once_mode = 0
  364. def set_circle_hook_to_msg_once_mode(self, msg: Optional[bytes] = None):
  365. if msg is None:
  366. range_num_min: int = 1
  367. range_num_max: int = self.__motor_and_relay_msg_list_length
  368. while True:
  369. get_number: int = random.randint(range_num_min, range_num_max)
  370. get_index: int = get_number - 1
  371. if get_index != self.__buffer_index_set_circle_hook_to_msg_once_mode:
  372. break
  373. self.__buffer_index_set_circle_hook_to_msg_once_mode = get_index
  374. msg = self.__motor_and_relay_msg_list[get_index]
  375. print(f"[Hook] MSG is None: Using random index [{get_index}]: {msg}")
  376. self.__msg_once_send_buffer = msg
  377. self.__msg_once_send_completed = False
  378. self.__set_nullable_circle_completed_hook(self.__circle_hook_method_msg_once_mode)
  379. self.__pause_receive_under_hook_mode = True
  380. def __circle_hook_method_msg_once_mode(self):
  381. # time.sleep(self.__msg_convert_interval_time_sec)
  382. if self.__msg_once_send_completed is False:
  383. if self.__msg_once_send_buffer is not None:
  384. self.reset_frame_std_and_restart(self.__msg_once_send_buffer)
  385. else:
  386. print("[Hook] Warning: Flag completed is false but buffer is None.")
  387. self.__msg_once_send_completed = True
  388. else:
  389. self.__msg_once_send_buffer = None
  390. self.reset_circle_hook_to_none()
  391. def set_circle_hook_to_using_msg_queue(self, msg_queue: Optional[list[bytes]] = None):
  392. if msg_queue is not None:
  393. self.__using_msg_queue = msg_queue
  394. else:
  395. self.__using_msg_queue = self.__default_msg_queue
  396. self.__set_nullable_circle_completed_hook(self.__circle_hook_method_using_msg_queue)
  397. self.__pause_receive_under_hook_mode = True
  398. def __circle_hook_method_using_msg_queue(self):
  399. time.sleep(self.__msg_convert_interval_time_sec)
  400. if self.__circle_hook_init is False:
  401. self.__using_msg_queue_counter_index_current = 0
  402. self.__using_msg_queue_counter_index_max = len(self.__using_msg_queue) - 1
  403. self.__circle_hook_init = True
  404. self.reset_frame_std_and_restart(self.__using_msg_queue[self.__using_msg_queue_counter_index_current])
  405. if self.__using_msg_queue_counter_index_current == self.__using_msg_queue_counter_index_max:
  406. self.__using_msg_queue_counter_index_current = 0
  407. else:
  408. self.__using_msg_queue_counter_index_current += 1
  409. def reset_circle_hook_to_none(self):
  410. self.__set_nullable_circle_completed_hook(None)
  411. self.__pause_receive_under_hook_mode = False
  412. def __set_nullable_circle_completed_hook(self, func_nullable_circle_completed_hook):
  413. self.__circle_hook_init = False
  414. self.__nullable_circle_completed_hook = func_nullable_circle_completed_hook
  415. def run_as_main(self):
  416. run_mode = ""
  417. while True:
  418. print("Run Mode: A: PLC-Tester B: CNC-Controller")
  419. input_mode = input("Choose a Mode: ").lower()
  420. if input_mode == "a":
  421. run_mode = input_mode
  422. break
  423. elif input_mode == "b":
  424. run_mode = input_mode
  425. break
  426. else:
  427. print("Invalid mode input! Try input again.")
  428. if run_mode == "a":
  429. self.run_as_plc_tester()
  430. elif run_mode == "b":
  431. self.run_as_cnc_controller()
  432. else:
  433. raise Exception("Error: Unknown type of run mode.")
  434. def run_as_cnc_controller(self):
  435. program_pubtools: ProgramPublicTools = ProgramPublicTools()
  436. serial_device_controller: SerialDeviceController = SerialDeviceController(program_pubtools)
  437. port_list = serial_device_controller.get_port_list()
  438. port_info: str = ''
  439. index_num: int = 0
  440. for port_name in port_list:
  441. port_info = port_info + f"Index[{index_num}]={port_name} "
  442. index_num += 1
  443. print(port_info)
  444. index = input("[CNC Controller] index: ")
  445. cnc_controller: SerialComService = SerialComService(program_pubtools)
  446. cnc_controller.port_connect(port_list[int(index)])
  447. result: bool = serial_device_controller.services.cnc_attenuator.match_connection(cnc_controller)
  448. if result is False:
  449. raise Exception("[CNC Controller] Error: Cannot connect to cnc device.")
  450. else:
  451. while True:
  452. get_value_input = input("Input to set cnc value: (dB) ")
  453. try:
  454. set_value = float(get_value_input)
  455. except ValueError:
  456. print("Invalid input! CNC value is under float format. Please enter a float number.")
  457. else:
  458. serial_device_controller.services.cnc_attenuator.set(set_value)
  459. print("Set success!")
  460. input("Input any content to continue...")
  461. def run_as_plc_tester(self):
  462. program_pubtools: ProgramPublicTools = ProgramPublicTools()
  463. serial_device_controller: SerialDeviceController = SerialDeviceController(program_pubtools)
  464. # serial_device_controller.auto_connect()
  465. port_list = serial_device_controller.get_port_list()
  466. port_info: str = ''
  467. index_num: int = 0
  468. for port_name in port_list:
  469. port_info = port_info + f"Index[{index_num}]={port_name} "
  470. index_num += 1
  471. print(port_info)
  472. index_1 = input("[Sender] index: ")
  473. index_2 = input("[Receiver] index: ")
  474. index_3 = input("[Receiver2] index: (Optional) ")
  475. com_sender: SerialComService = SerialComService(program_pubtools)
  476. com_sender.port_connect(port_list[int(index_1)])
  477. com_receiver: SerialComService = SerialComService(program_pubtools)
  478. com_receiver.port_connect(port_list[int(index_2)])
  479. com_receiver2: Optional[SerialComService] = None
  480. if index_3:
  481. print(f"[Serial Controller] Multi Receiver Mode is enabled. Receiver2: {port_list[int(index_3)]}.")
  482. com_receiver2 = SerialComService(program_pubtools)
  483. com_receiver2.port_connect(port_list[int(index_3)])
  484. serial_device_controller.services.plc_sender.match_connection(com_sender)
  485. serial_device_controller.services.plc_receiver.match_connection(com_receiver)
  486. serial_device_controller.services.plc_receiver2.match_connection(com_receiver2)
  487. while True:
  488. print("Please config interval seconds for every message.")
  489. get_sec = input("[One Message] Seconds: float = ")
  490. try:
  491. float_sec = float(get_sec)
  492. break
  493. except ValueError:
  494. print("Invalid input! Seconds config is under float format. Please enter a float number.")
  495. while True:
  496. print("Please config interval seconds for every ten messages.")
  497. get_sec_ten = input("[Ten Messages] Seconds: float = ")
  498. try:
  499. float_sec_ten = float(get_sec_ten)
  500. break
  501. except ValueError:
  502. print("Invalid input! Seconds config is under float format. Please enter a float number.")
  503. serial_device_controller.services.plc_sender.sleep_sec_every_step = float_sec
  504. serial_device_controller.services.plc_sender.sleep_sec_every_ten_step = float_sec_ten
  505. serial_device_controller.services.plc_receiver.keep_receive()
  506. if com_receiver2:
  507. serial_device_controller.services.plc_receiver2.keep_receive()
  508. self.start_test_service(serial_device_controller.services.plc_sender,
  509. serial_device_controller.services.plc_receiver,
  510. serial_device_controller.services.plc_receiver2)
  511. else:
  512. self.start_test_service(serial_device_controller.services.plc_sender,
  513. serial_device_controller.services.plc_receiver, None)
  514. while True:
  515. get_input: str = input()
  516. if get_input:
  517. if get_input == "b":
  518. self.set_circle_hook_to_msg_once_mode()
  519. if get_input == "a":
  520. if self.__nullable_circle_completed_hook is None:
  521. self.set_circle_hook_to_using_msg_queue()
  522. else:
  523. self.reset_circle_hook_to_none()
  524. def task_add_reset_error_rate(self):
  525. self.__task_reset_error_rate_percent = True
  526. def __do_reset_error_rate(self):
  527. if self.__sender and self.__receiver:
  528. self.__sender.clear()
  529. self.__receiver.clear()
  530. if self.__receiver2:
  531. self.__receiver2.clear()
  532. self.__speed_timer = time.time()
  533. def error_rate_push(self, error_rate_percent: float, receiver_id: int = 1):
  534. if receiver_id == 2:
  535. self.error_rate_percent2 = error_rate_percent
  536. else:
  537. self.error_rate_percent = error_rate_percent
  538. def start_test_service(self, sender: PLCDeviceSender, receiver: PLCDeviceReceiver,
  539. receiver2: Optional[PLCDeviceReceiver] = None):
  540. self.__pause_tester_signal = False
  541. if self.__is_tester_running is False:
  542. self.__start_tester_threading(sender, receiver, receiver2)
  543. self.__is_tester_running = True
  544. def pause_test_service(self):
  545. self.__pause_tester_signal = True
  546. def reset_frame_std_and_restart(self, frame_std_new: bytes):
  547. if self.task_lock_reset_frame_std_and_restart is False:
  548. self.task_lock_reset_frame_std_and_restart = True
  549. self.__frame_std_new = frame_std_new
  550. self.have_test_service_task_in_circle = True
  551. self.have_task_reset_frame_std = True
  552. def __do_reset_frame_std_and_restart(self):
  553. if self.__is_resetting_frame_std_and_restarting is False:
  554. self.__is_resetting_frame_std_and_restarting = True
  555. self.pause_test_service()
  556. self.__sender.pause_and_reset_frame_std_sender(self.__frame_std_new)
  557. self.__receiver.pause_and_reset_frame_std_receiver(self.__frame_std_new)
  558. if self.__receiver2 is not None:
  559. self.__receiver2.pause_and_reset_frame_std_receiver(self.__frame_std_new)
  560. self.task_add_reset_error_rate()
  561. self.start_test_service(self.__sender, self.__receiver, self.__receiver2)
  562. self.__is_resetting_frame_std_and_restarting = False
  563. def __test_service_task_in_circle(self):
  564. if self.have_task_reset_frame_std is True:
  565. self.__do_reset_frame_std_and_restart()
  566. self.have_task_reset_frame_std = False
  567. self.task_lock_reset_frame_std_and_restart = False
  568. def __start_tester_threading(self, sender: PLCDeviceSender, receiver: PLCDeviceReceiver,
  569. receiver2: Optional[PLCDeviceReceiver] = None):
  570. self.__threading = threading.Thread(target=self.test_service, args=(sender, receiver, receiver2)).start()
  571. def __check_task_reset_error_rate(self):
  572. if self.__task_reset_error_rate_percent is True:
  573. self.__do_reset_error_rate()
  574. self.__task_reset_error_rate_percent = False
  575. def update_error_rate(self, receiver_number: int = None):
  576. if receiver_number == 2:
  577. receiver = self.__receiver2
  578. else:
  579. receiver = self.__receiver
  580. self.__check_task_reset_error_rate()
  581. if (self.__sender is not None) and (receiver is not None):
  582. count_all = self.__sender.send_pkg_count
  583. get_error = receiver.error_count
  584. pkg_lose_bytes = (self.__sender.send_pkg_number - receiver.receive_pkg) * self.__sender.send_pkg_length
  585. count_error_all = pkg_lose_bytes + get_error
  586. if count_all != 0:
  587. error_rate_percent: float = 100 * count_error_all / count_all
  588. else:
  589. error_rate_percent: float = 0.0
  590. self.error_rate_push(error_rate_percent, receiver_number)
  591. self.__update_speed(count_all, pkg_lose_bytes, receiver_number)
  592. return count_all, pkg_lose_bytes, count_error_all
  593. else:
  594. return 0, 0, 0
  595. def __update_speed(self, count_all_bytes, pkg_lose_bytes, receiver_number: int = None):
  596. get_time = time.time() - self.__speed_timer
  597. if get_time != 0:
  598. speed_kbps = (count_all_bytes - pkg_lose_bytes) * 8 / (1000 * get_time)
  599. else:
  600. speed_kbps = 0
  601. if receiver_number == 2:
  602. self.speed_kbps2 = speed_kbps
  603. else:
  604. self.speed_kbps = speed_kbps
  605. def test_service(self, sender: PLCDeviceSender, receiver: PLCDeviceReceiver,
  606. receiver2: Optional[PLCDeviceReceiver] = None):
  607. self.__sender = sender
  608. self.__receiver = receiver
  609. self.__receiver2 = receiver2
  610. self.__sender.sleep_sec_every_step = self.config_sleep_sec_every_step
  611. self.__sender.sleep_sec_every_ten_step = self.config_sleep_sec_every_ten_step
  612. self.__receiver.auto_pause_when_receiving = True
  613. if self.__receiver2:
  614. self.__receiver2.auto_pause_when_receiving = True
  615. self.__receiver.keep_receive()
  616. if self.__receiver2:
  617. self.__receiver2.keep_receive()
  618. self.__sender.send_once()
  619. self.__rev_timer_sec = time.time()
  620. self.__rev_timeout_sec = 2
  621. self.__speed_timer = time.time()
  622. for _ in range(0, 3):
  623. self.__test_circle(if_print=False)
  624. self.task_add_reset_error_rate()
  625. self.test_service_init_complete_flag = True
  626. while True:
  627. if self.have_test_service_task_in_circle is True:
  628. self.__test_service_task_in_circle()
  629. self.have_test_service_task_in_circle = False
  630. if self.__pause_tester_signal is False:
  631. if self.__pause_receive_under_hook_mode is False:
  632. self.__test_circle(if_print=True)
  633. else:
  634. print("[Hook] Program is running under hook mode.")
  635. self.__sender.send_once()
  636. if self.__nullable_circle_completed_hook is not None:
  637. print("[Hook] Do hook func...")
  638. self.__nullable_circle_completed_hook()
  639. else:
  640. print("[Hook] Warning: Hook func is empty.")
  641. time.sleep(0.1)
  642. else:
  643. time.sleep(0.1)
  644. @staticmethod
  645. def __tester_output(message: str, if_print: bool):
  646. if if_print is True:
  647. print(message)
  648. def __test_circle(self, if_print: bool = True):
  649. rev_flag_1 = self.__receiver.pull_up_signal_when_receiving
  650. if self.__receiver2 is None:
  651. rev_flag_2 = False
  652. else:
  653. rev_flag_2 = self.__receiver2.pull_up_signal_when_receiving
  654. if (self.__receiver2 is None) and (rev_flag_1 is True):
  655. count_all, pkg_lose_bytes, count_error = self.update_error_rate(1)
  656. self.__tester_output("====================================================", if_print)
  657. self.__tester_output(
  658. f"all: {count_all * 8} bit, lose: {pkg_lose_bytes * 8} bit, error: {count_error * 8} bit", if_print)
  659. self.__tester_output(f"error_rate: {self.error_rate_percent} %", if_print)
  660. self.__tester_output(f"DBG: 发包={self.__sender.send_pkg_number} 收包={self.__receiver.receive_pkg}",
  661. if_print)
  662. self.__tester_output(f"speed: {self.speed_kbps} kbps", if_print)
  663. if self.__sender.send_pkg_number % 5 == 0:
  664. self.__tester_output(f"[Sender] Package Length: {len(self.__sender.frame_std)}", if_print)
  665. self.__tester_output(f"[Receiver] Package Length: {len(self.__receiver.frame_std)}", if_print)
  666. self.__receiver.pull_up_signal_when_receiving = False
  667. time.sleep(self.__sender.sleep_sec_every_step)
  668. if self.__sender.send_pkg_number % 10 == 0:
  669. time.sleep(self.__sender.sleep_sec_every_ten_step)
  670. self.__receiver.keep_receive()
  671. self.__sender.send_once()
  672. self.__rev_timer_sec = time.time()
  673. elif (self.__receiver2 is not None) and (rev_flag_1 is True) and (rev_flag_2 is True):
  674. count_all, pkg_lose_bytes, count_error = self.update_error_rate(1)
  675. count_all2, pkg_lose_bytes2, count_error2 = self.update_error_rate(2)
  676. self.__tester_output("====================================================", if_print)
  677. self.__tester_output(f"Receiver [1]:", if_print)
  678. self.__tester_output(
  679. f"all: {count_all * 8} bit, lose: {pkg_lose_bytes * 8} bit, error: {count_error * 8} bit", if_print)
  680. self.__tester_output(f"error_rate: {self.error_rate_percent} %", if_print)
  681. self.__tester_output(f"DBG: 发包={self.__sender.send_pkg_number} 收包={self.__receiver.receive_pkg}",
  682. if_print)
  683. self.__tester_output(f"speed: {self.speed_kbps} kbps", if_print)
  684. self.__tester_output(f"Receiver [2]:", if_print)
  685. self.__tester_output(
  686. f"all: {count_all2 * 8} bit, lose: {pkg_lose_bytes2 * 8} bit, error: {count_error2 * 8} bit", if_print)
  687. self.__tester_output(f"error_rate: {self.error_rate_percent2} %", if_print)
  688. self.__tester_output(f"DBG: 发包={self.__sender.send_pkg_number} 收包={self.__receiver2.receive_pkg}",
  689. if_print)
  690. self.__tester_output(f"speed: {self.speed_kbps2} kbps", if_print)
  691. self.__receiver.pull_up_signal_when_receiving = False
  692. time.sleep(self.__sender.sleep_sec_every_step)
  693. if self.__sender.send_pkg_number % 10 == 0:
  694. time.sleep(self.__sender.sleep_sec_every_ten_step)
  695. self.__receiver.keep_receive()
  696. self.__receiver2.keep_receive()
  697. self.__sender.send_once()
  698. self.__rev_timer_sec = time.time()
  699. elif time.time() - self.__rev_timer_sec >= self.__rev_timeout_sec:
  700. self.__tester_output("====================================================", if_print)
  701. self.__tester_output("[Serial Controller] Warning: Receiver timeout. A package lost.", if_print)
  702. self.update_error_rate(1)
  703. self.__tester_output(f"[Serial Controller] Warning: error_rate: {self.error_rate_percent} %", if_print)
  704. if self.__receiver2:
  705. self.update_error_rate(2)
  706. self.__tester_output(
  707. f"[Serial Controller] Warning: error_rate: {self.error_rate_percent2} % (Receiver2)", if_print)
  708. self.__receiver.pull_up_signal_when_receiving = False
  709. self.__receiver.keep_receive()
  710. if self.__receiver2:
  711. self.__receiver2.keep_receive()
  712. self.__sender.send_once()
  713. self.__rev_timer_sec = time.time()
  714. else:
  715. time.sleep(0.1)
  716. if __name__ == "__main__":
  717. main_tester: TestService = TestService()
  718. main_tester.run_as_main()