feat(cd): get_tyy_data 支持并行获取维视智造SVS识别字符
新增 get_svs_char/_parse_vtfp_line,通过 VTFP 文本协议触发拍照并解析 识别字符。get_tyy_data 增加可选 svs_host/svs_port/svs_flow,采集器与 SVS 并行触发后合并为一个 dict 返回,任一失败整体抛错。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
804746fde8
commit
2f8beec37f
|
|
@ -1,6 +1,7 @@
|
||||||
import socket
|
import socket
|
||||||
from rest_framework.exceptions import ParseError
|
from rest_framework.exceptions import ParseError
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
import time
|
import time
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from apps.utils.thread import MyThread
|
from apps.utils.thread import MyThread
|
||||||
|
|
@ -8,6 +9,7 @@ import uuid
|
||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
import requests
|
import requests
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
myLogger = logging.getLogger('log')
|
myLogger = logging.getLogger('log')
|
||||||
|
|
||||||
|
|
@ -243,10 +245,85 @@ def get_tyy_data_3(*args, retry=2):
|
||||||
raise ParseError(f"未知错误: {str(e)}")
|
raise ParseError(f"未知错误: {str(e)}")
|
||||||
|
|
||||||
|
|
||||||
def get_tyy_data(*args):
|
def _parse_vtfp_line(line):
|
||||||
host, port = args[0], int(args[1])
|
"""解析 SVS 的 VTFP 应答行,返回 (识别字符, 字段列表)。
|
||||||
|
|
||||||
|
返回格式形如 ``VTFP 0 <用户自定义数据...>``:
|
||||||
|
- 第 1 个字段固定为 ``VTFP``
|
||||||
|
- 第 2 个字段为状态码(0 成功,非 0 失败,失败时后面无其他数据)
|
||||||
|
- 其余字段为用户在程序中自定义输出的数据(即识别出的字符)
|
||||||
|
|
||||||
|
分隔符按协议同时兼容空格和逗号。
|
||||||
|
"""
|
||||||
|
parts = [p for p in re.split(r"[,\s]+", line.strip()) if p != ""]
|
||||||
|
if not parts or parts[0].upper() != "VTFP":
|
||||||
|
raise ParseError(f"SVS返回格式错误-{line}")
|
||||||
|
if len(parts) < 2:
|
||||||
|
raise ParseError(f"SVS返回缺少状态码-{line}")
|
||||||
|
status = parts[1]
|
||||||
|
if status != "0":
|
||||||
|
raise ParseError(f"SVS触发失败,状态码-{status}")
|
||||||
|
fields = parts[2:] # 用户自定义数据(识别字符),可能含可选的流程单元号回显
|
||||||
|
char = " ".join(fields)
|
||||||
|
return char, fields
|
||||||
|
|
||||||
|
|
||||||
|
def get_svs_char(host, port, flow_unit=0, timeout=10):
|
||||||
|
"""连接 SVS(维视智造 VisionBank),用 VTFP 触发拍照并取回识别字符。
|
||||||
|
|
||||||
|
返回 ``{"char": 识别字符, "fields": [...], "raw": 原始应答行}``。
|
||||||
|
"""
|
||||||
|
cmd = f"VTFP {flow_unit}\r\n".encode("ascii")
|
||||||
|
try:
|
||||||
|
with socket.create_connection((host, int(port)), timeout=timeout) as sc:
|
||||||
|
sc.sendall(cmd)
|
||||||
|
buf = bytearray()
|
||||||
|
while True:
|
||||||
|
chunk = sc.recv(1024)
|
||||||
|
if not chunk:
|
||||||
|
break
|
||||||
|
buf.extend(chunk)
|
||||||
|
text = buf.decode("utf-8", errors="replace")
|
||||||
|
# VTFP 应答前可能先收到 VGIF 等行,定位到 VTFP 那一行再解析
|
||||||
|
for ln in text.split("\r\n"):
|
||||||
|
if ln.startswith("VTFP"):
|
||||||
|
char, fields = _parse_vtfp_line(ln)
|
||||||
|
return {"char": char, "fields": fields, "raw": ln}
|
||||||
|
except ParseError:
|
||||||
|
raise
|
||||||
|
except (socket.timeout, ConnectionError, OSError) as e:
|
||||||
|
raise ParseError(f"SVS连接失败-{str(e)}")
|
||||||
|
raise ParseError("SVS未返回有效数据")
|
||||||
|
|
||||||
|
|
||||||
|
def get_tyy_data(host, port, svs_host=None, svs_port=None, svs_flow=0):
|
||||||
|
"""获取采集器数据;若传入 svs_host/svs_port,并行获取 SVS 识别字符并合并返回。
|
||||||
|
|
||||||
|
采集器与 SVS 任一失败均整体抛错。
|
||||||
|
"""
|
||||||
|
port = int(port)
|
||||||
|
|
||||||
|
def fetch_tyy():
|
||||||
r = requests.get(f"http://127.0.0.1:2300?host={host}&port={port}")
|
r = requests.get(f"http://127.0.0.1:2300?host={host}&port={port}")
|
||||||
res = r.json()
|
res = r.json()
|
||||||
if "err_msg" in res:
|
if "err_msg" in res:
|
||||||
raise ParseError(res["err_msg"])
|
raise ParseError(res["err_msg"])
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
if not (svs_host and svs_port):
|
||||||
|
return fetch_tyy()
|
||||||
|
|
||||||
|
# 采集器与 SVS 并行触发,尽量贴近同一时刻;任一失败在 result() 处抛出
|
||||||
|
with ThreadPoolExecutor(max_workers=2) as ex:
|
||||||
|
tyy_future = ex.submit(fetch_tyy)
|
||||||
|
svs_future = ex.submit(get_svs_char, svs_host, svs_port, svs_flow)
|
||||||
|
res = tyy_future.result()
|
||||||
|
svs = svs_future.result()
|
||||||
|
|
||||||
|
if not isinstance(res, dict):
|
||||||
|
# 采集器返回的不是 dict 时,包成一个完整 dict
|
||||||
|
res = {"tyy": res}
|
||||||
|
res["svs_char"] = svs["char"]
|
||||||
|
res["svs_fields"] = svs["fields"]
|
||||||
|
res["svs_raw"] = svs["raw"]
|
||||||
|
return res
|
||||||
Loading…
Reference in New Issue