serial_device_controller.py 37 KB

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