From 00fe92c90ea22f3c765e50349eb13bd7ebddc95e Mon Sep 17 00:00:00 2001 From: root <13910913995@163.com> Date: Fri, 26 Jun 2026 17:48:42 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E4=BA=8B=E4=BB=B6?= =?UTF-8?q?=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/adapters/device_client.py | 148 ++++++++++++-- backend/app/api/routes/alarms.py | 8 +- backend/app/db/sqlite.py | 71 ++++++- backend/app/repositories/alarm_repo.py | 34 ++-- backend/app/schemas/platform.py | 10 +- backend/app/services/platform_service.py | 19 +- backend/config/device.json | 4 +- backend/scripts/manual_rtu_read.py | 5 +- backend/tests/test_api.py | 52 +++-- backend/tests/test_modbus_client.py | 25 ++- document/gui.h | 207 -------------------- document/{gui_1.h => gui_2.h} | 70 ++++--- document/~$界面modbus协议寄存器定义v1.1.doc | Bin 162 -> 0 bytes document/显示界面modbus协议寄存器定义.md | 75 ++++--- 14 files changed, 391 insertions(+), 337 deletions(-) delete mode 100644 document/gui.h rename document/{gui_1.h => gui_2.h} (75%) delete mode 100644 document/~$界面modbus协议寄存器定义v1.1.doc diff --git a/backend/app/adapters/device_client.py b/backend/app/adapters/device_client.py index 1b0646a..15ac20c 100644 --- a/backend/app/adapters/device_client.py +++ b/backend/app/adapters/device_client.py @@ -15,11 +15,13 @@ from app.schemas.platform import ( DeviceStatus, LineAlarmSettingIn, LineData, + NetConfigItem, RealtimeData, SystemConfigIn, SwitchControlIn, TimeSyncConfigIn, ValueGroup, + UartConfigItem, ) @@ -56,6 +58,10 @@ class DeviceClient(ABC): def read_time_sync_config(self) -> Dict[str, str]: raise NotImplementedError + @abstractmethod + def read_net_config(self, nic: str) -> Dict[str, Any]: + raise NotImplementedError + @abstractmethod def send_device_config(self, payload: DeviceConfigIn) -> Dict[str, Any]: raise NotImplementedError @@ -80,6 +86,10 @@ class DeviceClient(ABC): def send_time_sync_config(self, payload: TimeSyncConfigIn) -> Dict[str, Any]: raise NotImplementedError + @abstractmethod + def send_net_config(self, payload: NetConfigItem) -> Dict[str, Any]: + raise NotImplementedError + @abstractmethod def send_switch_control(self, payload: SwitchControlIn) -> Dict[str, Any]: raise NotImplementedError @@ -131,6 +141,9 @@ AO_CHANNEL_START_REGISTER = 0x0072 AI_ALARM_START_REGISTER = 0x0090 ANALOG_BLOCK_REGISTER_COUNT = 30 DO_STATE_REGISTER = 0x00A6 +NET_CONFIG_START_REGISTER = 0x00AE +NET_CONFIG_REGISTER_COUNT = 28 +NET_ITEM_REGISTER_COUNT = 7 TIME_SYNC_START_REGISTER = 0x0000 @@ -329,6 +342,12 @@ class MockDeviceClient(DeviceClient): } ] self._system_config = {"time_sync": "2026-05-25 12:34:56", "brightness": 80, "screen_saver": 60} + self._net_config = [ + {"nic": "网卡一", "ip": "192.168.1.10", "mask": "255.255.255.0", "gateway": "192.168.1.1", "protocol": "Modbus TCP"}, + {"nic": "网卡二", "ip": "192.168.2.10", "mask": "255.255.255.0", "gateway": "192.168.2.1", "protocol": "Modbus TCP"}, + {"nic": "网卡三", "ip": "", "mask": "", "gateway": "", "protocol": ""}, + {"nic": "网卡四", "ip": "", "mask": "", "gateway": "", "protocol": ""}, + ] def _value_group(self, base_u: float, base_i: float, base_p: float) -> ValueGroup: delta = self._tick % 5 @@ -386,14 +405,15 @@ class MockDeviceClient(DeviceClient): def read_alarm_events(self) -> List[AlarmEvent]: if self._tick % 6 != 0: return [] + line_code = (self._tick // 6) % 4 + 1 return [ AlarmEvent( - alarm_type="line_alarm", - time=datetime.now(), - no=str((self._tick // 6) % 4 + 1), - type="电压", - content="模拟告警:线路电压越限", - level="中", + event_time=datetime.now(), + line_code=line_code, + event_type=3, + event_code=1, + event_value=220, + content=f"线路{line_code}电压越限,动作值=220", ) ] @@ -428,6 +448,12 @@ class MockDeviceClient(DeviceClient): def read_time_sync_config(self) -> Dict[str, str]: return {"time_sync": str(self._system_config.get("time_sync", ""))} + def read_net_config(self, nic: str) -> Dict[str, Any]: + for item in self._net_config: + if item["nic"] == nic: + return dict(item) + return {"nic": nic, "ip": "", "mask": "", "gateway": "", "protocol": ""} + def send_device_config(self, payload: DeviceConfigIn) -> Dict[str, Any]: return {"send_status": "成功", "target": "device", "items": len(payload.net) + len(payload.uart)} @@ -455,6 +481,17 @@ class MockDeviceClient(DeviceClient): self._system_config["time_sync"] = payload.time_sync return {"send_status": "成功", "target": "time_sync", "time_sync": payload.time_sync} + def send_net_config(self, payload: NetConfigItem) -> Dict[str, Any]: + updated = False + for index, item in enumerate(self._net_config): + if item["nic"] == payload.nic: + self._net_config[index] = payload.model_dump() + updated = True + break + if not updated: + self._net_config.append(payload.model_dump()) + return {"send_status": "成功", "target": "net", "nic": payload.nic, "modbus_written": {"net_registers": NET_CONFIG_REGISTER_COUNT}} + def send_switch_control(self, payload: SwitchControlIn) -> Dict[str, Any]: action_text = "合" if payload.action == 1 else "分" return {"control_status": f"执行成功: 开关{payload.ch}{action_text}"} @@ -607,6 +644,67 @@ class CDeviceClient(DeviceClient): def _protocol_code(self, protocol: str) -> int: return 1 if "MODBUS" in _normalize_text(protocol) else 0 + def _decode_net_protocol(self, code: int) -> str: + return "Modbus TCP" if int(code) == 1 else "" + + def _net_nic_names(self) -> List[str]: + return ["网卡一", "网卡二", "网卡三", "网卡四"] + + def _net_index(self, nic: str) -> int: + normalized = nic.strip() + nic_names = self._net_nic_names() + if normalized in nic_names: + return nic_names.index(normalized) + digits = "".join(character for character in normalized if character.isdigit()) + if digits: + index = max(1, min(4, int(digits))) - 1 + return index + chinese_map = {"一": 0, "二": 1, "三": 2, "四": 3, "1": 0, "2": 1, "3": 2, "4": 3} + for key, index in chinese_map.items(): + if key in normalized: + return index + return 0 + + def _ip_text_to_bytes(self, value: str) -> bytes: + parts = [part.strip() for part in str(value).split(".") if part.strip() != ""] + numbers = [] + for part in parts[:4]: + try: + numbers.append(max(0, min(255, int(part)))) + except ValueError: + numbers.append(0) + while len(numbers) < 4: + numbers.append(0) + return bytes(numbers[:4]) + + def _ip_bytes_to_text(self, value: bytes) -> str: + if len(value) < 4: + value = value + b"\x00" * (4 - len(value)) + if all(int(part) == 0 for part in value[:4]): + return "" + return ".".join(str(int(part)) for part in value[:4]) + + def _decode_net_item(self, nic: str, payload: bytes) -> Dict[str, Any]: + chunk = payload[:14] + b"\x00" * max(0, 14 - len(payload)) + return { + "nic": nic, + "ip": self._ip_bytes_to_text(chunk[0:4]), + "mask": self._ip_bytes_to_text(chunk[4:8]), + "gateway": self._ip_bytes_to_text(chunk[8:12]), + "protocol": self._decode_net_protocol(chunk[12]), + } + + def _encode_net_item(self, item: NetConfigItem) -> bytes: + return b"".join( + [ + self._ip_text_to_bytes(item.ip), + self._ip_text_to_bytes(item.mask), + self._ip_text_to_bytes(item.gateway), + bytes([self._protocol_code(item.protocol)]), + b"\x00", + ] + ) + def _line_rule_payload(self, limit: Any, delay: Any, output_node: str, enabled: bool) -> bytes: return bytes( [ @@ -789,12 +887,12 @@ class CDeviceClient(DeviceClient): line_text = "装置" if line_no == 0 else f"线路{line_no}" alarms.append( AlarmEvent( - alarm_type="modbus_event", - time=event_time, - no="" if line_no == 0 else str(line_no), - type=event_name, + event_time=event_time, + line_code=line_no, + event_type=event_type, + event_code=event_code, + event_value=action_value, content=f"{line_text}{event_name},动作值={action_value}", - level=self._decode_event_level(event_type), ) ) @@ -817,9 +915,6 @@ class CDeviceClient(DeviceClient): return f"AI越限{event_code}" return f"事件{event_type}-{event_code}" - def _decode_event_level(self, event_type: int) -> str: - return {1: "严重", 2: "中", 3: "中", 4: "一般", 5: "一般"}.get(event_type, "一般") - def read_channel_config(self) -> Dict[str, Any]: ai_registers = self.transport.read_holding_registers(AI_CHANNEL_START_REGISTER, ANALOG_BLOCK_REGISTER_COUNT) ao_registers = self.transport.read_holding_registers(AO_CHANNEL_START_REGISTER, ANALOG_BLOCK_REGISTER_COUNT) @@ -849,6 +944,13 @@ class CDeviceClient(DeviceClient): timestamp = int(registers[0]) | (int(registers[1]) << 16) return {"time_sync": datetime.fromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")} + def read_net_config(self, nic: str) -> Dict[str, Any]: + registers = self.transport.read_holding_registers(NET_CONFIG_START_REGISTER, NET_CONFIG_REGISTER_COUNT) + payload = self._registers_to_bytes(registers) + index = self._net_index(nic) + start = index * 14 + return self._decode_net_item(self._net_nic_names()[index], payload[start : start + 14]) + def send_device_config(self, payload: DeviceConfigIn) -> Dict[str, Any]: uart_map = {item.port.upper(): item for item in payload.uart} raw = bytearray() @@ -979,6 +1081,24 @@ class CDeviceClient(DeviceClient): "modbus_written": {"time_registers": len(registers)}, } + def send_net_config(self, payload: NetConfigItem) -> Dict[str, Any]: + registers = self.transport.read_holding_registers(NET_CONFIG_START_REGISTER, NET_CONFIG_REGISTER_COUNT) + raw = bytearray(self._registers_to_bytes(registers)) + expected_size = NET_CONFIG_REGISTER_COUNT * 2 + if len(raw) < expected_size: + raw.extend(b"\x00" * (expected_size - len(raw))) + index = self._net_index(payload.nic) + start = index * 14 + raw[start : start + 14] = self._encode_net_item(payload) + encoded = self._bytes_to_registers(bytes(raw)) + self.transport.write_registers(NET_CONFIG_START_REGISTER, encoded) + return { + "send_status": "成功", + "target": "net", + "nic": self._net_nic_names()[index], + "modbus_written": {"start_register": NET_CONFIG_START_REGISTER, "registers": len(encoded)}, + } + def send_switch_control(self, payload: SwitchControlIn) -> Dict[str, Any]: coil_address = max(0, payload.ch - 1) coil_value = bool(payload.action) diff --git a/backend/app/api/routes/alarms.py b/backend/app/api/routes/alarms.py index 34eb659..7ef71b5 100644 --- a/backend/app/api/routes/alarms.py +++ b/backend/app/api/routes/alarms.py @@ -14,16 +14,16 @@ router = APIRouter(prefix="/alarm", tags=["alarm"]) def list_alarms( page: int = Query(default=1, ge=1), size: int = Query(default=20, ge=1, le=100), - no: Optional[str] = Query(default=None), - type: Optional[str] = Query(default=None), + line_code: Optional[int] = Query(default=None, ge=0), + event_type: Optional[int] = Query(default=None, ge=0), start_time: Optional[datetime] = Query(default=None), end_time: Optional[datetime] = Query(default=None), ) -> dict: data = platform_service.list_alarms( page=page, size=size, - no=no, - alarm_type=type, + line_code=line_code, + event_type=event_type, start_time=start_time, end_time=end_time, ) diff --git a/backend/app/db/sqlite.py b/backend/app/db/sqlite.py index faf0b25..0665bc9 100644 --- a/backend/app/db/sqlite.py +++ b/backend/app/db/sqlite.py @@ -15,19 +15,78 @@ def get_connection(db_path: Optional[Path] = None) -> sqlite3.Connection: return connection +def _alarm_event_columns(connection: sqlite3.Connection) -> list[str]: + rows = connection.execute("PRAGMA table_info(alarm_event)").fetchall() + return [str(row["name"]) for row in rows] + + +def _ensure_alarm_event_schema(connection: sqlite3.Connection) -> None: + expected_columns = [ + "id", + "event_time", + "line_code", + "event_type", + "event_code", + "event_value", + "content", + ] + current_columns = _alarm_event_columns(connection) + if not current_columns: + return + if current_columns == expected_columns: + return + + connection.execute("ALTER TABLE alarm_event RENAME TO alarm_event_legacy") + connection.execute( + """ + CREATE TABLE alarm_event ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + event_time TEXT NOT NULL, + line_code INTEGER NOT NULL DEFAULT 0, + event_type INTEGER NOT NULL DEFAULT 0, + event_code INTEGER NOT NULL DEFAULT 0, + event_value INTEGER NOT NULL DEFAULT 0, + content TEXT NOT NULL + ) + """ + ) + + legacy_columns = set(current_columns) + if {"time", "content"}.issubset(legacy_columns): + connection.execute( + """ + INSERT INTO alarm_event (event_time, line_code, event_type, event_code, event_value, content) + SELECT + time, + CASE + WHEN typeof(no) IN ('integer', 'real') THEN CAST(no AS INTEGER) + WHEN trim(COALESCE(no, '')) GLOB '[0-9]*' AND trim(COALESCE(no, '')) <> '' THEN CAST(trim(no) AS INTEGER) + ELSE 0 + END, + 0, + 0, + 0, + content + FROM alarm_event_legacy + """ + ) + connection.execute("DROP TABLE alarm_event_legacy") + + def init_db() -> None: with get_connection() as connection: connection.execute( """ CREATE TABLE IF NOT EXISTS alarm_event ( id INTEGER PRIMARY KEY AUTOINCREMENT, - alarm_type TEXT NOT NULL, - time TEXT NOT NULL, - no TEXT, - type TEXT, - content TEXT NOT NULL, - level TEXT NOT NULL + event_time TEXT NOT NULL, + line_code INTEGER NOT NULL DEFAULT 0, + event_type INTEGER NOT NULL DEFAULT 0, + event_code INTEGER NOT NULL DEFAULT 0, + event_value INTEGER NOT NULL DEFAULT 0, + content TEXT NOT NULL ) """ ) + _ensure_alarm_event_schema(connection) connection.commit() diff --git a/backend/app/repositories/alarm_repo.py b/backend/app/repositories/alarm_repo.py index 7f7b369..0b93a1f 100644 --- a/backend/app/repositories/alarm_repo.py +++ b/backend/app/repositories/alarm_repo.py @@ -12,16 +12,16 @@ class AlarmRepository: with get_connection() as connection: cursor = connection.execute( """ - INSERT INTO alarm_event (alarm_type, time, no, type, content, level) + INSERT INTO alarm_event (event_time, line_code, event_type, event_code, event_value, content) VALUES (?, ?, ?, ?, ?, ?) """, ( - alarm.alarm_type, - alarm.time.isoformat(sep=" ", timespec="seconds"), - alarm.no, - alarm.type, + alarm.event_time.isoformat(sep=" ", timespec="seconds"), + alarm.line_code, + alarm.event_type, + alarm.event_code, + alarm.event_value, alarm.content, - alarm.level, ), ) connection.commit() @@ -31,8 +31,8 @@ class AlarmRepository: self, page: int = 1, size: int = 20, - no: str = "", - alarm_type: str = "", + line_code: Optional[int] = None, + event_type: Optional[int] = None, start_time: Optional[datetime] = None, end_time: Optional[datetime] = None, ) -> List[Dict[str, Any]]: @@ -40,17 +40,17 @@ class AlarmRepository: conditions = [] params: List[Any] = [] - if no: - conditions.append("no = ?") - params.append(no) - if alarm_type: - conditions.append("type = ?") - params.append(alarm_type) + if line_code is not None: + conditions.append("line_code = ?") + params.append(line_code) + if event_type is not None: + conditions.append("event_type = ?") + params.append(event_type) if start_time is not None: - conditions.append("time >= ?") + conditions.append("event_time >= ?") params.append(start_time.isoformat(sep=" ", timespec="seconds")) if end_time is not None: - conditions.append("time <= ?") + conditions.append("event_time <= ?") params.append(end_time.isoformat(sep=" ", timespec="seconds")) where_clause = "" @@ -60,7 +60,7 @@ class AlarmRepository: with get_connection() as connection: cursor = connection.execute( f""" - SELECT id, alarm_type, time, no, type, content, level + SELECT id, event_time, line_code, event_type, event_code, event_value, content FROM alarm_event {where_clause} ORDER BY id DESC diff --git a/backend/app/schemas/platform.py b/backend/app/schemas/platform.py index c42fbf8..95571f1 100644 --- a/backend/app/schemas/platform.py +++ b/backend/app/schemas/platform.py @@ -165,12 +165,12 @@ class DevicePasswordUpdateIn(BaseModel): class AlarmEvent(BaseModel): id: Optional[int] = None - alarm_type: str - time: datetime - no: str = "" - type: str = "" + event_time: datetime + line_code: int = 0 + event_type: int = 0 + event_code: int = 0 + event_value: int = 0 content: str - level: str = "一般" class SwitchControlIn(BaseModel): diff --git a/backend/app/services/platform_service.py b/backend/app/services/platform_service.py index 5574b78..7435b8c 100644 --- a/backend/app/services/platform_service.py +++ b/backend/app/services/platform_service.py @@ -350,17 +350,10 @@ class PlatformService: return data def get_net_config(self, nic: str) -> Dict[str, Any]: - device_config = self._load_device_config_with_defaults() - item = self._find_keyed_item(device_config["net"], "nic", nic) - if item is not None: - return item - return {"nic": nic, "ip": "", "mask": "", "gateway": "", "protocol": ""} + return self.device_client.read_net_config(nic) def save_net_config(self, payload: NetConfigItem) -> Dict[str, Any]: - device_config = self._load_device_config_with_defaults() - device_config["net"] = self._upsert_keyed_item(device_config["net"], payload.model_dump(), "nic") - path = self.config_repo.write_device_config(device_config) - return {"save_path": f"/config/{path.name}", "target": "net", "nic": payload.nic, "send_status": "成功"} + return self.device_client.send_net_config(payload) def get_uart_config(self, port: str) -> Dict[str, Any]: device_config = self._load_device_config_with_defaults() @@ -456,16 +449,16 @@ class PlatformService: self, page: int, size: int, - no: str = "", - alarm_type: str = "", + line_code: Optional[int] = None, + event_type: Optional[int] = None, start_time: Optional[datetime] = None, end_time: Optional[datetime] = None, ) -> List[Dict[str, Any]]: return self.alarm_repo.list_alarms( page=page, size=size, - no=no, - alarm_type=alarm_type, + line_code=line_code, + event_type=event_type, start_time=start_time, end_time=end_time, ) diff --git a/backend/config/device.json b/backend/config/device.json index e8a696f..4f224db 100644 --- a/backend/config/device.json +++ b/backend/config/device.json @@ -13,7 +13,7 @@ "net": [ { "nic": "网卡一", - "ip": "192.168.1.10", + "ip": "192.168.1.11", "mask": "255.255.255.1", "gateway": "192.168.1.1", "protocol": "Modbus TCP" @@ -29,7 +29,7 @@ "uart": [ { "port": "COM1", - "baud": 19200, + "baud": 9600, "parity": "NONE", "data_bits": 8, "stop_bits": 1, diff --git a/backend/scripts/manual_rtu_read.py b/backend/scripts/manual_rtu_read.py index c1f1d3d..63d52f8 100644 --- a/backend/scripts/manual_rtu_read.py +++ b/backend/scripts/manual_rtu_read.py @@ -74,8 +74,9 @@ def print_alarm_summary(client: CDeviceClient) -> None: else: for alarm in alarms: print( - f"time={alarm.time}, no={alarm.no}, type={alarm.type}, " - f"level={alarm.level}, content={alarm.content}" + f"event_time={alarm.event_time}, line_code={alarm.line_code}, " + f"event_type={alarm.event_type}, event_code={alarm.event_code}, " + f"event_value={alarm.event_value}, content={alarm.content}" ) print() diff --git a/backend/tests/test_api.py b/backend/tests/test_api.py index 5a19a74..aeb5158 100644 --- a/backend/tests/test_api.py +++ b/backend/tests/test_api.py @@ -90,22 +90,22 @@ def test_alarm_list_filters(tmp_path) -> None: repo = AlarmRepository() repo.save_alarm( AlarmEvent( - alarm_type="保护报警", - time=datetime(2026, 5, 16, 10, 0, 0), - no="L1", - type="voltage", + event_time=datetime(2026, 5, 16, 10, 0, 0), + line_code=1, + event_type=3, + event_code=1, + event_value=230, content="A 相过压", - level="严重", ) ) repo.save_alarm( AlarmEvent( - alarm_type="状态报警", - time=datetime(2026, 5, 16, 11, 0, 0), - no="L2", - type="current", + event_time=datetime(2026, 5, 16, 11, 0, 0), + line_code=2, + event_type=5, + event_code=2, + event_value=456, content="B 相过流", - level="一般", ) ) @@ -114,8 +114,8 @@ def test_alarm_list_filters(tmp_path) -> None: params={ "page": 1, "size": 20, - "no": "L2", - "type": "current", + "line_code": 2, + "event_type": 5, "start_time": "2026-05-16T10:30:00", "end_time": "2026-05-16T11:30:00", }, @@ -125,8 +125,10 @@ def test_alarm_list_filters(tmp_path) -> None: payload = response.json() assert payload["code"] == 200 assert len(payload["data"]) == 1 - assert payload["data"][0]["no"] == "L2" - assert payload["data"][0]["type"] == "current" + assert payload["data"][0]["line_code"] == 2 + assert payload["data"][0]["event_type"] == 5 + assert payload["data"][0]["event_code"] == 2 + assert payload["data"][0]["event_value"] == 456 def test_config_query_endpoints(tmp_path) -> None: @@ -181,6 +183,7 @@ def test_config_query_endpoints(tmp_path) -> None: assert device_response.json()["data"]["password"] == "" assert device_response.json()["data"]["net"][0]["ip"] == "192.168.1.10" assert net_response.json()["data"]["nic"] == "网卡一" + assert net_response.json()["data"]["ip"] == "192.168.1.10" assert uart_response.json()["data"]["port"] == "COM1" assert channel_response.json()["data"]["ai_channel"][0]["singal_type"] == "4-20mA" assert line_alarm_response.json()["data"]["line_no"] == 1 @@ -239,10 +242,16 @@ def test_protocol_config_endpoints_read_and_write_device_only(tmp_path) -> None: headers=headers, json={"time_sync": "2026-05-25 13:00:00", "brightness": 88, "screen_saver": 90}, ) + net_response = client.post( + "/api/config/device/net", + headers=headers, + json={"nic": "网卡二", "ip": "172.16.2.10", "mask": "255.255.255.0", "gateway": "172.16.2.1", "protocol": "Modbus TCP"}, + ) query_channel = client.get("/api/config/channel", headers=headers) query_line = client.get("/api/config/line_alarm_setting", headers=headers, params={"line_no": 3}) query_ai_alarm = client.get("/api/config/ai_alarm_setting", headers=headers) query_system = client.get("/api/config/system", headers=headers) + query_net = client.get("/api/config/device/net", headers=headers, params={"nic": "网卡二"}) time_sync_response = client.post( "/api/config/time-sync", headers=headers, @@ -254,13 +263,17 @@ def test_protocol_config_endpoints_read_and_write_device_only(tmp_path) -> None: assert line_response.status_code == 200 assert ai_alarm_response.status_code == 200 assert system_response.status_code == 200 + assert net_response.status_code == 200 assert time_sync_response.status_code == 200 assert query_channel.json()["data"]["ai_channel"][0]["line_no"] == 3 assert query_line.json()["data"]["transformer_change"][0]["value"] == 20.0 assert query_ai_alarm.json()["data"][0]["delay"] == 30 assert query_system.json()["data"]["brightness"] == 88 + assert query_net.json()["data"]["ip"] == "172.16.2.10" + assert query_net.json()["data"]["gateway"] == "172.16.2.1" assert query_time_sync.json()["data"]["time_sync"] == "2026-05-25 14:00:00" assert platform_service.config_repo.read_setting_config() == {} + assert platform_service.config_repo.read_device_config() == {} finally: platform_service.device_client = old_client platform_service.config_repo = old_repo @@ -268,7 +281,9 @@ def test_protocol_config_endpoints_read_and_write_device_only(tmp_path) -> None: def test_save_device_net_and_uart_by_key(tmp_path) -> None: old_repo = platform_service.config_repo + old_client = platform_service.device_client platform_service.config_repo = JsonConfigRepository(tmp_path) + platform_service.device_client = MockDeviceClient() try: platform_service.config_repo.write_device_config( @@ -338,15 +353,20 @@ def test_save_device_net_and_uart_by_key(tmp_path) -> None: saved = platform_service.config_repo.read_device_config() net_map = {item["nic"]: item for item in saved["net"]} uart_map = {item["port"]: item for item in saved["uart"]} + net_query = client.get("/api/config/device/net", headers=headers, params={"nic": "网卡二"}) assert net_map["网卡一"]["ip"] == "172.16.1.10" - assert net_map["网卡二"]["ip"] == "10.10.10.2" - assert net_map["网卡二"]["protocol"] == "IEC104" + assert net_map["网卡二"]["ip"] == "192.168.1.56" + assert net_map["网卡二"]["protocol"] == "Modbus TCP" assert uart_map["COM1"]["baud"] == 19200 assert uart_map["COM2"]["baud"] == 4800 assert uart_map["COM2"]["protocol"] == "DLT645" + assert net_query.status_code == 200 + assert net_query.json()["data"]["ip"] == "10.10.10.2" + assert net_query.json()["data"]["gateway"] == "10.10.10.1" assert saved["password"] == "hashed-password" finally: + platform_service.device_client = old_client platform_service.config_repo = old_repo diff --git a/backend/tests/test_modbus_client.py b/backend/tests/test_modbus_client.py index 1f1a2f2..1f0a9fc 100644 --- a/backend/tests/test_modbus_client.py +++ b/backend/tests/test_modbus_client.py @@ -16,6 +16,8 @@ from app.adapters.device_client import ( EVENT_FLAG_REGISTER, EVENT_INPUT_START_REGISTER, EVENT_SUCCESS_REPLY_REGISTER, + NET_CONFIG_REGISTER_COUNT, + NET_CONFIG_START_REGISTER, LINE_ALARM_REGISTERS_PER_LINE, LINE_ALARM_START_REGISTER, LINE_START_REGISTERS, @@ -145,8 +147,10 @@ def test_modbus_client_reads_alarm_event_once() -> None: duplicate = client.read_alarm_events() assert len(alarms) == 1 - assert alarms[0].no == "2" - assert alarms[0].type == "PT断线" + assert alarms[0].line_code == 2 + assert alarms[0].event_type == 1 + assert alarms[0].event_code == 1 + assert alarms[0].event_value == 456 assert "线路2PT断线" in alarms[0].content assert duplicate == [] assert transport.written_registers[-1] == (EVENT_SUCCESS_REPLY_REGISTER, [1]) @@ -190,6 +194,13 @@ def test_modbus_client_reads_and_writes_downstream_registers() -> None: ] + [0] * 55 ) + net_payload_bytes = bytes( + [ + 192, 168, 1, 10, 255, 255, 255, 0, 192, 168, 1, 1, 1, 0, + 10, 0, 0, 2, 255, 255, 255, 0, 10, 0, 0, 1, 1, 0, + ] + + [0] * 28 + ) timestamp = int(datetime(2026, 5, 25, 12, 34, 56).timestamp()) transport.set_holding_block(AI_CHANNEL_START_REGISTER, ai_channel_payload) @@ -199,6 +210,7 @@ def test_modbus_client_reads_and_writes_downstream_registers() -> None: client._bytes_to_registers(line_alarm_payload_bytes), ) transport.set_holding_block(AI_ALARM_START_REGISTER, client._bytes_to_registers(ai_alarm_payload_bytes)) + transport.set_holding_block(NET_CONFIG_START_REGISTER, client._bytes_to_registers(net_payload_bytes)) transport.set_holding_block(TIME_SYNC_START_REGISTER, [timestamp & 0xFFFF, (timestamp >> 16) & 0xFFFF]) device_payload = DeviceConfigIn( @@ -248,7 +260,9 @@ def test_modbus_client_reads_and_writes_downstream_registers() -> None: read_ai_alarm = client.read_ai_alarm_setting() read_system = client.read_system_config() read_time_sync = client.read_time_sync_config() + read_net = client.read_net_config("网卡二") device_result = client.send_device_config(device_payload) + net_result = client.send_net_config(NetConfigItem(nic="网卡一", ip="172.16.1.10", mask="255.255.0.0", gateway="172.16.0.1", protocol="Modbus TCP")) channel_result = client.send_channel_config(channel_payload) line_result = client.send_line_alarm_setting(line_alarm_payload) ai_alarm_result = client.send_ai_alarm_setting(ai_alarm_payload) @@ -269,6 +283,9 @@ def test_modbus_client_reads_and_writes_downstream_registers() -> None: assert read_ai_alarm[0]["delay"] == 3 assert read_system["time_sync"] == "2026-05-25 12:34:56" assert read_time_sync["time_sync"] == "2026-05-25 12:34:56" + assert read_net["nic"] == "网卡二" + assert read_net["ip"] == "10.0.0.2" + assert read_net["gateway"] == "10.0.0.1" assert AI_CHANNEL_START_REGISTER in register_map assert len(register_map[AI_CHANNEL_START_REGISTER]) == 30 @@ -278,6 +295,8 @@ def test_modbus_client_reads_and_writes_downstream_registers() -> None: assert len(register_map[LINE_ALARM_START_REGISTER + LINE_ALARM_REGISTERS_PER_LINE]) == 16 assert AI_ALARM_START_REGISTER in register_map assert len(register_map[AI_ALARM_START_REGISTER]) == 30 + assert NET_CONFIG_START_REGISTER in register_map + assert len(register_map[NET_CONFIG_START_REGISTER]) == NET_CONFIG_REGISTER_COUNT assert TIME_SYNC_START_REGISTER in register_map assert len(register_map[TIME_SYNC_START_REGISTER]) == 2 assert DO_STATE_REGISTER in register_map @@ -285,6 +304,8 @@ def test_modbus_client_reads_and_writes_downstream_registers() -> None: assert transport.written_coils == [(2, True)] assert device_result["send_status"] == "成功" + assert net_result["target"] == "net" + assert net_result["nic"] == "网卡一" assert channel_result["modbus_written"]["ai_registers"] == 30 assert line_result["line_no"] == 2 assert ai_alarm_result["items"] == 1 diff --git a/document/gui.h b/document/gui.h deleted file mode 100644 index ca9bc82..0000000 --- a/document/gui.h +++ /dev/null @@ -1,207 +0,0 @@ -#ifndef __GUI_H -#define __GUI_H - -#include "time.h" - -#define LINE_TOTAL_NUM 4 //· -#define ANALOG_CH_NUM 12 //AIAOģͨ - - -/**************************************************************С -> ****************************************************************************/ - -//ʵʱ -typedef struct -{ - //ѹ - float Ua; //Aѹ - float Ub; //Bѹ - float Uc; //Cѹ - - // - float Ia; //A - float Ib; //B - float Ic; //C - - //й - float Pa; - float Pb; - float Pc; - float Pt; //й - - //޹ - float Qa; - float Qb; - float Qc; - float Qt; //޹ - - //ڹ - float Sa; - float Sb; - float Sc; - float St; //ڹ - - // - float PFa; - float PFb; - float PFc; - float PFt; //ܹ - - //ߵѹ - float Uab; - float Ubc; - float Uca; - - //Ƶ - float frq; - -}measure_unit_t; - -//Bʱʱ -typedef union -{ - uint8_t b_code[6]; - - struct - { - uint8_t ascii_s_unit : 4; - uint8_t ascii_s_ten : 4; - uint8_t ascii_min_unit : 4; - uint8_t ascii_min_ten : 4; - uint8_t ascii_hour_unit : 4; - uint8_t ascii_hour_ten : 4; - uint8_t ascii_m_day_unit: 4; - uint8_t ascii_m_day_ten : 4; - uint8_t ascii_month_unit: 4; //4bit - uint8_t ascii_month_ten : 4; //4bit - uint8_t ascii_year_unit : 4; //4bit - uint8_t ascii_year_ten : 4; //4bit - }time; -}b_code_t; - - -//롢 -typedef struct -{ - uint16_t in; //ÿһλӦһͨ״̬ 1 = ϣ 0 = - uint16_t out; //ÿһλӦһͨ״̬ 1 = ϣ 0 = -}d_io_t; - -//ģ:AI AO -typedef struct -{ - float in[ANALOG_CH_NUM]; //ģ - float out[ANALOG_CH_NUM]; //ģ -}a_io_t; - -//¼ -typedef struct -{ - struct tm t; //¼ʱ - uint32_t ms; //¼ĺʱ - uint8_t evt; //ͣ0 = ¼ 1 = ¼ -}event_t; - -// -typedef struct -{ uint32_t stamp; //ʱ - int16_t ch[8]; //4·ʱ8ͨuaubuc, iaibicƵϵ - //AIɼͨʱ 8ͨӦ -}ch_data_t; - -// -typedef struct -{ - uint8_t line_index; //· 0 ~ 3 = 4·ѹݣ4 = AIɼ - ch_data_t dot[64]; //64һܲ -}waveform_data_t; - - -//С -> -typedef struct -{ - measure_unit_t measure_unit[LINE_TOTAL_NUM];//ܹ4· - - a_io_t d_io; //ģ롢 - - d_io_t a_io; //롢 - - b_code_t b_code; //Bʱ - - event_t event[2]; //¼־:event[0] = pt ߣ event[1] = CT - - waveform_data_t waveform; //ʵʱһܲ - -}gui_in_t; - - -/************************************************************** -> С****************************************************************************/ -//һ޸ģ·һ - - -//ڲ -typedef struct -{ - uint32_t baud_tate; // 9600 115200Ĭϣ - uint8_t parity; //Уλ 0 = У飬 1 = У飬 2 = żУ - uint8_t data; //λ 0 = 8λ - uint8_t stop; //ֹͣλ 0 = 1λ 1 = 2λ - uint8_t protocol; //ͨѶЭ 0 = Э飬1 = modbus -}com_param_t; - - -//ģֵͨ -typedef struct -{ - uint8_t sig_type;//ź 0 = 4~20maźţ 1 = 1~5Vź - uint8_t line_sub;//· 1 = ·1 2 = ·2 3 =·3 4 = ·4 0 = װ - uint8_t att; // : 0 = UA, 1 = UB, 2 = UC, 3 = UAB, 4 = UBC, 5 = UCA, 6 = P, 7 = Q, 8 = F - uint8_t u_limit; //ֵ : - uint8_t d_limit; //ֵ : -}analog_ch_param_t; - - -//·ֵ-> -typedef struct -{ - // uint8_t index; //·ţ 1 = ·1, 2 = ·2 3 = ·3, 4 = ·4 - uint8_t limit; //ֵ - uint8_t delay; //ʱ [1 ~ 10] - uint8_t out_num; //ڵ㣺 [1 = 1 2 = 2...] - uint8_t select; //Ͷˣ [0 = Ͷ 1 = Ͷ] -}line_param_t; - -typedef struct -{ - line_param_t type[7]; //7𣺵ѹ, ʣƵʣPTߣCT -}line_type_t; - - -//AI -> -typedef struct -{ - uint8_t u_limit; //ֵ : - uint8_t d_limit; //ֵ : - uint8_t delay; //ʱ [1 ~ 10] - uint8_t out_num; //ڵ㣺 [1 = 1 2 = 2...] - uint8_t select; //Ͷˣ [0 = Ͷ 1 = Ͷ] -}ai_alarm_param_t; - - -// -> С -//ģ -typedef struct -{ - struct tm t; //ʱ - - d_io_t a_io; //롢 - - com_param_t com_param[4]; //ڲ - line_type_t line_param[4]; //·ֵ: 4·ֵһ· - analog_ch_param_t a_in_param[12]; //12·AIģͨ - analog_ch_param_t a_out_param[12]; //12·AOģͨ - ai_alarm_param_t ai_alarm_param[12];//12·AIģ뱨 - -}gui_out_t; - - -#endif diff --git a/document/gui_1.h b/document/gui_2.h similarity index 75% rename from document/gui_1.h rename to document/gui_2.h index 79196cf..2689aaa 100644 --- a/document/gui_1.h +++ b/document/gui_2.h @@ -1,12 +1,12 @@ #ifndef __GUI_H #define __GUI_H -#include "time.h" +#include #define LINE_TOTAL_NUM 4 //ܲ· #define ANALOG_CH_NUM 12 //ģͨ - +#define GUI_SIZE sizeof(gui_in_04reg_t) /***********************************************************************С -> ****************************************************************************/ //ʵʱ @@ -54,6 +54,8 @@ typedef struct //Ƶ float frq; + // float rev1[10];//10 + }measure_unit_t; //Bʱʱ @@ -83,38 +85,36 @@ typedef union typedef struct { uint32_t timestamp; //¼ʱ䣬ʱ - uin16_t ms; //¼ĺʱ - uint8_t line_num; //·ţ0 = 1 = 1·2 =2· 3 = 3· 4 = 4· - uint8_t type; //¼ͣ1 = pt ߣ 2 = CTߣ¼α + uint16_t ms; //¼ĺʱ + uint8_t line_num; //4λ· (0 = װã1 = ·12 =·2 3 = ·3 4 = ·45= AIԽ) + //4λ¼(1 = 2 = 3 = 4 = ) + uint8_t code; /*¼룺ࣺ1 = pt ߣ 2 = CTߣ¼α + ࣺ1 = ѹ2 = 3 = ai4 = ao + ࣺ· + */ + int16_t value; //ֵ ࡢAIԽ¼ʱ̵ļֵ/״̬0֡0ϣ }event_t; -// // -// typedef struct -// { uint32_t stamp; //ʱ -// int16_t ch[8]; //4·ʱ8ͨuaubuc, iaibicƵϵ -// //AIʱ 8ͨӦ -// }ch_data_t; - -// // -// typedef struct -// { -// uint8_t line_index; //· 0 ~ 3 = 4·ѹݣ4 = AIɼ -// ch_data_t dot[64]; //64һܲ -// }waveform_data_t; - //С -> typedef struct { measure_unit_t measure_unit[LINE_TOTAL_NUM];//ܹ4· - float d_in[8]; //ģ + float a_in[8]; //ģ b_code_t b_code; //Bʱ - uint16_t a_in; // - - uint16_t evt_f; //¼־ȡʱ־ٶȡּĴ¼ + uint16_t d_in; // + uint16_t d_out; //״̬ + uint16_t evt_f; //¼ȡ¼0ʱȡĴ¼ }gui_in_04reg_t;//Ĵ +union gui_t +{ + uint8_t buf[GUI_SIZE]; + gui_in_04reg_t sub; +}; + + typedef struct { event_t event; @@ -140,6 +140,15 @@ typedef struct uint8_t protocol; //ͨѶЭ 0 = Э飬1 = modbus }com_param_t; +// +typedef struct +{ + uint8_t id[4]; + uint8_t mask[4]; + uint8_t gateway[4]; + uint8_t protocol; //Э + uint8_t rev;// +}net_t; //ģֵͨ typedef struct @@ -160,11 +169,15 @@ typedef struct uint8_t delay; //ʱ [1 ~ 10] uint8_t out_num; //ڵ㣺 [1 = 1 2 = 2...] uint8_t select; //Ͷˣ [0 = Ͷ 1 = Ͷ] + + }line_param_t; typedef struct { line_param_t type[7]; //7𣺵ѹ, ʣƵʣPTߣCT + uint16_t ct; //ct + uint16_t pt; //pt }line_type_t; @@ -194,12 +207,17 @@ typedef struct }gui_out_03reg_t;//뱣ּĴеIJ + typedef struct { - uint16_t a_in; // - -}gui_out_01reg_t;//ȦĴе + uint16_t d_out; // +}gui_out_15reg_t;//ȦĴе +typedef struct +{ + gui_out_03reg_t reg_03; + gui_out_15reg_t reg_15; +}param_set_t; #endif diff --git a/document/~$界面modbus协议寄存器定义v1.1.doc b/document/~$界面modbus协议寄存器定义v1.1.doc deleted file mode 100644 index ebdf1dd6e1a46333856bf292a78045bc861d2e3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 162 ycmd;0Ov%m6%PcM_N-W7QVjvRmG9)sjFyu01GUNeqF+&MM5m2OrAs