Browse Source

Merge branch 'dev-python' of gitlab.pubdata.cn:liangyibo/CameraMachine into dev-python

zhangyh 9 months ago
parent
commit
142049e05d

+ 86 - 0
python/mcu/BaseClass.py

@@ -0,0 +1,86 @@
+import asyncio
+from sockets import ConnectionManager
+class BaseClass:
+
+    def __init__(self, websocket_manager: ConnectionManager):
+        self.websocket_manager = websocket_manager
+
+    def sendSocketMessage(self, code=0, msg="", data=None):
+        data = {"code": code, "msg": msg, "data": data}
+        loop = asyncio.get_event_loop()
+        loop.create_task(self.websocket_manager.broadcast(data))
+    def change_hex_to_int(self,_bytearray):
+        return ' '.join([hex(x) for x in _bytearray])
+
+    def read_cmd(self, serial_handle, check=None):
+        n = 0
+        while 1:
+            try:
+                read_d = serial_handle.read_all()  # 读取接收到的数据
+                self.receive_data += read_d
+            except BaseException as e:
+                print("171串口接收报错", e)
+                self.serial_handle = None
+                return False
+
+            if len(self.receive_data) < 4:
+                break
+
+            if self.receive_data[0] == 0x55 and self.receive_data[1] == 0x55:
+                # print("read ori ", self.change_hex_to_int(self.receive_data))
+                data_len = self.receive_data[2]
+                if len(self.receive_data) < data_len + 4:
+                    # 此处需要超时机制
+                    # print("数据长度不够,等待下次读取")
+                    # 超时退出
+                    # if not self.serial_handle.txdone():
+                    #     return None
+                    # n += 1
+                    # if n > out_time_n:
+                    #     return None
+                    # time.sleep(0.01)
+                    continue
+                _data = self.receive_data[3 : data_len + 4]
+                # 更新缓存区
+                self.receive_data = self.receive_data[data_len + 4 :]
+                # 校验数据
+                if 0xFF & ~sum(_data[:-1]) == _data[-1]:
+                    # print("receive_data:", self.change_hex_to_int(self.receive_data[:-1]))
+                    return _data[:-1]
+                else:
+                    return None
+            else:
+                # print("起始位不是 55 55 进行移除", self.receive_data[0])
+                # 起始位不是 55 55 进行移除
+                while self.receive_data:
+                    if len(self.receive_data) == 1:
+                        if self.receive_data[0] == 0x55:
+                            break
+                        else:
+                            self.receive_data = b""
+                    else:
+                        if (
+                            self.receive_data[0] == 0x55
+                            and self.receive_data[1] == 0x55
+                        ):
+                            break
+                        else:
+                            self.receive_data = self.receive_data[1:]
+
+    def write_cmd(self, serial_handle,data: list):
+        if serial_handle:
+            # data = [(0xff & par1), (0xff & (par1 >> 8))]
+            # self.clearn_flush()
+            buf = bytearray(b"")
+            buf.extend([0x55, 0x55, (0xFF & len(data))])
+            buf.extend(data)
+            buf.extend([0xFF & ~sum(data)])
+            # 55 55 02 5a 01 a4
+            # print("send buf  {}".format(self.change_hex_to_int(buf)))
+            try:
+                serial_handle.write(buf)
+                return True
+            except:
+                serial_handle = None
+                _recv_data = b""
+                return False

+ 414 - 0
python/mcu/BlueToothMode.py

@@ -0,0 +1,414 @@
+import asyncio
+import time
+from bleak import BleakScanner, BleakClient
+import threading
+from collections import deque
+from utils.SingletonType import SingletonType
+from .RemoteControlV2 import RemoteControlV2
+from .BaseClass import BaseClass
+from sockets.connect_manager import ConnectionManager
+
+class BlueToothMode(BaseClass,metaclass=SingletonType):
+    instance = None
+    init_flag = None
+
+    def __init__(self, websocket_manager: ConnectionManager):
+        super().__init__(websocket_manager)
+        """此处设计为,如果已经存在实例时,不再执行初始化"""
+        if self.init_flag:
+            return
+        else:
+            self.init_flag = True
+        self.remote_control_v2 = RemoteControlV2(self, websocket_manager)
+        # 用于存储找到的目标设备的地址
+        self.target_device_address = None
+
+        self._lock = threading.Lock()
+        self.connect_state = False
+        self.receive_data = b""
+        self.devices = {}
+        self.devices_name = {}
+        self.last_value = None
+        self.retry_num = 0
+        self.last_error = ""
+
+    def __new__(cls, *args, **kwargs):
+        """如果当前没有实例时,调用父类__new__方法,生成示例,有则返回保存的内存地址。"""
+        if not cls.instance:
+            cls.instance = super().__new__(cls)
+        return cls.instance
+
+    def print_error(self, text, *args):
+        if text != self.last_error:
+            self.last_error = text
+            print(text)
+
+    async def scan_for_esp32(self):
+        """扫描附近的BLE设备,并寻找ESP32"""
+        print("Scanning for ESP32 devices...*****************")
+        try:
+            devices = await BleakScanner.discover()
+        except BaseException as e:
+            self.print_error("蓝牙疑似未打开,{}".format(e))
+            return
+        for d in devices:
+            # print(f"Device: {d.name} - Address: {d.address}")
+            # 假设ESP32的广播名称包含"ESP32-S3"字符串
+            if d.name:
+                if "ESP32-S3" in d.name:
+                    if d.address not in self.devices:
+                        self.devices_name[d.name] = d.address
+                        self.devices[d.address] = {
+                            "name": d.name,
+                            "client": None,
+                            "send_queue": deque(maxlen=20),
+                            "recv_queue": deque(maxlen=20),
+                            "receive_data": b"",
+                            "connect_state": True,
+                        }
+                        self.print_error(
+                            f"Found device: {d.name} - Address: {d.address}"
+                        )
+                        asyncio.create_task(self.connect_and_listen(d.address))
+                        self.connect_state = True
+
+    # 注册到遥控器等上位机
+    def connect_device(self, address, name):
+        print("注册到遥控器等上位机", address, name)
+        if "remote" in name:
+            self.remote_control_v2.to_connect_bluetooth(address, is_test=False)
+
+    # 关闭连接
+    def disconnect_device(self, address, name):
+        print("关闭蓝牙连接", address, name)
+        if "remote" in name:
+            self.print_error("71  关闭蓝牙连接{}-{}".format(address, name))
+            self.remote_control_v2.close_bluetooth_connect()
+
+    async def handle_disconnect(self, client):
+        """处理断开连接事件"""
+        self.print_error("Device was disconnected.")
+        # 尝试重新连接
+        while not client.is_connected:
+            try:
+                self.print_error("Attempting to reconnect...")
+                await client.connect()
+            except Exception as e:
+                self.print_error(f"Failed to reconnect: {e}")
+                await asyncio.sleep(5)  # 等待一段时间再重试
+
+    async def notification_handler(self, address, characteristic, data: bytearray):
+        """处理接收到的通知数据"""
+        with self._lock:
+            #
+            # if data == bytearray(b'UU\x01c\x9c'):
+            #     self.devices[address]['send_queue'].append(bytearray(b'UU\x01c\x9c'))
+            #     pass
+            # else:
+            #     # print("60 data:", address, data)
+            self.devices[address]["recv_queue"].append(data)
+            self.devices[address]["connect_state"] = True
+
+    async def write_characteristic(self, client, characteristic_uuid, data):
+        """向指定特征写入数据"""
+        try:
+            await client.write_gatt_char(characteristic_uuid, data)
+        except BaseException as e:
+            self.print_error("write_characteristic error ", e)
+
+    async def connect_and_listen(self, address):
+        """连接到指定地址的ESP32设备并监听通知"""
+        self.print_error("""连接到指定地址的ESP32设备并监听通知""")
+        while True:
+            # try:
+                async with BleakClient(address) as client:
+                    if not client.is_connected:
+                        self.print_error("Failed to connect to the device.")
+                        self.devices[address]["connect_state"] = False
+                        self.disconnect_device(
+                            address=address, name=self.devices[address]["name"]
+                        )
+                        continue
+                    self.devices[address]["connect_state"] = True
+                    self.print_error(f"Connected to {address}")
+                    # 获取服务和特征(假设你已经知道要监听的特征UUID)
+                    services = await client.get_services()
+                    for service in services:
+                        for char in service.characteristics:
+                            if "notify" in char.properties:
+                                self.print_error(
+                                    f"Subscribing to characteristic: {char.uuid}"
+                                )
+                                await client.start_notify(
+                                    char,
+                                    lambda char, data: asyncio.create_task(
+                                        self.notification_handler(address, char, data)
+                                    ),
+                                )
+
+                    # 进入一个简单的循环,保持连接
+                    self.print_error(
+                        "进入一个简单的循环  保持连接", self.devices[address]["name"]
+                    )
+                    self.connect_device(
+                        address=address, name=self.devices[address]["name"]
+                    )
+                    self.retry_num += 1
+                    while True:
+                        if not client.is_connected:
+                            self.print_error(
+                                f"Device {address} disconnected unexpectedly."
+                            )
+                            with self._lock:
+                                self.disconnect_device(
+                                    address=address, name=self.devices[address]["name"]
+                                )
+                                self.devices[address]["connect_state"] = False
+                            break
+
+                        if self.devices[address]["send_queue"]:
+                            with self._lock:
+                                send_data = self.devices[address][
+                                    "send_queue"
+                                ].popleft()
+                                # print("-----------> send_data:", self.change_hex_to_10_int(send_data))
+                            await self.write_characteristic(
+                                client, char.uuid, send_data
+                            )
+                            await asyncio.sleep(0.01)
+
+                        await asyncio.sleep(0.02)
+            # except Exception as e:
+            #     with self._lock:
+            #         self.disconnect_device(
+            #             address=address, name=self.devices[address]["name"]
+            #         )
+            #         self.devices[address]["connect_state"] = False
+            #     print(f"Error during connection or listening: {e}")
+            #     await asyncio.sleep(2)  # 发生错误时等待一段时间再重试
+
+    async def main_func(self):
+        """主函数"""
+        # address = "24:EC:4A:26:4B:BE"
+        # _name = "ESP32-S3-remote"
+        # self.devices[address] = {'name': _name,
+        #                          'client': None,
+        #                          'send_queue': [],
+        #                          'recv_queue': [],
+        #                          "receive_data": b"",
+        #                          "connect_state": True,
+        #                          }
+        # self.devices_name[_name] = address
+        # asyncio.create_task(self.connect_and_listen(address))
+        await self.scan_for_esp32()
+        # 定期重新扫描以发现新设备
+        while True:
+            if self.devices:
+                await asyncio.sleep(20)
+            else:
+                await asyncio.sleep(3)
+            await self.scan_for_esp32()
+
+    def run(self):
+        self.print_error("开启蓝牙扫描")
+        asyncio.run(self.main_func())
+
+    def write_cmd(self, address, data: list):
+        buf = []
+        buf.extend([0x55, 0x55, (0xFF & len(data))])
+        buf.extend(data)
+        buf.extend([0xFF & ~sum(data)])
+        self.send_data(address, bytes(buf))
+
+    def send_data(self, address, byte_list: bytes):
+        chunk_size = 20
+        chunks = [
+            byte_list[i : i + chunk_size] for i in range(0, len(byte_list), chunk_size)
+        ]
+        try:
+            with self._lock:
+                for chunk in chunks:
+                    self.devices[address]["send_queue"].append(chunk)
+        except Exception as e:
+            self.devices[address]["connect_state"] = False
+            self.print_error(f"Error sending notification: {address},{e}")
+
+    def change_hex_to_int(self, _bytearray):
+        return " ".join([hex(x)[2:].zfill(2) for x in _bytearray])
+
+    def change_hex_to_10_int(self, _bytearray):
+        return " ".join([str(int(x)) for x in _bytearray])
+
+    def read_cmd(self, time_out=10):
+        data_list = []
+        for device_name in self.devices_name:
+            address = self.devices_name[device_name]
+            # print(self.devices[address]['connect_state'])
+            while self.devices[address]["recv_queue"]:
+                receive_data = self.read_cmd_one(address)
+                if receive_data:
+                    data_list.append(
+                        {
+                            "device_name": device_name,
+                            "address": address,
+                            "receive_data": receive_data,
+                        }
+                    )
+        return data_list
+
+    def read_cmd_one(self, address):
+        # 获取所有缓冲区的字节
+        if not self.devices[address]["connect_state"]:
+            return
+        # print("读取数据....")
+        while 1:
+            receive_data = self.devices[address]["receive_data"]
+            # print("166 receive_data", receive_data)
+            if self.devices[address]["recv_queue"]:
+                with self._lock:
+                    read_d = self.devices[address]["recv_queue"].popleft()
+                    # print("170 read_d", read_d)
+                    receive_data += read_d
+                    self.devices[address]["receive_data"] = receive_data
+
+            if not receive_data:
+                return None
+
+            if len(receive_data) < 4:
+                break
+            # print("--------receive_data",self.change_hex_to_10_int(receive_data))
+            if receive_data[0] == 0x55 and receive_data[1] == 0x55:
+                data_len = receive_data[2]
+                if len(receive_data) < data_len + 4:
+                    # n += 1
+                    # if n > time_out:
+                    #     time.sleep(0.001)
+                    #     return False
+                    # continue
+                    return
+
+                _data = receive_data[3 : data_len + 4]
+                # 更新缓存区
+                with self._lock:
+                    self.devices[address]["receive_data"] = receive_data[data_len + 4 :]
+                    receive_data = receive_data[data_len + 4 :]
+                # 校验数据
+                if 0xFF & ~sum(_data[:-1]) == _data[-1]:
+                    return _data[:-1]
+                else:
+                    print("数据异常,丢弃")
+                    return False
+            else:
+                # 起始位不是 55 55 进行移除
+                while receive_data:
+                    if len(receive_data) == 1:
+                        if receive_data[0] == 0x55:
+                            break
+                        else:
+                            with self._lock:
+                                self.devices[address]["receive_data"] = b""
+                                receive_data = b""
+                    else:
+                        if receive_data[0] == 0x55 and receive_data[1] == 0x55:
+                            break
+                        else:
+                            with self._lock:
+                                self.devices[address]["receive_data"] = receive_data[1:]
+                            receive_data = receive_data[1:]
+
+    def analysis_received_data(self):
+        receive_data = self.read_cmd(time_out=5)
+        if not receive_data:
+            return
+        else:
+            # print("receive_data:", receive_data)
+            # print("read2 receive_data {}".format(self.change_hex_to_int(receive_data)))
+            pass
+        for data in receive_data:
+            address = data["address"]
+            rec_data = data["receive_data"]
+            # print("read address {}".format(address))
+            # print("read receive_data {}".format(self.change_hex_to_int(rec_data)))
+            # 数据 结构 command,按命令解析
+
+            if rec_data[0] == 1:
+                # 扫码数据
+                bar_code = rec_data[1:].decode()
+                bar_code = bar_code.replace("\r", "")
+                bar_code = bar_code.replace("\n", "")
+                print("bar_code:", bar_code)
+                return
+            if rec_data[0] == 2:
+                print("read receive_data {}".format(self.change_hex_to_int(rec_data)))
+                return
+            if rec_data[0] == 90:
+                print(
+                    "read receive_data-90 {}".format(self.change_hex_to_int(rec_data))
+                )
+                return
+
+            if rec_data[0] == 99:
+                blue_mode.write_cmd(address, [99])
+                # print("发送心跳包")
+                return
+            if rec_data[0] == 111:
+                print(
+                    "{} receive_data-111 {}".format(
+                        self.retry_num, self.change_hex_to_10_int(rec_data)
+                    )
+                )
+                value = (
+                    rec_data[1] << 24
+                    | rec_data[2] << 16
+                    | rec_data[3] << 8
+                    | rec_data[4]
+                )
+                if self.last_value is None:
+                    self.last_value = value
+                else:
+                    self.last_value += 1
+                if self.last_value == value:
+                    flag = True
+                else:
+                    flag = False
+                print(
+                    "{}  value:{},last_value;{},flag:{}\n".format(
+                        self.retry_num, value, self.last_value, flag
+                    )
+                )
+
+
+if __name__ == "__main__":
+    blue_mode = BlueToothMode(None)
+    threading.Thread(target=blue_mode.run, args=()).start()
+    print("=" * 50)
+    n = 0
+    time.sleep(1)
+    k = 0
+    last_t = time.time()
+    while 1:
+        time.sleep(0.001)
+        s = time.time()
+        address = "24:EC:4A:26:4B:BE"
+        blue_mode.analysis_received_data()
+
+        n += 1
+        if n == 500:
+            print(s - last_t)
+            for i in range(3):
+                k += 1
+                data = [
+                    111,
+                    0xFF & k >> 24,
+                    0xFF & k >> 16,
+                    0xFF & k >> 8,
+                    0xFF & k,
+                ]
+                data.extend([x for x in range(100, 130)])
+                blue_mode.write_cmd(address=address, data=data)
+                print("send_value:{}".format(k))
+
+            data = [90]
+            blue_mode.write_cmd(address=address, data=data)
+            n = 0
+        last_t = s

+ 137 - 115
python/mcu/DeviceControl.py

@@ -1,13 +1,22 @@
 import asyncio
 import serial.tools.list_ports
-import time
+import time, json
 from .SerialIns import SerialIns
-from sockets import socket_manager
+from utils.SingletonType import SingletonType
+from .BaseClass import BaseClass
+from sockets import ConnectionManager
+
 # mcu命令
-class DeviceControl:
-    def __init__(self):
+class DeviceControl(BaseClass,metaclass=SingletonType):
+
+    def __init__(self, websocket_manager: ConnectionManager):
+        super().__init__(websocket_manager)
         self.serial_ins = None
         self.connected_ports_dict = {}  # 已连接的ports
+        self.p_list = []
+        self.temp_ports_dict = {}
+        self.is_running = False
+
     def scan_serial_port(self) -> dict:
         # 获取所有可用串口列表
         ports_dict = {}
@@ -27,20 +36,16 @@ class DeviceControl:
         if len(ports_dict) <= 0:
             return {}
         return ports_dict
-
     def remove_port(self, port_name):
-        '''移除串口'''
+        """移除串口"""
         print("remove", port_name)
-        self.sign_data.emit(
-            {
-                "_type": "remove_port",
-                "plugins_mode": "auto_select_com",
-                "data": {"port_name": port_name},
-            }
-        )
-    def sendMsg(self,code=0,msg="",data=None):
-        asyncio.run(socket_manager.send_message(code=code, msg=msg, data=data))
-        # print(code,msg,data)
+        data = {
+            "_type": "remove_port",
+            "plugins_mode": "auto_select_com",
+            "data": {"port_name": port_name},
+        }
+        self.sendSocketMessage(1, "串口被移除", data)
+
     def add_port_by_linkage(self, port_name):
         # port_value :串口基础信息
         # todo 根据prot_value 信息自动进行连接
@@ -52,7 +57,7 @@ class DeviceControl:
             "data": {"text": "开始识别接口:{}".format(port_name)},
         }
 
-        self.sendMsg(msg="开始识别接口:{}".format(port_name), data=message_data)
+        self.sendSocketMessage(msg="开始识别接口:{}".format(port_name), data=message_data)
         time.sleep(1)
         """
         步骤:
@@ -60,16 +65,15 @@ class DeviceControl:
         """
         try:
             # 尝试使用115200波特率链接
-            serial_handle =  serial.Serial(
-                port=port_name, baudrate=115200, timeout=0.5
-            )
+            serial_handle = serial.Serial(port=port_name, baudrate=115200, timeout=0.5)
         except:
             message_data = {
                 "_type": "show_info",
                 "plugins_mode": "auto_select_com",
                 "data": {"text": "串口:{} 被占用,或无法识别".format(port_name)},
             }
-            self.sendMsg(
+            self.sendSocketMessage(
+                1,
                 msg="串口:{} 被占用,或无法识别".format(port_name).format(port_name),
                 data=message_data,
             )
@@ -83,6 +87,11 @@ class DeviceControl:
             serial_handle.flushInput()  # 尝试重置输入缓冲区
         except serial.SerialTimeoutException:
             print("超时错误:无法在规定时间内重置输入缓冲区。")
+            self.sendSocketMessage(
+                1,
+                msg="超时错误:无法在规定时间内重置输入缓冲区。",
+                data=None,
+            )
             serial_handle.close()
             return
         print("尝试写入数据")
@@ -121,7 +130,7 @@ class DeviceControl:
                     "plugins_mode": "auto_select_com",
                     "data": {"text": "MCU开始连接"},
                 }
-                self.sendMsg(
+                self.sendSocketMessage(
                     msg="MCU开始连接",
                     data=message_data,
                 )
@@ -134,7 +143,7 @@ class DeviceControl:
                     "port_name": port_name,
                 },
             }
-            self.sendMsg(
+            self.sendSocketMessage(
                 msg="MCU连接成功",
                 data=message_data,
             )
@@ -147,83 +156,104 @@ class DeviceControl:
         # 不正常进行尝试连接
         # 连接不上,记录为其他列表
 
-    def read_cmd(self, serial_handle, check=None):
-        n = 0
-        while 1:
-            try:
-                read_d = serial_handle.read_all()  # 读取接收到的数据
-                self.receive_data += read_d
-            except BaseException as e:
-                print("171串口接收报错", e)
-                self.serial_handle = None
-                return False
-
-            if len(self.receive_data) < 4:
-                break
-
-            if self.receive_data[0] == 0x55 and self.receive_data[1] == 0x55:
-                # print("read ori ", self.change_hex_to_int(self.receive_data))
-                data_len = self.receive_data[2]
-                if len(self.receive_data) < data_len + 4:
-                    # 此处需要超时机制
-                    # print("数据长度不够,等待下次读取")
-                    # 超时退出
-                    # if not self.serial_handle.txdone():
-                    #     return None
-                    # n += 1
-                    # if n > out_time_n:
-                    #     return None
-                    # time.sleep(0.01)
-                    continue
-                _data = self.receive_data[3 : data_len + 4]
-                # 更新缓存区
-                self.receive_data = self.receive_data[data_len + 4 :]
-                # 校验数据
-                if 0xFF & ~sum(_data[:-1]) == _data[-1]:
-                    # print("receive_data:", self.change_hex_to_int(self.receive_data[:-1]))
-                    return _data[:-1]
-                else:
-                    return None
-            else:
-                # print("起始位不是 55 55 进行移除", self.receive_data[0])
-                # 起始位不是 55 55 进行移除
-                while self.receive_data:
-                    if len(self.receive_data) == 1:
-                        if self.receive_data[0] == 0x55:
-                            break
-                        else:
-                            self.receive_data = b""
-                    else:
-                        if (
-                            self.receive_data[0] == 0x55
-                            and self.receive_data[1] == 0x55
-                        ):
-                            break
-                        else:
-                            self.receive_data = self.receive_data[1:]
 
     def to_connect_com(self, port_name):
+        # 关闭串口
+        print("to_connect_com", port_name)
         self.close_connect()
-        time.sleep(0.5)
+        time.sleep(0.3)
+        self.connect_state = False
         try:
-            self.serial_ins = SerialIns(port_name=port_name, baud=115200)
-            # self.serial_ins = SerialIns(port_name=port_name, baud=9600)
-            if self.serial_ins.serial_handle:
-                self.sendMsg(msg="条码识别 打开串口成功")
-                self.connect_state = True
-                self.start()
-                return True
-            else:
-                self.sendMsg(code=1, msg="条码识别 打开串口失败")
+            self.serial_ins = SerialIns(port_name=port_name, baud=115200, timeout=0.1)
+            if not self.serial_ins.serial_handle:
+                message_data= {
+                        "_type": "show_info",
+                        "plugins_mode": "mcu",
+                        "data": "MCU 打开串口失败",
+                    }
+                self.sendSocketMessage(
+                    msg="MCU 打开串口失败",
+                    data=message_data,
+                )
                 self.serial_ins = None
                 self.connect_state = False
+                return False
+
         except:
-            self.sendMsg(code=1, msg="条码识别 打开串口失败")
-            # print("条码识别 打开串口失败")
+            message_data={
+                "_type": "show_info",
+                "plugins_mode": "mcu",
+                "data": "MCU 打开串口失败",
+            }
+            self.sendSocketMessage(
+                msg="MCU 打开串口失败",
+                data=message_data,
+            )
             self.serial_ins = None
             self.connect_state = False
             return False
 
+        message_data={"_type": "show_info", "plugins_mode": "mcu", "data": "MCU 开始连接"}
+        self.sendSocketMessage(
+            msg="MCU 开始连接",
+            data=message_data,
+        )
+        # =======================发送连接请求=================================
+        cmd = 90
+        data = [cmd, 1]
+        print("405  发送 连接请求  -----------------------------------------")
+        print(self.serial_ins)
+        # self.serial_ins.clearn_flush()
+        self.serial_ins.write_cmd(data)
+
+        # 延迟接收数据
+        time.sleep(0.3)
+        receive_data = self.serial_ins.read_cmd(out_time=1)
+        if receive_data:
+            print(
+                "409  receive_data--90:{}".format(self.change_hex_to_int(receive_data))
+            )
+        if receive_data:
+            # receive_data[2]=1 表示为MCU设备编号
+            if receive_data[0] == 90 and receive_data[2] == 1:
+                connect_flag = receive_data[1]
+                # 是否有初始化
+                try:
+                    mcu_has_been_set = receive_data[
+                        6
+                    ]  # 设备是否有初始化 ,1 表示已初始化
+                except:
+                    mcu_has_been_set = 99  # 未知状态
+                print("MCU初始化信息{}".format(mcu_has_been_set))
+                message_data = {
+                    "_type": "show_info",
+                    "plugins_mode": "mcu",
+                    "data": "MCU 已连接",
+                }
+                self.sendSocketMessage(
+                    msg="MCU 已连接",
+                    data=message_data,
+                )
+                self.connect_state = True
+                if not self.is_running:
+                    print("MCU start")
+                    self.is_running = True
+                print("MCU 已连接")
+                self.port_name = port_name
+                return
+
+        print("MCU 连接失败")
+        message_data = {
+            "_type": "show_info",
+            "plugins_mode": "mcu",
+            "data": "MCU 连接失败",
+        }
+        self.sendSocketMessage(
+            msg="MCU 连接失败",
+            data=message_data,
+        )
+        self.close_connect()
+
     def close_connect(self):
         self.port_name = ""
         if self.serial_ins:
@@ -231,38 +261,30 @@ class DeviceControl:
             self.connect_state = False
 
 
-if __name__ == "__main__":
-    device_control = DeviceControl()
-    p_list = []
-    temp_ports_dict = {}
-    while 1:
-        time.sleep(1)
-        ports_dict = device_control.scan_serial_port()
-        temp_ports_dict = ports_dict
-
+async def checkMcuConnection(device_ctrl: DeviceControl):
+    """实时检测串口是否连接"""
+    while True:
+        await asyncio.sleep(0.5)
+        ports_dict = device_ctrl.scan_serial_port()
+        device_ctrl.temp_ports_dict = ports_dict
         if not ports_dict:
             # 全部清空 移除所有串口
-            if p_list:
-                _p = p_list.pop()
-                device_control.remove_port(_p)
+            if device_ctrl.p_list:
+                _p = device_ctrl.p_list.pop()
+                device_ctrl.remove_port(_p)
+            print("串口未连接,请检查")
+            device_ctrl.sendSocketMessage(code=1, msg="串口未连接,请检查")
             continue
 
         if ports_dict:
-            # print(plist)
-            for index, _i in enumerate(p_list):
+            for index, _i in enumerate(device_ctrl.p_list):
                 if _i not in ports_dict:
-                    _p = p_list.pop(index)
-                    device_control.remove_port(_p)
-
+                    _p = device_ctrl.p_list.pop(index)
+                    device_ctrl.remove_port(_p)
             for _port_name, _port_value in ports_dict.items():
-                if _port_name not in p_list:
+                if _port_name not in device_ctrl.p_list:
                     # try:
-                        p_list.append(_port_name)
-                        device_control.add_port_by_linkage(_port_name)
+                        device_ctrl.p_list.append(_port_name)
+                        device_ctrl.add_port_by_linkage(_port_name)
                     # except BaseException as e:
-                    #     print(e.__traceback__.tb_frame.f_globals["__file__"])   # 发生异常所在的文件
-                    #     print(e.__traceback__.tb_lineno)                        # 发生异常所在的行数
-                    #     print("串口不存在{} {}".format(_port_name, e))
-
-                # threading.Thread(target=self.add_port, args=(_port_name, _port_value)).start()
-                # self.add_port(_p)
+                        # print("串口不存在{} {}".format(_port_name, e))

+ 340 - 0
python/mcu/RemoteControlV2.py

@@ -0,0 +1,340 @@
+# module_remote_control_v2
+
+
+import time, asyncio
+import settings
+from .SerialIns import SerialIns
+from .BaseClass import BaseClass
+from sockets.connect_manager import ConnectionManager
+
+
+# from .BlueToothMode import BlueToothMode
+class RemoteControlV2(BaseClass):
+    # sign_data = Signal(dict)
+
+    def __init__(self, bluetooth_ins, websocket_manager: ConnectionManager):
+        # 遥控设备处理--新版遥控器;硅胶按钮
+        super().__init__(websocket_manager)
+        self.websocket_manager = websocket_manager
+        # self.windows = windows
+        self.serial_ins = None
+        self.bluetooth_ins = bluetooth_ins
+
+        self.port_name = ""
+        self.bluetooth_address = ""
+        self.connect_state = False
+        self.is_running = False
+
+    def to_connect_com(self, port_name, is_test=False):
+        if self.connect_state:
+            return
+
+        self.close_connect()
+        time.sleep(0.5)
+        try:
+            # 原值为9600
+            self.serial_ins = SerialIns(port_name=port_name, baud=115200)
+            # self.serial_ins = SerialIns(port_name=port_name, baud=9600)
+            if self.serial_ins.serial_handle:
+                message = {
+                    "_type": "show_info",
+                    "plugins_mode": "remote_control",
+                    "data": "遥控设备V2 打开串口成功",
+                }
+                self.sendSocketMessage(
+                    code=0, msg="遥控设备V2 打开串口成功", data=message
+                )
+                # print(message)
+                self.connect_state = True
+                message = {
+                    "_type": "remote_control_connect",
+                    "plugins_mode": "remote_control",
+                    "data": port_name,
+                }
+                self.sendSocketMessage(code=0, msg="", data=message)
+                print(message)
+                self.port_name = port_name
+
+                self.is_running = True
+                self.set_voltage_value(0)
+                self.run()
+                return True
+
+            else:
+                message = {
+                    "_type": "show_info",
+                    "plugins_mode": "remote_control",
+                    "data": "遥控设备V2 打开串口失败",
+                }
+                self.sendSocketMessage(
+                    code=1, msg="遥控设备V2 打开串口失败", data=message
+                )
+                print(message)
+                self.serial_ins = None
+                self.connect_state = False
+                self.set_voltage_value(None)
+        except:
+            message = {
+                "_type": "show_info",
+                "plugins_mode": "remote_control",
+                "data": "遥控设备V2 打开串口失败",
+            }
+            print(message)
+            self.sendSocketMessage(code=1, msg="遥控设备V2 打开串口失败", data=message)
+            self.serial_ins = None
+            self.connect_state = False
+            self.set_voltage_value(None)
+            return False
+
+    def to_connect_bluetooth(self, address, is_test=False):
+        print("to_connect_bluetooth", self.connect_state)
+        if self.connect_state:
+            return
+        # if self.bluetooth_ins == None:
+        #     print("bluetooth_ins 未初始化", bluetooth_mode)
+        #     self.bluetooth_ins = BlueToothMode()
+        # else:
+        #     self.bluetooth_ins = bluetooth_mode
+        self.close_connect()
+        if self.bluetooth_ins == None:
+            print("bluetooth_ins 未初始化", self.bluetooth_ins)
+            # self.bluetooth_ins = self.
+        self.bluetooth_address = address
+        message = {
+            "_type": "show_info",
+            "plugins_mode": "remote_control",
+            "data": "遥控设备V2 打开蓝牙成功",
+        }
+        print(message)
+        self.sendSocketMessage(code=0, msg="遥控设备V2 打开蓝牙成功", data=message)
+        self.connect_state = True
+        self.is_running = True
+        self.set_voltage_value(0)
+        if is_test is False:
+            loop = asyncio.get_event_loop()
+            loop.create_task(self.run())
+
+    def close_bluetooth_connect(self):
+        if self.bluetooth_address:
+            print("蓝牙断开")
+            message = {
+                "_type": "show_info",
+                "plugins_mode": "remote_control",
+                "data": "遥控设备V2 蓝牙断开",
+            }
+            print(message)
+            self.sendSocketMessage(code=1, msg="遥控设备V2 蓝牙断开", data=message)
+            self.close_connect()
+
+    def set_voltage_value(self, voltage_value=None, voltage_text=None):
+        if self.is_running:
+            flag = "接收器已连接 {}".format(
+                "蓝牙" if self.bluetooth_address else "串口"
+            )
+        else:
+            flag = "接收器未连接"
+
+        if voltage_value is None:
+            # self.windows.show_label.setText("{}".format(flag))
+            if voltage_text:
+                print(voltage_text)
+                # self.windows.show_label.setText("{}".format(voltage_text))
+        else:
+            if voltage_value == 0:
+                print(flag)
+                # self.windows.show_label.setText("{}".format(flag))
+            else:
+                print("电量:{}%".format(voltage_value))
+                # self.windows.show_label.setText("电量:{}%".format(voltage_value))
+        print("打印===>", flag)
+    def close_connect(self):
+        self.port_name = ""
+        self.bluetooth_address = ""
+        # self.bluetooth_ins = None
+        if self.serial_ins:
+            self.serial_ins.close_serial_port()
+        self.connect_state = False
+
+    def __del__(self):
+        self.close_connect()
+
+    def play_sound(self, tip="sound_tips_3"):
+        self.windows.playsound.tips_type = tip
+        self.windows.playsound.start()
+
+    def analysis_received_data(self):
+        if not self.connect_state:
+            return
+
+        if self.bluetooth_address:
+            receive_data = self.bluetooth_ins.read_cmd_one(
+                address=self.bluetooth_address
+            )
+            # print("received data", receive_data)
+        else:
+            receive_data = self.serial_ins.read_cmd(out_time=1, check=None)
+        # print("self.bluetooth_ins", receive_data)
+        if receive_data is False:
+            self.connect_state = False
+            return False
+
+        if not receive_data:
+            return
+        # 数据 结构 command,按命令解析
+        if receive_data[0] == 1:
+            # self.play_sound("get_qr_code")
+            # 扫码数据
+            bar_code = receive_data[1:].decode()
+            bar_code = bar_code.replace("\r", "")
+            bar_code = bar_code.replace("\n", "")
+            message = {"_type": 0, "plugins_mode": "remote_control", "data": bar_code}
+            print(message)
+            self.sendSocketMessage(code=0, msg="", data=message)
+            return
+        if receive_data[0] == 9:
+            # 播放声音
+            button_value = receive_data[1]
+            data = {"button_value": button_value}
+            message = {"_type": 9, "plugins_mode": "remote_control", "data": data}
+            print(message)
+            self.sendSocketMessage(code=0, msg="", data=message)
+            if settings.IS_DEBUG:
+                print("收到按键", button_value)
+            return
+
+        if receive_data[0] == 10:
+            voltage_value = receive_data[1]
+            self.set_voltage_value(voltage_value)
+            if settings.IS_TEST:
+                print("遥控器V2电量:{}".format(voltage_value))
+            return
+
+        # 使用Max17048 查看电量
+        if receive_data[0] == 12:
+            chg_status = self.get_data_from_receive_data(
+                receive_data=receive_data, start=1, len_data=1
+            )
+            soc_percentage = self.get_data_from_receive_data(
+                receive_data=receive_data, start=2, len_data=4
+            )
+            soc_percentage = soc_percentage / 100
+
+            voltage = self.get_data_from_receive_data(
+                receive_data=receive_data, start=6, len_data=4
+            )
+            voltage = voltage / 100
+
+            current = self.get_data_from_receive_data(
+                receive_data=receive_data, start=10, len_data=4
+            )
+            current = current / 10000
+
+            temperature = self.get_data_from_receive_data(
+                receive_data=receive_data, start=14, len_data=4
+            )
+            temperature = temperature / 100
+
+            adjusted_soc = self.get_data_from_receive_data(
+                receive_data=receive_data, start=18, len_data=4
+            )
+            adjusted_soc = adjusted_soc / 100
+
+            soft_vision = self.get_data_from_receive_data(
+                receive_data=receive_data, start=22, len_data=1
+            )
+            full_status = self.get_data_from_receive_data(
+                receive_data=receive_data, start=23, len_data=1
+            )
+
+            # print("is_charging:{}".format(chg_status))
+            # print("Battery SOC: {:.2f}%".format(soc_percentage))
+            # print("Battery Voltage: {:.3f}V".format(voltage))
+            # print("Average Current: {:.3f}A".format(current))
+            # print("Chip Temperature: {:.1f}°C".format(temperature))
+            # print("adjusted Battery soc: {:.2f}%".format(adjusted_soc))
+            # print("soft_vision:{}".format(soft_vision))
+            # print("chg_status:{}  full_status:{}".format(chg_status, full_status))
+            if chg_status:
+                t1 = "充电中"
+            else:
+                t1 = ""
+
+            if full_status:
+                t2 = "已充满"
+            else:
+                t2 = ""
+
+            self.set_voltage_value(
+                voltage_text="遥控器:{:.1f}%  {:.2f}V {}{}".format(
+                    min(adjusted_soc / 92.5 * 100, 100), voltage, t1, t2
+                )
+            )
+            return
+
+        if receive_data[0] == 111:
+            value = (
+                receive_data[1] << 24
+                | receive_data[2] << 16
+                | receive_data[3] << 8
+                | receive_data[4]
+            )
+            print("遥控器-测试   value:{}".format(value))
+            # self.windows.show_label.setText("--------{}".format(value))
+        if receive_data[0] == 112:
+            bar_code = receive_data[1:].decode()
+            bar_code = bar_code.replace("\r", "")
+            bar_code = bar_code.replace("\n", "")
+            print("read bar_code {}".format(bar_code))
+
+    # 通用串口数据解析器
+    def get_data_from_receive_data(
+        self, receive_data, start, len_data, data_magnification=1
+    ):
+        # data_magnification 数据放大倍数,或缩小倍数,默认为1
+        try:
+            if len_data == 1:
+                data = receive_data[start]
+                return data * data_magnification
+            elif len_data == 2:
+                data = receive_data[start] << 8 | receive_data[start + 1]
+                return data * data_magnification
+            elif len_data == 4:
+                data = (
+                    receive_data[start] << 24
+                    | receive_data[start + 1] << 16
+                    | receive_data[start + 2] << 8
+                    | receive_data[start + 3]
+                )
+                return data * data_magnification
+            return None
+        except:
+            return None
+
+    async def run(self):
+        # self.show_info.emit("未连接")
+        # self.data_command_sign.emit(data)
+        self.is_running = True
+        print("Running")
+        while 1:
+            await asyncio.sleep(0.01)
+            if not self.connect_state:
+                message = {
+                    "_type": "show_info",
+                    "plugins_mode": "remote_control",
+                    "data": "遥控设备V2 未连接",
+                }
+                print(message)
+                self.sendSocketMessage(code=1, msg="遥控设备V2 未连接", data=message)
+                break
+            self.analysis_received_data()
+
+        self.is_running = False
+        if not self.connect_state:
+            message = {
+                "_type": "show_info",
+                "plugins_mode": "remote_control",
+                "data": "遥控设备V2 未连接",
+            }
+            print(message)
+            self.sendSocketMessage(code=1, msg="遥控设备V2 未连接", data=message)
+            self.set_voltage_value(None)

+ 3 - 2
python/mcu/SerialIns.py

@@ -66,7 +66,7 @@ class SerialIns(object):
             buf.extend(data)
             buf.extend([0xFF & ~sum(data)])
             # 55 55 02 5a 01 a4
-            # print("send buf  {}".format(self.change_hex_to_int(buf)))
+            print("send buf  {}".format(self.change_hex_to_int(buf)))
             try:
                 self.serial_handle.write(buf)
                 return True
@@ -302,12 +302,13 @@ class SerialIns(object):
         self.close_serial_port()
 
 if __name__ == "__main__":
-    s = SerialIns(port_name="COM4", baud=115200, timeout=0.1)
+    s = SerialIns(port_name="COM5", baud=115200, timeout=0.1)
     s.scan_serial_port()
     s.clearn_flush()
     print("-" * 30)
     for i in range(2):
         data = [2,99,1]
+        
         s.write_cmd(data)
 
         time.sleep(0.1)

BIN
python/requestments.txt


+ 2 - 1
python/sockets/__init__.py

@@ -1 +1,2 @@
-from .socket_client import socket_manager
+from .socket_client import socket_manager, sendMsg
+from .connect_manager import ConnectionManager

+ 1 - 0
python/sockets/connect_manager.py

@@ -32,3 +32,4 @@ class ConnectionManager:
         """广播消息"""
         for connection in self.active_connections:
             await connection.send_json(message)
+

+ 5 - 1
python/sockets/socket_client.py

@@ -1,5 +1,6 @@
 # socket_manager.py
-import socket, json
+from enum import Flag
+import socket, json, asyncio
 import websockets
 from settings import APP_HOST,PORT
 from middleware import UnicornException
@@ -38,3 +39,6 @@ class SocketClient:
 
 # 创建全局 SocketManager 实例
 socket_manager = SocketClient(f"ws://{APP_HOST}:{PORT}/ws")
+def sendMsg(code=0, msg="", data=None):
+    '''通用客户端发送消息机制'''
+    asyncio.run(socket_manager.send_message(code=code, msg=msg, data=data))

+ 17 - 8
python/sockets/socket_server.py

@@ -3,25 +3,34 @@ import asyncio
 from models import *
 from .connect_manager import ConnectionManager
 from .message_handler import *
+from mcu.DeviceControl import DeviceControl,checkMcuConnection
+from mcu.BlueToothMode import BlueToothMode
+from mcu.RemoteControlV2 import RemoteControlV2
 import time
 from .socket_client import socket_manager
-manager = ConnectionManager()
+conn_manager = ConnectionManager()
 active_connections = set()
-
+device_ctrl = DeviceControl(websocket_manager=conn_manager)
+blue_tooth = BlueToothMode(websocket_manager=conn_manager)
 @app.websocket("/ws")
 async def websocket_endpoint(websocket: WebSocket):
-    await manager.connect(websocket)
+    await conn_manager.connect(websocket)
     active_connections.add(websocket)
+
     try:
-        await socket_manager.connect()
+        # await socket_manager.connect()
         async def handler_messages():
             while True:
                 byteDats = await websocket.receive()
                 print("byteDats", byteDats)
-                await handlerSend(manager, json.dumps(byteDats), websocket)
-        await asyncio.gather(handler_messages())
+                await handlerSend(conn_manager, json.dumps(byteDats), websocket)
+        async def checkConnMcu():
+            await checkMcuConnection(device_ctrl)
+        async def connectBlueTooth():
+            await blue_tooth.main_func()
+        await asyncio.gather(handler_messages(), checkConnMcu(), connectBlueTooth())
     except WebSocketDisconnect:
-        socket_manager.close()
+        # socket_manager.close()
         print("Client disconnected")
     finally:
         active_connections.discard(websocket)
@@ -32,7 +41,7 @@ async def websocket_endpoint(websocket: WebSocket):
 @app.on_event("shutdown")
 async def shutdown_event():
     print("Shutting down...")
-    socket_manager.close()
+    # socket_manager.close()
     # 清理操作
     for connection in list(active_connections):
         try:

+ 12 - 0
python/utils/SingletonType.py

@@ -0,0 +1,12 @@
+import threading
+class SingletonType(type):
+    _instance_lock = threading.Lock()
+
+    def __call__(cls, *args, **kwargs):
+        if not hasattr(cls, "_instance"):
+            with SingletonType._instance_lock:
+                if not hasattr(cls, "_instance"):
+                    cls._instance = super(SingletonType, cls).__call__(*args, **kwargs)
+        return cls._instance
+
+