ble.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import asyncio
  4. from bleak import BleakClient, BleakScanner
  5. from bleak.backends.characteristic import BleakGATTCharacteristic
  6. import chardet
  7. import time
  8. import json
  9. from inputimeout import inputimeout, TimeoutOccurred
  10. import base64
  11. from Crypto.Cipher import AES
  12. from Crypto.Util.Padding import pad, unpad
  13. import copy
  14. import os
  15. from message_base import MessageBase
  16. from websocket_server import WebServer
  17. import sys
  18. import atexit
  19. import signal
  20. import traceback
  21. import logging
  22. logger = logging.getLogger(__name__)
  23. logger.setLevel(logging.DEBUG)
  24. s_h = logging.StreamHandler(sys.stderr)
  25. # formatter = logging.Formatter('%(asctime)s.%(msecs)03d-%(name)s-%(filename)s-[line:%(lineno)d]'
  26. # '-%(levelname)s-[日志信息]: %(message)s',
  27. # datefmt='%Y-%m-%d,%H:%M:%S')
  28. formatter = logging.Formatter('%(asctime)s-[%(lineno)d]'
  29. '-%(levelname)s: %(message)s',
  30. datefmt='%d %H:%M:%S')
  31. s_h.setFormatter(formatter)
  32. logger.addHandler(s_h)
  33. config_file="config.json"
  34. #设备的Characteristic UUID
  35. par_notification_characteristic="0000ffe2-0000-1000-8000-00805f9b34fb"
  36. #设备的Characteristic UUID(具备写属性Write)
  37. par_write_characteristic="0000ffe1-0000-1000-8000-00805f9b34fb"
  38. #设备的MAC地址
  39. # par_device_addr="D1:1D:6A:52:CD:F8"
  40. par_device_addr="D5:2D:D4:9E:5C:3C"
  41. # 密钥(key), 密斯偏移量(iv) CBC模式加密
  42. EV_SOFT_BLE_SEND_CODE = '#MOIF@KHab%DECR$' #//蓝牙发送密钥
  43. EV_SOFT_BLE_RECV_CODE = '$*@AB%DHJqopENC#' #//蓝牙接收密钥
  44. # EV_SOFT_BLE_SEND_CODE = 'XH23456789ABCDEF' #//蓝牙发送密钥
  45. # EV_SOFT_BLE_RECV_CODE = 'XH23456789ABCDEF' #//蓝牙接收密钥
  46. g_config = {}
  47. g_ble_mtu = 20
  48. g_ble_client = None
  49. g_download_cfg = {"Key":"", "Total":0, "Number":0, "DataCrc16":0}
  50. mb = None
  51. ws = None
  52. def calculate_time(func):
  53. def wrapper(*args, **kwargs):
  54. start_time = time.time()
  55. result = func(*args, **kwargs)
  56. end_time = time.time()
  57. if end_time - start_time >= 0.5 :
  58. print("函数 %s 运行时间为 %.3f 秒" % (func.__name__, end_time - start_time))
  59. return result
  60. return wrapper
  61. @atexit.register
  62. def exit_handler():
  63. logger.info("异常退出")
  64. # 采用traceback模块查看异常,这个方法会打印出异常代码的行号
  65. exc_type, exc_value, exc_tb = sys.exc_info()
  66. logger.info(str(traceback.format_exception(exc_type, exc_value, exc_tb)))
  67. def sig_handler(signum, frame):
  68. logger.info('catched singal: %d' % signum)
  69. sys.exit(0)
  70. def shuncomdacode(data):
  71. """ 识别data的编码格式 """
  72. result = chardet.detect(data)
  73. print(result['encoding'])
  74. return (result['encoding'])
  75. def AES_Encrypt(aes_iv, aes_key, plain_text):
  76. """
  77. AES encrypt
  78. :param plain_text: bytes
  79. :param aes_key: bytes
  80. :param aes_iv: bytes
  81. :return: bytes
  82. """
  83. try:
  84. pad_data = pad(plain_text, AES.block_size)
  85. return AES.new(aes_key, AES.MODE_CBC, aes_iv).encrypt(pad_data)
  86. except Exception as e:
  87. logger.error("Err in {}[{}]:\n {}".format(os.path.basename(e.__traceback__.tb_frame.f_globals["__file__"]), e.__traceback__.tb_lineno, e))
  88. return bytes()
  89. def AES_Decrypt(aes_iv, aes_key, plain_text):
  90. """
  91. AES decrypt
  92. :param plain_text: bytes
  93. :param aes_key: bytes, aes_key
  94. :param aes_iv: bytes, aes_iv
  95. :return: bytes
  96. """
  97. try:
  98. dec_data = AES.new(aes_key, AES.MODE_CBC, aes_iv).decrypt(plain_text)
  99. return unpad(dec_data, AES.block_size)
  100. except Exception as e:
  101. logger.error("Err in {}[{}]:\n {}".format(os.path.basename(e.__traceback__.tb_frame.f_globals["__file__"]), e.__traceback__.tb_lineno, e))
  102. return bytes()
  103. def AES_Encrypt2(vi, key, data):
  104. enctext = bytes()
  105. try:
  106. # vi = '0102030405060708'
  107. pad = lambda s: s + (16 - len(s) % 16) * chr(16 - len(s) % 16)
  108. data = pad(data)
  109. # 字符串补位
  110. cipher = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8'))
  111. encryptedbytes = cipher.encrypt(data.encode('utf8'))
  112. # 加密后得到的是bytes类型的数据
  113. encodestrs = base64.b64encode(encryptedbytes)
  114. # 使用Base64进行编码,返回byte字符串
  115. enctext = encodestrs.decode('utf8')
  116. # 对byte字符串按utf-8进行解码
  117. except Exception as e:
  118. logger.error("Err in {}[{}]:\n {}".format(os.path.basename(e.__traceback__.tb_frame.f_globals["__file__"]), e.__traceback__.tb_lineno, e))
  119. return enctext
  120. def AES_Decrypt2(vi, key, data):
  121. text_decrypted = bytes()
  122. try:
  123. # vi = '0102030405060708'
  124. data = data.encode('utf8')
  125. encodebytes = base64.decodebytes(data)
  126. # 将加密数据转换位bytes类型数据
  127. cipher = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8'))
  128. text_decrypted = cipher.decrypt(encodebytes)
  129. unpad = lambda s: s[0:-s[-1]]
  130. text_decrypted = unpad(text_decrypted)
  131. # 去补位
  132. text_decrypted = text_decrypted.decode('utf8')
  133. except Exception as e:
  134. logger.error("Err in {}[{}]:\n {}".format(os.path.basename(e.__traceback__.tb_frame.f_globals["__file__"]), e.__traceback__.tb_lineno, e))
  135. return text_decrypted
  136. def sum_ccitt_16(data):
  137. total = sum(data)
  138. total &= 0xFFFF
  139. return total
  140. def crc_ccitt_16(data):
  141. crc = 0
  142. for byte in data:
  143. crc ^= (byte << 8)
  144. for _ in range(8):
  145. if crc & 0x8000:
  146. crc = (crc << 1) ^ 0x1021
  147. else:
  148. crc <<= 1
  149. crc &= 0xFFFF
  150. return crc
  151. def checknum_16(data):
  152. type = g_config["def_cfg"]["checknum_type"]
  153. if type == "sum16":
  154. return sum_ccitt_16(data)
  155. elif type == "crc16":
  156. return crc_ccitt_16(data)
  157. return 0
  158. def get_aes_key(type):
  159. if type == "send":
  160. if g_config["def_cfg"]["aes_cbc_key_send"]:
  161. return bytes(g_config["def_cfg"]["aes_cbc_key_send"], encoding='utf-8')
  162. else:
  163. return bytes(EV_SOFT_BLE_SEND_CODE, encoding='utf-8')
  164. else:
  165. if g_config["def_cfg"]["aes_cbc_key_recv"]:
  166. return bytes(g_config["def_cfg"]["aes_cbc_key_recv"], encoding='utf-8')
  167. else:
  168. return bytes(EV_SOFT_BLE_RECV_CODE, encoding='utf-8')
  169. # 初始化一个CRC16校验码计算函数,多项式为0x8005
  170. def ev_packing(data):
  171. data = bytes(data)
  172. # 加密
  173. if g_config["def_cfg"]["aes_cbc_enbable"]:
  174. logger.info("加密前:{}".format(bytes(data)))
  175. key = get_aes_key("send")
  176. data = AES_Encrypt(bytes(key), bytes(key), bytes(data))
  177. lenght = len(data)
  178. # crc16 = crc16_func(data) ## 计算数据的CRC-16校验码
  179. # checknum = crc16(data, 0, len(data))
  180. out = []
  181. # 头
  182. if g_config["def_cfg"]["head_send"] != "":
  183. HEAD_SEND = str(g_config["def_cfg"]["head_send"]).encode()
  184. out = list(HEAD_SEND)
  185. # 长度
  186. out = out + [ (lenght>>8)&0xFF, (lenght>>0)&0xFF]
  187. # 校验码
  188. if g_config["def_cfg"]["checknum_type"] != "":
  189. checknum = checknum_16(data)
  190. out = out + [ (checknum>>8)&0xFF, (checknum>>0)&0xFF]
  191. for d in bytes(data):
  192. out.append(d)
  193. return bytes(out)
  194. #监听回调函数,此处为打印消息
  195. # 记录数据
  196. recv_data = []
  197. recv_start_time = 0
  198. GetConfiguration = []
  199. def PACK_LEN():
  200. H = len(g_config["def_cfg"]["head_send"])
  201. L = 2
  202. C = 2 if g_config["def_cfg"]["checknum_type"]!="" else 0
  203. return H+L+C
  204. @calculate_time
  205. def recv_handler(data: bytearray, reve=False):
  206. global recv_start_time
  207. global g_download_cfg
  208. if g_config["def_cfg"]["recv_detail_print"]:
  209. logger.info("包接收:{}".format(bytes(data)))
  210. for d in data:
  211. recv_data.append(d)
  212. if len(recv_data)>1500:
  213. recv_data.clear()
  214. return
  215. if len(recv_data)<PACK_LEN():
  216. return
  217. recv_data_str = str(bytes(recv_data))
  218. HEAD_SEND = str(g_config["def_cfg"]["head_send"])
  219. HEAD_RECV = str(g_config["def_cfg"]["head_recv"])
  220. find_head = HEAD_SEND if not reve else HEAD_RECV
  221. index = -1
  222. if find_head != "":
  223. index = recv_data_str.find(find_head)#'EV>'
  224. if index < 2:
  225. return
  226. index -= 2
  227. data_len = (recv_data[index+4]&0xff) | (recv_data[index+3]&0xff)<<8
  228. if index+PACK_LEN()+data_len > len(recv_data):
  229. return
  230. if g_config["def_cfg"]["recv_soc_data_print"]:
  231. logger.info("接收:{}".format(bytes(recv_data).hex()))
  232. if recv_start_time:
  233. recv_end_time = time.time()
  234. logger.info('接收耗时:%.3fs', recv_end_time - recv_start_time)
  235. recv_start_time = 0
  236. soc_data = recv_data[index+PACK_LEN():index+PACK_LEN()+data_len]
  237. if g_config["def_cfg"]["checknum_type"]!="":
  238. H = len(g_config["def_cfg"]["head_send"])
  239. L = 2
  240. get_crc = (recv_data[index+(H+L+1)]&0xff)| (recv_data[index+(H+L)]&0xff)<<8
  241. check_num = checknum_16(soc_data)
  242. if get_crc != check_num:
  243. logger.info("校验失败(%d):0x%04X 0x%04X", len(recv_data), get_crc, check_num)
  244. return
  245. logger.info("校验成功")
  246. if g_config["def_cfg"]["aes_cbc_enbable"]:
  247. find_type= "recv" if not reve else 'send'
  248. key = get_aes_key(find_type) #"recv"
  249. dec_data = AES_Decrypt(bytes(key), bytes(key), bytes(soc_data))
  250. logger.info("解密后:{}".format(bytes(dec_data)))
  251. else:
  252. dec_data = soc_data
  253. # 清理
  254. recv_data.clear()
  255. def notification_handler(characteristic: BleakGATTCharacteristic, data: bytearray):
  256. global mq
  257. recv_handler(data)
  258. mq.add("ws_send", data)
  259. async def ble_send(client, data):
  260. global recv_start_time
  261. global g_ble_mtu
  262. logger.info('发送:{}'.format(bytes(data).hex()))
  263. frame_len = g_ble_mtu #244 #20 #244
  264. if g_config["def_cfg"]["ble_mtu"] > 0:
  265. frame_len = g_config["def_cfg"]["ble_mtu"]
  266. all_count = len(data)
  267. send_count = 0
  268. send_start_time = time.time()
  269. try:
  270. while send_count<all_count:
  271. cur_len = frame_len
  272. if all_count-send_count<frame_len:
  273. cur_len = all_count-send_count
  274. s = data[send_count:send_count+cur_len]
  275. await client.write_gatt_char(g_config["def_cfg"]["write_char"], s)
  276. send_count += cur_len
  277. except Exception as e:
  278. logger.error("Err in {}[{}]:\n {}".format(os.path.basename(e.__traceback__.tb_frame.f_globals["__file__"]), e.__traceback__.tb_lineno, e))
  279. send_end_time = time.time()
  280. recv_start_time = time.time()
  281. logger.info('发送耗时:%.3fs', send_end_time - send_start_time)
  282. await asyncio.sleep(g_config["def_cfg"]["ble_send_wait"]) #每休眠1秒发送一次
  283. import sys, select
  284. def timeoutable_input(clue="",timeout=None):
  285. print(clue,end="")
  286. i, o, e = select.select([sys.stdin], [], [], timeout)
  287. return sys.stdin.readline() if len(i)>0 else None
  288. def print_data_list():
  289. global g_config
  290. try:
  291. for i in range(len(g_config["cmd_list"])):
  292. print(i, g_config["cmd_list"][i][2])
  293. except Exception as e:
  294. logger.error("Err in {}[{}]:\n {}".format(os.path.basename(e.__traceback__.tb_frame.f_globals["__file__"]), e.__traceback__.tb_lineno, e))
  295. @calculate_time
  296. async def send_form_data_list(client, i, data=None):
  297. global g_config
  298. if data:
  299. json_data = data
  300. else:
  301. if i >= len(g_config["cmd_list"]):
  302. return None
  303. json_data = g_config["cmd_list"][i]
  304. if len(json_data) < 3:
  305. return
  306. byte_sequence = b''
  307. if len(json_data[0]) > 4:
  308. if json_data[0][:4] == "hex:":
  309. byte_sequence += bytes.fromhex(json_data[0][4:])
  310. elif json_data[0][:4] == "str:":
  311. byte_sequence += bytes(json_data[0][4:], 'utf-8')
  312. elif json_data[0][:4] == "bin:":
  313. byte_value = int(json_data[0][4:], 2) # 将二进制字符串转换为整数
  314. byte_array = bytes([byte_value]) # 将整数转换为单字节的字节串
  315. byte_sequence += byte_array
  316. if len(json_data[1]) > 4:
  317. if json_data[1][:4] == "hex:":
  318. byte_sequence += bytes.fromhex(json_data[1][4:])
  319. elif json_data[1][:4] == "str:":
  320. byte_sequence += bytes(json_data[1][4:], 'utf-8')
  321. elif json_data[1][:4] == "bin:":
  322. byte_value = int(json_data[1][4:], 2) # 将二进制字符串转换为整数
  323. byte_array = bytes([byte_value]) # 将整数转换为单字节的字节串
  324. byte_sequence += byte_array
  325. # data_str = json.dumps(json_data)
  326. # 字符串编码为字节序列
  327. # byte_sequence = str.encode(data_str)
  328. # 字节序列转换为bytearray类型
  329. byte_array = bytearray(byte_sequence)
  330. byte_array = ev_packing(byte_array)
  331. send_data = bytes(byte_array)
  332. await ble_send(client, send_data)
  333. async def task():
  334. global g_config
  335. await asyncio.sleep(3)
  336. while True:
  337. await asyncio.sleep(1)
  338. @calculate_time
  339. def read_config_call():
  340. global g_config
  341. # 读取更新json
  342. with open(config_file, "r", encoding="utf-8") as f:
  343. send_list_new = json.load(f)
  344. if g_config != send_list_new:
  345. g_config = send_list_new
  346. logger.info("json内容改变,内容如下:")
  347. print_data_list()
  348. @calculate_time
  349. async def heart_beat_call(client):
  350. global g_config
  351. # 心跳发送:维持蓝牙通讯
  352. logger.info("执行心跳发送任务..")
  353. await send_form_data_list(client, g_config["def_cfg"]["heart_beat_sel"])
  354. @calculate_time
  355. async def input_call(client):
  356. global g_config
  357. # 输入
  358. userinput = None
  359. if g_download_cfg["Number"] == 0: #非批量获取配置的状态下
  360. try:
  361. userinput = inputimeout(prompt='请命令序号:', timeout=g_config["def_cfg"]["input_interval"])
  362. except TimeoutOccurred:
  363. userinput = None
  364. if userinput:
  365. # 判断类型: 数字int 字符串str 列表list 元组tuple 字典dict
  366. # isinstance(userinput, str)
  367. if not userinput.isalpha():
  368. await send_form_data_list(client, int(userinput))
  369. elif str(userinput)=='l':
  370. # 读取更新json
  371. with open(config_file, "r", encoding="utf-8") as f:
  372. send_list_new = json.load(f)
  373. logger.info("json内容如下:")
  374. print_data_list()
  375. @calculate_time
  376. async def auto_getcfg_call(client):
  377. global g_config
  378. global g_download_cfg
  379. # 自动发送任务
  380. try:
  381. if g_download_cfg["Number"]>0:
  382. new_one = []
  383. for i, v in enumerate(g_config["cmd_list"]):
  384. if v[2] == "GetConfigurationNumber":
  385. new_one = copy.deepcopy(v) #深拷贝
  386. break
  387. if new_one:
  388. Number = g_download_cfg["Number"]
  389. Total = g_download_cfg["Total"]
  390. new_one[3]["Number"] = Number
  391. await send_form_data_list(client, 0, new_one)
  392. g_download_cfg["Number"] = Number+1 if Number < Total else 0
  393. except Exception as e:
  394. logger.error("Err in {}[{}]:\n {}".format(os.path.basename(e.__traceback__.tb_frame.f_globals["__file__"]), e.__traceback__.tb_lineno, e))
  395. async def main():
  396. global g_ble_client
  397. global g_config
  398. global g_ble_mtu
  399. global mq
  400. logger.info("读取配置...")
  401. with open(config_file, "r", encoding="utf-8") as f:
  402. g_config = json.load(f)
  403. print_data_list()
  404. logger.info("快速显示列表, 可输入l")
  405. while True:
  406. logger.info("开始扫描...")
  407. #基于MAC地址查找设备
  408. device = await BleakScanner.find_device_by_address(
  409. g_config["def_cfg"]["ble_mac"], cb=dict(use_bdaddr=False) #use_bdaddr判断是否是MOC系统
  410. )
  411. if device is None:
  412. logger.error("无法找到设备({})".format(g_config["def_cfg"]["ble_mac"]))
  413. continue
  414. #事件定义
  415. disconnected_event = asyncio.Event()
  416. #断开连接事件回调
  417. def disconnected_callback(client):
  418. global g_ble_client
  419. logger.info("断开回调!")
  420. disconnected_event.set()
  421. g_ble_client = None
  422. logger.info("尝试连接设备({})...".format(g_config["def_cfg"]["ble_mac"]))
  423. async with BleakClient(device,disconnected_callback=disconnected_callback) as client:
  424. g_ble_client = client
  425. logger.info("已连接(mtu=%d)", client.mtu_size)
  426. g_ble_mtu = client.mtu_size-3
  427. try:
  428. await client.start_notify(g_config["def_cfg"]["notif_char"], notification_handler)
  429. except Exception as e:
  430. logger.error("Err in {}[{}]:\n {}".format(os.path.basename(e.__traceback__.tb_frame.f_globals["__file__"]), e.__traceback__.tb_lineno, e))
  431. g_download_cfg["Number"] = 0
  432. read_config_time = 0
  433. heart_beart_time = 0
  434. input_wait_time = 0
  435. while client and client.is_connected:
  436. if time.time()-read_config_time >= 3:
  437. read_config_time = time.time()
  438. read_config_call()
  439. if g_config["def_cfg"]["auto_send_GetConfigurationNumber"] and g_download_cfg["Number"]>0:
  440. await auto_getcfg_call(client)
  441. elif g_config["def_cfg"]["heart_beat_interval"] >0 and time.time()-heart_beart_time>=g_config["def_cfg"]["heart_beat_interval"]:
  442. heart_beart_time = time.time()
  443. await heart_beat_call(client)
  444. elif g_config["def_cfg"]["input_interval"]>0 and time.time()-input_wait_time>=g_config["def_cfg"]["input_interval"]:
  445. input_wait_time = time.time()
  446. await input_call(client)
  447. try:
  448. # 开始根据设备即功能处理消息
  449. ws_data = mq.get("ws_recv")
  450. if ws_data:
  451. await ble_send(client, ws_data)
  452. except Exception as err:
  453. pass
  454. time.sleep(0.1)
  455. def test_aes():
  456. key = EV_SOFT_BLE_SEND_CODE
  457. data = b'[2, "123456789012", "KeepAlive", {}]' #需要加密的内容
  458. AES_Encrypt(key, key, data)
  459. enctext = AES_Encrypt(key, key, data)
  460. print(enctext)
  461. text_decrypted = AES_Decrypt(key, key, enctext)
  462. print(text_decrypted)
  463. import array,struct
  464. def test_base64():
  465. in_data = b'1234567890\0_+,.//abcdefg'
  466. print(len(in_data), in_data)
  467. base64_bytes = base64.b64encode(in_data)
  468. print(len(base64_bytes),base64_bytes)
  469. out = base64.b64decode(base64_bytes)
  470. print(len(out),out)
  471. def test_recv():
  472. with open(config_file, "r", encoding="utf-8") as f:
  473. g_config = json.load(f)
  474. print_data_list()
  475. str_a = '45 56 3C 00 40 EB AE E3 1F 7B FA 9A 61 86 22 BE 36 0D 0D 09 FE 12 C6 60 27 A8 74 2A 32 08 0D D1 54 B4 4C E1 69 10 4B FC DD 00 23 58 14 74 C1 3F 64 48 9C 88 12 41 17 59 6B 82 F7 90 E7 89 A1 71 80 5A 07 FF 4D 4D 99'
  476. recv_handler(bytes.fromhex(str_a), True)
  477. str_b = '45 56 3C 00 40 B2 E6 73 BD E7 EE B8 1E D4 DE B7 BF 64 19 80 95 24 94 D2 F3 4E 45 97 99 A3 B5 F7 29 E0 86 1A 60 07 96 77 C4 73 DC C0 4B 2B FB 7E 73 D2 A1 CE 17 26 CF 12 FF 06 DC 83 B7 5E 65 C9 18 0B 87 16 EB 75 0F'
  478. recv_handler(bytes.fromhex(str_b), True)
  479. def run(tasks = []):
  480. # tasks.append(task())
  481. loop = asyncio.get_event_loop()
  482. loop.run_until_complete(asyncio.wait(tasks))
  483. loop.close()
  484. #---------------------------------------------------------
  485. if __name__ == "__main__":
  486. signal.signal(signal.SIGTERM, sig_handler) # kill pid
  487. signal.signal(signal.SIGINT, sig_handler) # ctrl -c
  488. # test_aes()
  489. # test_base64()
  490. # test_recv()
  491. mq = MessageBase()
  492. ws = WebServer("0.0.0.0", 11100, mq)
  493. ws.run()
  494. run([main(),])
  495. sys.exit(0)