kinve vor 1 Jahr
Ursprung
Commit
9dc10dd176
10 geänderte Dateien mit 552 neuen und 69 gelöschten Zeilen
  1. BIN
      ble.exe
  2. 56 20
      ble.py
  3. 1 8
      ble.spec
  4. 277 0
      client.js
  5. 47 41
      config.json
  6. BIN
      dist/ble.exe
  7. 29 0
      message_base.py
  8. 6 0
      package.json
  9. 72 0
      u8data.js
  10. 64 0
      websocket_server.py

BIN
ble.exe


+ 56 - 20
ble.py

@@ -12,6 +12,9 @@ from Crypto.Cipher import AES
 from Crypto.Util.Padding import pad, unpad
 import copy
 
+from message_base import MessageBase
+from websocket_server import WebServer
+
 import sys
 import atexit
 import signal
@@ -50,6 +53,9 @@ g_ble_mtu = 20
 g_ble_client = None
 g_download_cfg = {"Key":"", "Total":0, "Number":0, "DataCrc16":0}
 
+mb = None
+ws = None
+
 def calculate_time(func):
     def wrapper(*args, **kwargs):
         start_time = time.time()
@@ -194,9 +200,17 @@ def ev_packing(data):
     lenght = len(data)
     # crc16 = crc16_func(data) ## 计算数据的CRC-16校验码
     # checknum = crc16(data, 0, len(data))
-    checknum = checknum_16(data)
-    HEAD_SEND = str(g_config["def_cfg"]["head_send"]).encode()
-    out = list(HEAD_SEND) + [ (lenght>>8)&0xFF, (lenght>>0)&0xFF, (checknum>>8)&0xFF, (checknum>>0)&0xFF]
+    out = []
+    # 头
+    if g_config["def_cfg"]["head_send"] != "":
+        HEAD_SEND = str(g_config["def_cfg"]["head_send"]).encode()
+        out = list(HEAD_SEND)
+    # 长度
+    out = out + [ (lenght>>8)&0xFF, (lenght>>0)&0xFF]
+    # 校验码
+    if g_config["def_cfg"]["checknum_type"] != "":
+        checknum = checknum_16(data)
+        out = out + [ (checknum>>8)&0xFF, (checknum>>0)&0xFF]
 
     for d in bytes(data):
         out.append(d)
@@ -207,6 +221,12 @@ def ev_packing(data):
 recv_data = []
 recv_start_time = 0
 GetConfiguration = []
+def PACK_LEN():
+    H = len(g_config["def_cfg"]["head_send"])
+    L = 2
+    C = 2 if g_config["def_cfg"]["checknum_type"]!="" else 0
+    return H+L+C
+
 @calculate_time
 def recv_handler(data: bytearray, reve=False):
     global recv_start_time
@@ -221,19 +241,21 @@ def recv_handler(data: bytearray, reve=False):
         recv_data.clear()
         return        
 
-    if len(recv_data)<7:
+    if len(recv_data)<PACK_LEN():
         return
 
     recv_data_str = str(bytes(recv_data))
     HEAD_SEND = str(g_config["def_cfg"]["head_send"])
     HEAD_RECV = str(g_config["def_cfg"]["head_recv"])
     find_head = HEAD_SEND if not reve else HEAD_RECV
-    index = recv_data_str.find(find_head)#'EV>'
-    if index < 2:
-        return
+    index = -1
+    if find_head != "":
+        index = recv_data_str.find(find_head)#'EV>'
+        if index < 2:
+            return
     index -= 2
     data_len = (recv_data[index+4]&0xff) | (recv_data[index+3]&0xff)<<8
-    if index+7+data_len > len(recv_data):
+    if index+PACK_LEN()+data_len > len(recv_data):
         return
     
     if g_config["def_cfg"]["recv_soc_data_print"]:
@@ -244,12 +266,15 @@ def recv_handler(data: bytearray, reve=False):
         logger.info('接收耗时:%.3fs', recv_end_time - recv_start_time)  
         recv_start_time = 0     
 
-    soc_data = recv_data[index+7:index+7+data_len]
-    get_crc = (recv_data[index+6]&0xff)| (recv_data[index+5]&0xff)<<8 
-    check_num = checknum_16(soc_data)
-    if get_crc != check_num:
-        logger.info("校验失败(%d):0x%04X 0x%04X", len(recv_data), get_crc, check_num)
-        return
+    soc_data = recv_data[index+PACK_LEN():index+PACK_LEN()+data_len]
+    if g_config["def_cfg"]["checknum_type"]!="":
+        H = len(g_config["def_cfg"]["head_send"])
+        L = 2
+        get_crc = (recv_data[index+(H+L+1)]&0xff)| (recv_data[index+(H+L)]&0xff)<<8 
+        check_num = checknum_16(soc_data)
+        if get_crc != check_num:
+            logger.info("校验失败(%d):0x%04X 0x%04X", len(recv_data), get_crc, check_num)
+            return
 
     logger.info("校验成功")
 
@@ -265,7 +290,9 @@ def recv_handler(data: bytearray, reve=False):
     recv_data.clear()
 
 def notification_handler(characteristic: BleakGATTCharacteristic, data: bytearray):
+    global mq
     recv_handler(data)
+    mq.add("ws_send", data)
 
 async def ble_send(client, data):
     global recv_start_time
@@ -427,6 +454,7 @@ async def main():
     global g_ble_client
     global g_config
     global g_ble_mtu
+    global mq
 
     logger.info("读取配置...")
     with open(config_file, "r", encoding="utf-8") as f:
@@ -486,6 +514,14 @@ async def main():
                     input_wait_time = time.time()                      
                     await input_call(client)
 
+                try:
+                    # 开始根据设备即功能处理消息
+                    ws_data = mq.get(device)
+                    if ws_data:
+                        ble_send(client, ws_data)        
+                except Exception as err:
+                    pass
+
                 time.sleep(0.1)
 
 def test_aes():
@@ -516,16 +552,14 @@ def test_recv():
     recv_handler(bytes.fromhex(str_b), True)    
 
 
-def run():
-    tasks = []
-    tasks.append(main())
+def run(tasks = []):
     # tasks.append(task())
     loop = asyncio.get_event_loop()
     loop.run_until_complete(asyncio.wait(tasks))
     loop.close()
 
 
-
+#---------------------------------------------------------
 if __name__ == "__main__":
     signal.signal(signal.SIGTERM, sig_handler)  # kill pid
     signal.signal(signal.SIGINT, sig_handler)  # ctrl -c
@@ -533,8 +567,10 @@ if __name__ == "__main__":
     # test_aes()
     # test_base64()
     # test_recv()
+    mq = MessageBase()
+    ws = WebServer("0.0.0.0", 11100, mq)
+    ws.run()
+    run([main(),])
 
-    run()
-    # asyncio.run(main())
     sys.exit(0)
 

+ 1 - 8
ble.spec

@@ -1,9 +1,6 @@
 # -*- mode: python ; coding: utf-8 -*-
 
 
-block_cipher = None
-
-
 a = Analysis(
     ['ble.py'],
     pathex=[],
@@ -14,18 +11,14 @@ a = Analysis(
     hooksconfig={},
     runtime_hooks=[],
     excludes=[],
-    win_no_prefer_redirects=False,
-    win_private_assemblies=False,
-    cipher=block_cipher,
     noarchive=False,
 )
-pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
+pyz = PYZ(a.pure)
 
 exe = EXE(
     pyz,
     a.scripts,
     a.binaries,
-    a.zipfiles,
     a.datas,
     [],
     name='ble',

+ 277 - 0
client.js

@@ -0,0 +1,277 @@
+// 使用ws模块
+const { monitorEventLoopDelay } = require('perf_hooks');
+const WebSocket = require('ws');
+
+// npm i stickpackage
+// npm i ws
+
+var ws = new WebSocket("ws://jiangyi.site:11111/");
+
+let stringsArray = [
+    "get:version=?",
+    "get:heart_beat=?",
+    "get:time=?",
+
+    "get:work_status=?",
+    "get:fire_status=?",
+    "get:fault_status=?",
+
+    "get:humidity=?",
+    "get:temperature=?",
+    "get:cur_photosen0=?",
+    "get:photosen0=?",
+    "get:net=?",
+    "get:event_num=?",
+    "get:warning_num=?",
+
+    // "set:extra_time=180",
+    // "set:standing_time=10",
+    // "set:interval_time=600",
+
+    // "set:photosen0=6000,6000,6000,6000",
+    // "set:time=24,9,15,21,53,0",
+    // "set:reset=1",
+    // "set:net=WIFI24,12345678,qq.com,1883,user,123456",
+    // "set:event_num=1000",
+    // "set:warning_num=1000",
+    // "set:erase_bit=7",
+];
+let strings_i = 0;
+
+
+
+function bin2String(array) {
+    return String.fromCharCode.apply(String, array);
+}
+
+function calculateChecksum16bit(code) {
+    let sum = 0;
+    for (let i = 0; i < code.length; i++) {
+        sum += code[i]&0xff;
+        // console.log("sum:"+ sum +",i:"+ code[i] );
+    }
+    // 将结果转换为16位二进制字符串
+    return sum&0xffff;
+}
+
+function stringToByteArray(str) {
+    const encoder = new TextEncoder(); // 创建TextEncoder实例
+    const bytes = encoder.encode(str); // 将字符串转换为字节序列
+    return bytes; // 返回字节数组
+}
+
+//小端模式
+function BytesToIntLittleEndian(bytes){
+    var val = 0;
+    for (var i = bytes.length - 1; i >= 0; i--) {        
+        val += bytes[i];    
+        if (i != 0) {
+            val = val << 8;
+        }    
+    }
+    return val;
+}
+ 
+//小端模式
+//number 要转换的整形数值
+//length 要转成什么byte数组,规定数组的长度
+//如uint16,则lenght=2表示两个字节,转成的byte数组长度是length=2
+//如uint32,则lenght=2表示两个字节,转成的byte数组长度是length=4
+function IntToBytesLittleEndian(number, length){
+    var bytes = [];
+    var i = 0;
+    do {
+    bytes[i++] = number & (255);
+    number = number>>8;
+    } while ( i < length )
+    return bytes;
+}
+ 
+//大端模式
+function BytesToIntBigEndian(bytes){
+    var val = 0;
+    for (var i = 0; i < bytes.length; ++i) {        
+        val += bytes[i];        
+        if (i < bytes.length-1) {
+            val = val << 8;
+        }
+    }
+    return val;
+}
+ 
+//大端模式
+//number 要转换的整形数值
+//length 要转成什么byte数组,规定数组的长度
+//如uint16,则lenght=2表示两个字节,转成的byte数组长度是length=2
+//如uint32,则lenght=2表示两个字节,转成的byte数组长度是length=4
+function IntToBytesBigEndian(number, length){
+    var bytes = [];
+    var i = length;
+    do {
+    bytes[--i] = number & (255);
+    number = number>>8;
+    } while (i)
+    return bytes;
+}
+
+function Uint8ArrayToString(fileData){
+    var dataString = "";
+    for (var i = 0; i < fileData.length; i++) {
+      dataString += String.fromCharCode(fileData[i]);
+    }
+   
+    return dataString
+  
+  }
+
+function stringToUint8Array(str){
+    var arr = [];
+    for (var i = 0, j = str.length; i < j; ++i) {
+      arr.push(str.charCodeAt(i));
+    }
+   
+    var tmpUint8Array = new Uint8Array(arr);
+    return tmpUint8Array
+  }
+     
+
+// 封包
+function pack_send(str) {
+    console.log("封包前:"+str);
+    
+    body = str;
+    body = stringToByteArray(str);
+    data_len = body.length;
+    data_check = calculateChecksum16bit(body);
+    let headBuf = Buffer.from([0x59, 0x4C, '<', (data_len>>8)&0xff, (data_len>>0)&0xff, (data_check>>8)&0xff, (data_check>>0)&0xff]);
+    let pack_date = headBuf+body
+    ws.send(pack_date);   
+    console.log("封包后:"+pack_date);
+}
+
+function handler_recv(out){
+    console.log("解析:" + out);
+    str = String(out);
+    let arr = str.split("=");
+    if(arr.length == 2){
+        let key = arr[0]
+        let val = arr[1]
+        if(key == "heart_beat"){
+            let arr2 = val.split(",");
+            if(arr2.length == 6){
+                year = arr2[0];
+                mon = arr2[1]; 
+                day = arr2[2]; 
+                hour = arr2[3];
+                min = arr2[4]; 
+                sec = arr2[5]; 
+                console.log("heart_beat:20"+year+"-"+mon+"-"+day+" "+hour+":"+min+":"+sec);
+            }
+
+        }
+    }
+}
+
+
+// 解包
+let RecvBuf = "";
+function unpack_recv(data) {
+    console.log("recv " + data.length +":"+ data);
+    // data添加到全局RecvBuf
+    if(RecvBuf == ""){
+        RecvBuf = data;
+    }else{
+        RecvBuf += data;
+    }
+    
+
+    if(RecvBuf.length < 7){
+        return;
+    }
+
+    // console.log(Buffer.from(RecvBuf.slice(0, 7)).toString('hex')); 
+
+    //找包头 
+    let index = -1;    
+    for (i = 0; i < RecvBuf.length-3; i++) { 
+        if(RecvBuf.slice(i, i+3) == "YL>"){
+            index = i;
+            break;
+        }
+    }
+    if(index == -1){
+        console.log("未找到到包头");
+        return;
+    }
+    console.log("已查到包头, index:"+ index );
+
+    // 如果不满足,断包了
+    if(index+7 > RecvBuf.length){
+        console.log("检查长度1失败");
+        return;
+    }
+    console.log("检查长度1正常" );
+
+    // console.log(RecvBuf.slice(index+3, index+5));   
+    let data_len = BytesToIntBigEndian(RecvBuf.slice(index+3, index+5));
+    // console.log("data_len:"+ data_len );
+    // 检查实际长度
+    if(index+7+data_len > RecvBuf.length || data_len == 0){
+        console.log("检查长度2失败, data_len:" + data_len + "recv_len:" + RecvBuf.length );
+        return;
+    }    
+    console.log("检查长度2正常" );
+
+    // 检查校验码
+    // console.log("5:" + RecvBuf[index+5]  +", 6:"+ RecvBuf[index+6] );
+    let recv_check = BytesToIntBigEndian(RecvBuf.slice(index+5, index+7));
+    let cal_check = calculateChecksum16bit(RecvBuf.slice(index+7,index+7+data_len));
+    if(recv_check != cal_check){
+        console.log("校验码失败, recv_check:" + recv_check  +", cal_check:"+ cal_check );
+        return;
+    }
+    console.log("校验码成功" );
+
+    //解析数据
+    let start = index+7;
+    let stop = index+7+data_len;
+    console.log("start:" + start  +", stop:"+ stop );
+    let out = RecvBuf.slice(index+7,index+7+data_len);
+
+    // 处理out数据
+    handler_recv(out);
+
+    // 留下剩余数据:正常情况下长度=0:无剩余数据
+    let old_len = data.length;
+    RecvBuf = RecvBuf.slice(index+7+data_len);
+    console.log("剩余数据:" + RecvBuf);
+}
+
+
+// 设置一个每5000毫秒执行一次的定时器
+setInterval(function() {
+    array = stringsArray[strings_i];
+    pack_send(array);
+
+
+    strings_i++;
+    if(strings_i >= stringsArray.length){
+        strings_i = 0;
+    }
+}, 5000);
+
+
+
+ws.onopen = function(evt) {
+	console.log("Connection open ...");
+};
+ 
+ws.onmessage = function(evt) {
+    unpack_recv(evt.data); //string
+};
+ 
+ws.onclose = function(evt) {
+	console.log("Connection closed.");
+};
+
+

+ 47 - 41
config.json

@@ -1,18 +1,18 @@
 {
 "def_cfg":{
-	"ble_mac": "E0:C2:39:D6:16:6C",
+	"ble_mac": "7C:B9:4C:DA:61:7C",
 	"ble_mac2": "E9:8B:FC:41:3F:CF",
 	"ble_mac3": "DB:57:46:A6:14:22",
 	"heart_beat_sel": 1,
 	"input_interval": 5,
 	"heart_beat_interval": 25,	
-	"notif_char": "0000ffe2-0000-1000-8000-00805f9b34fb",
-	"write_char": "0000ffe1-0000-1000-8000-00805f9b34fb",
+	"notif_char": "49535343-8841-43f4-a8d4-ecbe34729bb3",
+	"write_char": "49535343-1e4d-4bd9-ba61-23c647249616",
 	"ble_mtu": 0,
 	"ble_send_wait": 1.5,
-	"head_send": "YL<",
-	"head_recv": "YL>",
-	"checknum_type": "sum16",
+	"head_send": "",
+	"head_recv": "",
+	"checknum_type": "",
 	"checknum_type1": "crc16",
 	"aes_cbc_enbable": false,
 	"aes_cbc_key_send": "",
@@ -26,85 +26,91 @@
 "cmd_list":	
 [
 	[
-		"hex:00",
 		"",
+		"str:get:heart_beat",
 		"heart_beat_sel"
 	],
 	[
-		"hex:01",
 		"",
+		"str:get:version=?",
 		"get_soft_version"
 	],
 	[
-		"hex:04",
 		"",
+		"str:get:work_status=?",
 		"get_work_status"
 	],
 	[
-		"hex:05",
 		"",
-		"get_fire_version"
+		"str:get:fire_status=?",
+		"get_fire_status"
 	],	
 	[
-		"hex:06",
 		"",
+		"str:get:fault_status=?",
 		"get_fault_status"
 	],
 	[
-		"hex:07",
 		"",
+		"str:get:humidity=?",
 		"get_humidity"
 	],
 	[
-		"hex:08",
 		"",
+		"str:get:temperature=?",
 		"get_temperature"
 	],
 	[
-		"hex:09",
-		"hex:00",
-		"get_photosen"
-	],
-
-	[
-		"hex:80",
 		"",
-		"TestSet"
+		"str:get:cur_photosen0=?",
+		"get_photosen"
 	],	
 	[
-		"hex:81",
-		"str:wifi_ssid=WIFI24,wifi_pass=12345678,mqtt_host=qq.com,mqtt_port=1883,mqtt_user=user,mqtt_pass=123456,",
-		"set_config"
+		"",
+		"str:get:photosen0=?",
+		"get_photosen"
 	],
 	[
-		"hex:83",
-		"hex:00b4",
-		"set_100m_time"
+		"",
+		"str:get:net=?",
+		"get_net"
 	],
+	
+	[
+		"",
+		"str:set:extra_time=180",
+		"set_extra_time"
+	],	
 	[
-		"hex:84",
-		"hex:000a",
+		"",
+		"str:set:standing_time=10",
 		"set_standing_time"
-	],
+	],	
 	[
-		"hex:85",
-		"hex:0258",
+		"",
+		"str:set:interval_time=600",
 		"set_interval_time"
-	],
+	],	
 	[
-		"hex:86",
-		"hex:00 0190 012c 00c8 0064",
+		"",
+		"str:set:photosen0=6000,6000,6000,6000",
 		"set_photosen"
-	],
+	],	
 	[
-		"hex:87",
-		"hex:18 0a 01 00 00 00",
+		"",
+		"set:time=24,10,1,0,0,0",
 		"set_time"
 	],
 	[
-		"hex:88",
 		"",
-		"set_reset"
+		"set:reset=1",
+		"set_rest"
+	],
+
+	[
+		"",
+		"set:net=WIFI24,12345678,qq.com,1883,user,123456",
+		"set_net"
 	]
 
 ]

BIN
dist/ble.exe


+ 29 - 0
message_base.py

@@ -0,0 +1,29 @@
+from queue import Queue
+ 
+ 
+class MessageBase:
+    def __init__(self):
+        self.data = dict()
+ 
+    def add(self, device, data):
+        if device in self.data:
+            self.data[device].put(data)
+        else:
+            self.data[device] = Queue()
+            self.data[device].put(data)
+ 
+    def get(self, device):
+        data_queue: Queue = self.data.get(device)
+        if not data_queue or data_queue.empty():
+            return None
+        data = data_queue.get()
+        return data
+ 
+ 
+if __name__ == '__main__':
+    mb = MessageBase()
+    mb.add("a", "asdasd")
+    mb.add("a", "11111111")
+    print(mb.data)
+    data = mb.get("a")
+    print(data)

+ 6 - 0
package.json

@@ -0,0 +1,6 @@
+{
+  "dependencies": {
+    "stickpackage": "^3.1.5",
+    "ws": "^8.18.0"
+  }
+}

+ 72 - 0
u8data.js

@@ -0,0 +1,72 @@
+
+//将字节数组转成有符号的8位整型,大端字节序
+function toInt8(bytes) {
+    return getView(bytes).getInt8();
+}
+//将字节数组转成无符号的8位整型,大端字节序
+function toUint8(bytes) {
+    return getView(bytes).getUint8();
+}
+//将字节数组转成有符号的16位整型,大端字节序
+function toInt16(bytes) {
+    return getView(bytes).getInt16();
+}
+//将字节数组转成无符号的16位整型,大端字节序
+function toUint16(bytes) {
+    return getView(bytes).getUint16();
+}
+//将字节数组转成有符号的32位整型,大端字节序
+function toInt32(bytes) {
+    return getView(bytes).getInt32();
+}
+//将字节数组转成无符号的32位整型,大端字节序
+function toUint32(bytes) {
+    return getView(bytes).getUint32();
+}
+//将字节数组转成32位浮点型,大端字节序
+function toFloat32(bytes) {
+    return getView(bytes).getFloat32();
+}
+//将字节数组转成64位浮点型,大端字节序
+function toFloat64(bytes) {
+    return getView(bytes).getFloat64();
+}
+
+//将数值写入到视图中,获得其字节数组,大端字节序
+function getUint8Array(len, setNum) {
+    var buffer = new ArrayBuffer(len);  //指定字节长度
+    setNum(new DataView(buffer));  //根据不同的类型调用不同的函数来写入数值
+    return new Uint8Array(buffer); //创建一个字节数组,从缓存中拿取数据
+}
+//得到一个8位有符号整型的字节数组,大端字节序
+function getInt8Bytes(num) {
+    return getUint8Array(1, function (view) { view.setInt8(0, num); })
+}
+//得到一个8位无符号整型的字节数组,大端字节序
+function getUint8Bytes(num) {
+    return getUint8Array(1, function (view) { view.setUint8(0, num); })
+}
+//得到一个16位有符号整型的字节数组,大端字节序
+function getInt16Bytes(num) {
+    return getUint8Array(2, function (view) { view.setInt16(0, num); })
+}
+//得到一个16位无符号整型的字节数组,大端字节序
+function getUint16Bytes(num) {
+    return getUint8Array(2, function (view) { view.setUint16(0, num); })
+}
+//得到一个32位有符号整型的字节数组,大端字节序
+function getInt32Bytes(num) {
+    return getUint8Array(4, function (view) { view.setInt32(0, num); })
+}
+//得到一个32位无符号整型的字节数组,大端字节序
+function getUint32Bytes(num) {
+    return getUint8Array(4, function (view) { view.setUint32(0, num); })
+}
+//得到一个32位浮点型的字节数组,大端字节序
+function getFloat32Bytes(num) {
+    return getUint8Array(4, function (view) { view.setFloat32(0, num); })
+}
+//得到一个64位浮点型的字节数组,大端字节序
+function getFloat64Bytes(num) {
+    return getUint8Array(8, function (view) { view.setFloat64(0, num); })
+}

+ 64 - 0
websocket_server.py

@@ -0,0 +1,64 @@
+import asyncio
+import json
+import threading
+import websockets
+##
+from message_base import MessageBase
+ 
+ 
+class WebServer:
+    def __init__(self, host, port, message_base: MessageBase):
+        self.host = host
+        self.port = port
+        self.clients = []
+        self.message_base = message_base
+ 
+    async def echo(self, websocket, path):
+        self.clients.append(websocket)
+        client_ip, client_port = websocket.remote_address
+        print(f"客户连接:{client_ip}:{client_port}")
+        while True:
+            try:
+                recv_text = await websocket.recv()
+                if recv_text:
+                    print("收到", recv_text)
+                    self.message_base.add("ws_recv", recv_text)
+
+                send_text = self.message_base.get("ws_send")
+                if send_text:
+                    print("发送", send_text)
+                    await websocket.send(send_text)
+                # data = json.loads(recv_text)
+                # device = data.get("device")
+                # if device:
+                #     self.message_base.add(device, data)
+                # else:
+                #     continue
+            except websockets.ConnectionClosed:
+                print("ConnectionClosed...")  # 链接断开
+                self.clients.remove(websocket)
+                break
+            except websockets.InvalidState:
+                print("InvalidState...")  # 无效状态
+                self.clients.remove(websocket)
+                break
+            except Exception as e:
+                print(e)
+ 
+    def connect(self):
+        print("连接成功!")
+        asyncio.set_event_loop(asyncio.new_event_loop())
+        start_server = websockets.serve(self.echo, self.host, self.port)
+        asyncio.get_event_loop().run_until_complete(start_server)
+        asyncio.get_event_loop().run_forever()
+ 
+    def run(self):
+        t = threading.Thread(target=self.connect)
+        t.start()
+        print("已启动!")
+ 
+ 
+if __name__ == '__main__':
+    mb = MessageBase()
+    ws = WebServer("192.168.6.28", 8001, mb)
+    ws.run()