From ea2d12c74ef700982c30d5f52e32cb8bfe357f1c Mon Sep 17 00:00:00 2001 From: wsy182 <2392948297@qq.com> Date: Fri, 14 Nov 2025 09:55:29 +0800 Subject: [PATCH 1/6] =?UTF-8?q?feat(network):=20=E6=B7=BB=E5=8A=A0=20WITS?= =?UTF-8?q?=20=E6=95=B0=E6=8D=AE=E6=8E=A5=E6=94=B6=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=B9=B6=E6=9B=B4=E6=96=B0=20WebSocket=20=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 recive_wits.py 文件用于通过 TCP 接收 WITS 数据- 实现自动重连机制和数据黏包/拆包处理- 更新 test_websocket.py 中的 WebSocket 连接地址- 修改端口号以适配新的服务配置 --- recive_wits.py | 59 +++++++++++++++++++++++++++++++++++++++++++++++ test_websocket.py | 2 +- 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 recive_wits.py diff --git a/recive_wits.py b/recive_wits.py new file mode 100644 index 0000000..6109073 --- /dev/null +++ b/recive_wits.py @@ -0,0 +1,59 @@ +import socket +import time + +HOST = "192.168.1.41" +PORT = 9928 + +def connect(): + """建立 TCP 连接(带重试)""" + while True: + try: + print(f"Connecting to {HOST}:{PORT} ...") + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((HOST, PORT)) + s.settimeout(5) + print("Connected successfully!") + return s + except Exception as e: + print(f"Connection failed: {e}, retrying in 3s...") + time.sleep(3) + + +def receive_wits_data(sock): + """持续接收 WITS 数据(自动处理黏包/拆包)""" + buffer = "" + + while True: + try: + data = sock.recv(4096) + + # 服务器关闭 + if not data: + print("Server closed connection.") + return False + + buffer += data.decode(errors="ignore") + + # WITS 多为 \r\n 分隔 + while "\n" in buffer: + line, buffer = buffer.split("\n", 1) + line = line.strip() + if line: + print("Received:", line) + + except socket.timeout: + # 正常情况,继续接收即可 + continue + except Exception as e: + print("Error:", e) + return False + + +if __name__ == "__main__": + while True: + sock = connect() + ok = receive_wits_data(sock) + sock.close() + + print("Reconnecting in 3 seconds...") + time.sleep(3) diff --git a/test_websocket.py b/test_websocket.py index 6a6cdee..62f31ac 100644 --- a/test_websocket.py +++ b/test_websocket.py @@ -1,7 +1,7 @@ import ssl import websocket -URL = "ws://192.168.1.41:9516/ws/" # 注意走 443,不要再连 8080 了 +URL = "ws://192.168.1.8:8084/ws" # 注意走 443,不要再连 8080 了 # 如果你的 WS 路径是 /ws/,就写成上面这样;若是别的路径自己改 def on_message(ws, msg): print("收到:", msg) From 123dfc44115d6e8c980074f160a475feece472a7 Mon Sep 17 00:00:00 2001 From: wsy182 <2392948297@qq.com> Date: Mon, 17 Nov 2025 09:58:16 +0800 Subject: [PATCH 2/6] =?UTF-8?q?feat(wits):=20=E6=B7=BB=E5=8A=A0WITS?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=A8=A1=E6=8B=9F=E5=8F=91=E9=80=81=E8=84=9A?= =?UTF-8?q?=E6=9C=AC-=20=E5=AE=9E=E7=8E=B0=E9=9A=8F=E6=9C=BA=E7=94=9F?= =?UTF-8?q?=E6=88=90WITS=E5=8D=8F=E8=AE=AE=E6=95=B0=E6=8D=AE=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=20-=20=E6=94=AF=E6=8C=81=E6=95=B4=E6=95=B0=E5=92=8C?= =?UTF-8?q?=E6=B5=AE=E7=82=B9=E6=95=B0=E4=B8=A4=E7=A7=8D=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=20-=20=E9=85=8D=E7=BD=AE=E7=9B=AE=E6=A0=87?= =?UTF-8?q?=E4=B8=BB=E6=9C=BA=E5=92=8C=E7=AB=AF=E5=8F=A3=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?TCP=E8=BF=9E=E6=8E=A5=20-=20=E5=BE=AA=E7=8E=AF=E5=8F=91?= =?UTF-8?q?=E9=80=81=E9=A2=84=E5=AE=9A=E4=B9=89=E7=9A=84WITS=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E7=A0=81=20-=20=E6=B7=BB=E5=8A=A0=E5=8F=91=E9=80=81?= =?UTF-8?q?=E9=97=B4=E9=9A=94=E6=8E=A7=E5=88=B6=E5=92=8C=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E5=A4=84=E7=90=86=E6=9C=BA=E5=88=B6-=20=E6=8F=90=E4=BE=9B?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E8=A1=8C=E5=85=A5=E5=8F=A3=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- send_wtis.py | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 send_wtis.py diff --git a/send_wtis.py b/send_wtis.py new file mode 100644 index 0000000..1cbda50 --- /dev/null +++ b/send_wtis.py @@ -0,0 +1,61 @@ +import socket +import random +import time + +HOST = "192.168.1.5" # 目标地址 +PORT = 9929 # 目标端口 + +# 你给的示例里出现的所有前四位字段 +WITS_CODES = [ + "0105", + "0106", + "0108", + "0112", + "0114", + "0116", + "0118", + "0120", + "0121", + "0122" +] + +def random_value(prefix): + """ + 生成类似你收到的数据: + - 有些是整数:例如 0105 251114 + - 有些是浮点:例如 0108 37.26745 + """ + # 随机决定生成整数 or 小数 + if random.random() < 0.3: + # 生成整数(6位左右) + value = str(random.randint(100000, 999999)) + else: + # 生成浮点(保留4~5位小数) + value = f"{random.uniform(0, 500):.5f}" + + return prefix + value + + +def send_wits_data(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((HOST, PORT)) + print("Connected to target. Sending WITS data...") + + try: + while True: + for code in WITS_CODES: + msg = random_value(code) + + sock.sendall((msg + "\r\n").encode()) + print("Sent:", msg) + + time.sleep(0.2) # 每条间隔 200ms,可根据需要调整 + + except Exception as e: + print("Error:", e) + finally: + sock.close() + + +if __name__ == "__main__": + send_wits_data() From 167d96b6ba24b89c168a61db4b6744edb267cfe8 Mon Sep 17 00:00:00 2001 From: wsy182 <2392948297@qq.com> Date: Mon, 24 Nov 2025 10:23:46 +0800 Subject: [PATCH 3/6] =?UTF-8?q?feat(wits):=20=E6=B7=BB=E5=8A=A0WITS?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=A8=A1=E6=8B=9F=E5=8F=91=E9=80=81=E8=84=9A?= =?UTF-8?q?=E6=9C=AC-=20=E5=AE=9E=E7=8E=B0=E9=9A=8F=E6=9C=BA=E7=94=9F?= =?UTF-8?q?=E6=88=90WITS=E5=8D=8F=E8=AE=AE=E6=95=B0=E6=8D=AE=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=20-=20=E6=94=AF=E6=8C=81=E6=95=B4=E6=95=B0=E5=92=8C?= =?UTF-8?q?=E6=B5=AE=E7=82=B9=E6=95=B0=E4=B8=A4=E7=A7=8D=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=20-=20=E9=85=8D=E7=BD=AE=E7=9B=AE=E6=A0=87?= =?UTF-8?q?=E4=B8=BB=E6=9C=BA=E5=92=8C=E7=AB=AF=E5=8F=A3=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?TCP=E8=BF=9E=E6=8E=A5=20-=20=E5=BE=AA=E7=8E=AF=E5=8F=91?= =?UTF-8?q?=E9=80=81=E9=A2=84=E5=AE=9A=E4=B9=89=E7=9A=84WITS=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E7=A0=81=20-=20=E6=B7=BB=E5=8A=A0=E5=8F=91=E9=80=81?= =?UTF-8?q?=E9=97=B4=E9=9A=94=E6=8E=A7=E5=88=B6=E5=92=8C=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E5=A4=84=E7=90=86=E6=9C=BA=E5=88=B6-=20=E6=8F=90=E4=BE=9B?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E8=A1=8C=E5=85=A5=E5=8F=A3=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test_websocket.py | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/test_websocket.py b/test_websocket.py index 62f31ac..b534ab8 100644 --- a/test_websocket.py +++ b/test_websocket.py @@ -1,28 +1,20 @@ import ssl import websocket -URL = "ws://192.168.1.8:8084/ws" # 注意走 443,不要再连 8080 了 -# 如果你的 WS 路径是 /ws/,就写成上面这样;若是别的路径自己改 +# URL = "ws://192.168.1.202:9100/well-tool-test-system/ws/" -def on_message(ws, msg): print("收到:", msg) -def on_error(ws, err): print("错误:", err) -def on_close(ws, code, reason): print("关闭:", code, reason) -def on_open(ws): - print("连接成功") - ws.send("hello server mac") +URL = "wss://192.168.1.87/ws/" +ws = websocket.WebSocketApp( + URL, + on_open=lambda ws: print("连接成功"), + on_message=lambda ws, msg: print("消息:", msg), + on_error=lambda ws, err: print("错误:", err), + on_close=lambda ws, code, reason: print("关闭:", code, reason), +) -if __name__ == "__main__": - websocket.enableTrace(True) - ws = websocket.WebSocketApp( - URL, - on_open=on_open, - on_message=on_message, - on_error=on_error, - on_close=on_close, - # header=["Origin: https://192.168.1.3"] # 如后端不校验 Origin 可删 - header=[] # 如后端不校验 Origin 可删 - ) - ws.run_forever(sslopt={ +ws.run_forever( + sslopt={ "cert_reqs": ssl.CERT_NONE, "check_hostname": False, - }) \ No newline at end of file + } +) From 8c4e48f0ef3da7e60bcf7f7a49bda633f873e336 Mon Sep 17 00:00:00 2001 From: wsy182 <2392948297@qq.com> Date: Mon, 24 Nov 2025 11:12:25 +0800 Subject: [PATCH 4/6] =?UTF-8?q?feat(utils):=20=E6=B7=BB=E5=8A=A0MD5?= =?UTF-8?q?=E7=AD=BE=E5=90=8D=E7=94=9F=E6=88=90=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现了基于Java规则的MD5签名计算方法 - 支持参数映射排序与URL编码处理 - 添加了空格去除和特殊字符处理逻辑 - 集成了时间戳、随机字符串生成功能 - 提供完整的curl请求示例输出 - 支持复杂对象序列化为JSON请求体 - 实现了多层级参数拼接与签名验证 --- generateSign.py | 97 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 generateSign.py diff --git a/generateSign.py b/generateSign.py new file mode 100644 index 0000000..7f71b5c --- /dev/null +++ b/generateSign.py @@ -0,0 +1,97 @@ +import json +import time +import uuid +import urllib.parse +import hashlib + + +# =================== MD5签名计算 =================== +def calculate_md5(param, secret_key): + # Java 规则:MD5(secretKey + param) + return hashlib.md5((secret_key + param).encode('utf-8')).hexdigest() + + +def create_sign(param_map, secret_key): + sorted_keys = sorted(param_map.keys()) + parts = [] + + for key in sorted_keys: + val = param_map[key] + + if val is None: + parts.append(f"{key}=") + continue + + if isinstance(val, list): + for item in val: + encoded_val = urllib.parse.quote(str(item), encoding='utf-8') + parts.append(f"{key}={encoded_val}") + continue + + val_str = str(val) + + if key != "REQUEST_BODY_CONTENT": + val_str = urllib.parse.quote(val_str, encoding='utf-8') + + parts.append(f"{key}={val_str}") + + param = "&".join(parts) + + # Java逻辑: .replaceAll("\\s", "") + param = "".join(param.split()) + + print("参数明文 param:", param) + + sign = calculate_md5(param, secret_key) + print("生成签名 sign:", sign) + + return sign + + +# =================== Python 主程序 =================== +if __name__ == '__main__': + platform_ifc_system_code = "9f5bbb73710c4a6a8923c278ffa00d50" + + systemCode = "57395d2bc668496c9c57d8f2b19bd516" + + appId = "57395d2bc668496c9c57d8f2b19bd516" + secret_key = "lC4&NvBQcnvgH@i+io=k#K3HO5aCiuHD" + + ts = str(int(time.time() * 1000)) + randomString = uuid.uuid4().hex + + selector = { + "searchKeys": ["b353614a47e2425a8a8885d270267407", "3DC1B33E1B5B431E99FA163BF9E86E6A", "9cb864213c6f48ceaf90e98e7ca375e9"], + "userType": "0", + "hasCascade": True + } + + request_body_json = json.dumps(selector, separators=(',', ':')) + + param_map = { + "systemCode": systemCode, + "timestamp": ts, + "nonce": randomString, + "REQUEST_BODY_CONTENT": request_body_json + } + + sign = create_sign(param_map, secret_key) + + ifcUrl = "https://dpc.cet.cnooc/prod-api/cenertech-interface-center/IFC2" + apiPath = "/userListByDeptSearch" + + fullUrl = f"{ifcUrl}/{platform_ifc_system_code}{apiPath}" + + curl = f""" +curl -X POST "{fullUrl}" \\ + -H "Content-Type: application/json" \\ + -H "systemCode: {systemCode}" \\ + -H "timestamp: {ts}" \\ + -H "nonce: {randomString}" \\ + -H "sign: {sign}" \\ + -H "App-Id: {appId}" \\ + -d '{request_body_json}' +""" + + print("\n===== 最终 curl 请求 =====\n") + print(curl) From bab29150abbed6cc40c44f4460934d6bf9a54055 Mon Sep 17 00:00:00 2001 From: wsy182 <2392948297@qq.com> Date: Mon, 24 Nov 2025 16:01:44 +0800 Subject: [PATCH 5/6] =?UTF-8?q?fix(generateSign):=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E9=85=8D=E7=BD=AE=E5=92=8C=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E9=94=AE=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改 systemCode 和 appId 为新的标识符 - 更新 secret_key 以增强安全性 - 扩展 searchKeys 列表,增加多个新的搜索键值 - 调整 searchKeys 数组格式以提高可读性 --- generateSign.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/generateSign.py b/generateSign.py index 7f71b5c..37833c9 100644 --- a/generateSign.py +++ b/generateSign.py @@ -52,16 +52,19 @@ def create_sign(param_map, secret_key): if __name__ == '__main__': platform_ifc_system_code = "9f5bbb73710c4a6a8923c278ffa00d50" - systemCode = "57395d2bc668496c9c57d8f2b19bd516" + systemCode = "114ee506d80a479c8d106c4748d2d029" - appId = "57395d2bc668496c9c57d8f2b19bd516" - secret_key = "lC4&NvBQcnvgH@i+io=k#K3HO5aCiuHD" + appId = "114ee506d80a479c8d106c4748d2d029" + secret_key = "31z&h7IhG6gba@ptz=F8FSr+C5-*OHy6" ts = str(int(time.time() * 1000)) randomString = uuid.uuid4().hex selector = { - "searchKeys": ["b353614a47e2425a8a8885d270267407", "3DC1B33E1B5B431E99FA163BF9E86E6A", "9cb864213c6f48ceaf90e98e7ca375e9"], + "searchKeys": ["4555f083accb42c699fa222f85508404", "9bc5618ba6fa485aa2b3f88e37d34238", + "cf0e557530884526bd693341928885c6", "ffc7d0c4018b4c8cad0e8c70aae3698c", + "71985162af674df0bd1ca75d3e6e1984", "a704938ab033447ead129f1e7821d685", + "b761bd9a156a485ebed9a21c714b9807", "3fd5dd6c401b46e7b1178badb6723491", "13347", ], "userType": "0", "hasCascade": True } From c1fa662552b97ca738b5113421d7f095ae8c8e05 Mon Sep 17 00:00:00 2001 From: wsy182 <2392948297@qq.com> Date: Tue, 25 Nov 2025 11:06:04 +0800 Subject: [PATCH 6/6] =?UTF-8?q?feat(sign):=20=E6=94=AF=E6=8C=81=E5=A4=9A?= =?UTF-8?q?=E7=8E=AF=E5=A2=83=E9=85=8D=E7=BD=AE=E5=92=8C=E7=AD=BE=E5=90=8D?= =?UTF-8?q?=E7=94=9F=E6=88=90=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增开发环境和生产环境的配置管理 - 移除参数中的空格替换逻辑 - 更新默认搜索键和用户类型 - 支持通过命令行参数切换运行环境 - 自动构建完整请求URL用于curl测试 - 添加环境选择提示和错误处理机制 --- generateSign.py | 52 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/generateSign.py b/generateSign.py index 37833c9..bd216de 100644 --- a/generateSign.py +++ b/generateSign.py @@ -3,11 +3,30 @@ import time import uuid import urllib.parse import hashlib +import sys + + +# =================== 运行环境配置 =================== +CONFIG = { + "dev": { + "platform_ifc_system_code": "ef5b17caff6e4da19d6af82d539e894d", + "systemCode": "57395d2bc668496c9c57d8f2b19bd516", + "appId": "57395d2bc668496c9c57d8f2b19bd516", + "secret_key": "KMFHKo1Uzrl&MWXorbQIT&C$Qea$uQOY", + "ifcUrl": "http://192.168.1.202:8083/dev-api/cenertech-interface-center/IFC2" + }, + "prod": { + "platform_ifc_system_code": "57395d2bc668496c9c57d8f2b19bd516", + "systemCode": "57395d2bc668496c9c57d8f2b19bd516", + "appId": "57395d2bc668496c9c57d8f2b19bd516", + "secret_key": "opS=K9Parlf&p+JxBOQD2q+zNZa+uXEE", + "ifcUrl": "https://dpc.cet.cnooc/prod-api/cenertech-interface-center/IFC2" + } +} # =================== MD5签名计算 =================== def calculate_md5(param, secret_key): - # Java 规则:MD5(secretKey + param) return hashlib.md5((secret_key + param).encode('utf-8')).hexdigest() @@ -36,8 +55,6 @@ def create_sign(param_map, secret_key): parts.append(f"{key}={val_str}") param = "&".join(parts) - - # Java逻辑: .replaceAll("\\s", "") param = "".join(param.split()) print("参数明文 param:", param) @@ -50,22 +67,31 @@ def create_sign(param_map, secret_key): # =================== Python 主程序 =================== if __name__ == '__main__': - platform_ifc_system_code = "9f5bbb73710c4a6a8923c278ffa00d50" - systemCode = "114ee506d80a479c8d106c4748d2d029" + # 选择 dev / prod + env = "dev" + if len(sys.argv) > 1: + env = sys.argv[1] + print(f"\n=== 当前环境:{env} ===\n") - appId = "114ee506d80a479c8d106c4748d2d029" - secret_key = "31z&h7IhG6gba@ptz=F8FSr+C5-*OHy6" + if env not in CONFIG: + print("❗ 错误:请使用 python sign.py dev 或 python sign.py prod") + sys.exit(1) + + cfg = CONFIG[env] + + systemCode = cfg["systemCode"] + appId = cfg["appId"] + secret_key = cfg["secret_key"] + platform_ifc_system_code = cfg["platform_ifc_system_code"] + ifcUrl = cfg["ifcUrl"] ts = str(int(time.time() * 1000)) randomString = uuid.uuid4().hex selector = { - "searchKeys": ["4555f083accb42c699fa222f85508404", "9bc5618ba6fa485aa2b3f88e37d34238", - "cf0e557530884526bd693341928885c6", "ffc7d0c4018b4c8cad0e8c70aae3698c", - "71985162af674df0bd1ca75d3e6e1984", "a704938ab033447ead129f1e7821d685", - "b761bd9a156a485ebed9a21c714b9807", "3fd5dd6c401b46e7b1178badb6723491", "13347", ], - "userType": "0", + "searchKeys": ["9cb864213c6f48ceaf90e98e7ca375e9","3DC1B33E1B5B431E99FA163BF9E86E6A","13336","b353614a47e2425a8a8885d270267407"], + "userType": "1", "hasCascade": True } @@ -80,9 +106,7 @@ if __name__ == '__main__': sign = create_sign(param_map, secret_key) - ifcUrl = "https://dpc.cet.cnooc/prod-api/cenertech-interface-center/IFC2" apiPath = "/userListByDeptSearch" - fullUrl = f"{ifcUrl}/{platform_ifc_system_code}{apiPath}" curl = f"""