修改了事件处理逻辑
This commit is contained in:
parent
6937c5bfdd
commit
00fe92c90e
@ -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)
|
||||
|
||||
@ -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,
|
||||
)
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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,
|
||||
)
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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()
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
207
document/gui.h
207
document/gui.h
@ -1,207 +0,0 @@
|
||||
#ifndef __GUI_H
|
||||
#define __GUI_H
|
||||
|
||||
#include "time.h"
|
||||
|
||||
#define LINE_TOTAL_NUM 4 //线路数量
|
||||
#define ANALOG_CH_NUM 12 //AI、AO模拟量通道数
|
||||
|
||||
|
||||
/*************上行数据*************************************************小核数据 -> 大核****************************************************************************/
|
||||
|
||||
//实时量
|
||||
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个通道:ua、ub、uc, ia、ib、ic、频率系数、开入量
|
||||
//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
|
||||
@ -1,12 +1,12 @@
|
||||
#ifndef __GUI_H
|
||||
#define __GUI_H
|
||||
|
||||
#include "time.h"
|
||||
#include <rtthread.h>
|
||||
|
||||
#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 = 线路1,2 =线路2, 3 = 线路3, 4 = 线路4,5= AI越限)
|
||||
//低4位:事件类型(1 = 保护、2 = 动作、3 = 报警、4 = 操作)
|
||||
uint8_t code; /*事件代码:保护类:1 = pt 断线, 2 = CT断线,其它事件依次编号
|
||||
报警类:1 = 过压,2 = 过流,3 = ai,4 = ao
|
||||
动作类:路数
|
||||
*/
|
||||
int16_t value; //动作值, 保护类、报警类和AI越限类→事件发生时刻的监测值、动作类→开入分/合状态(0分、非0合)
|
||||
}event_t;
|
||||
|
||||
// //波形数据
|
||||
// typedef struct
|
||||
// { uint32_t stamp; //时标
|
||||
// int16_t ch[8]; //4路波形数据时:8个通道:ua、ub、uc, ia、ib、ic、频率系数、开入量
|
||||
// //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;//放入保持寄存器中的参数
|
||||
|
||||
|
||||
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
|
||||
Binary file not shown.
@ -196,6 +196,7 @@
|
||||
| AO通道设置 | `0x03 / 0x10` | 保持寄存器 | `0x0072` | 30 | 12路整体下发 |
|
||||
| AI报警设置 | `0x03 / 0x10` | 保持寄存器 | `0x0090` | 30 | 12路整体下发 |
|
||||
| 开出状态字写入 | `0x10` | 保持寄存器 | `0x00A6` | 1 | 16位状态字,每 bit 一路开出 |
|
||||
| 网口设置 | `0x03 / 0x10` | 保持寄存器 | `0x00AE` | 28 | 4路 × 14字节 / 2 = 28 |
|
||||
|
||||
### 5.2.1 时间设置 / 独立对时说明
|
||||
|
||||
@ -269,40 +270,68 @@
|
||||
| 停止位 | `stop` |
|
||||
| 通讯协议 | `protocol` |
|
||||
|
||||
### 5.4 常规配置 / 网卡设置
|
||||
### 5.4 常规配置 / 网口设置
|
||||
|
||||
#### 5.4.1 协议定义说明
|
||||
#### 5.4.1 基本定义
|
||||
|
||||
| 项目 | 定义 |
|
||||
| ---- | ---- |
|
||||
| 配置项 | 网卡号、IP地址、子网掩码、默认网关、通讯协议 |
|
||||
| 当前协议状态 | 本版 Modbus 协议**未定义对应寄存器** |
|
||||
| Modbus 读写支持 | 不支持 |
|
||||
| 处理方式 | 由上位机或系统本地配置逻辑单独处理,不纳入当前 Modbus 参数区 |
|
||||
| 寄存器类型 | 保持寄存器 |
|
||||
| 功能码 | `0x03 / 0x10` |
|
||||
| 起始地址 | `0x00AE` |
|
||||
| 寄存器长度 | `28` |
|
||||
| 长度说明 | `(4(路) × 14byte数据) / 2 = 28` |
|
||||
|
||||
说明:
|
||||
|
||||
- 对应界面中的“常规配置 / 网卡设置”区域。
|
||||
- 当前文档已定义“串口设置”的 Modbus 参数区,但**未定义网卡设置的寄存器地址、长度和编码方式**。
|
||||
- 因此本版协议下,网卡参数**不能**按 `03H / 10H` 通过 Modbus 回读或下发。
|
||||
- 若后续需要将网卡参数纳入 Modbus 管理,建议在协议后续版本中补充:
|
||||
- 寄存器类型
|
||||
- 功能码
|
||||
- 起始地址
|
||||
- 寄存器长度
|
||||
- IP / 掩码 / 网关 / 协议字段编码规则
|
||||
- 对应界面中的“常规配置 / 网口设置”区域。
|
||||
- 共支持 `4` 路网口参数读写。
|
||||
- 每一路网口参数占用 `14` 字节,即 `7` 个保持寄存器。
|
||||
- 主站进入参数页面时应先使用 `03H` 回读当前网口参数,界面修改后再使用 `10H` 整组下发。
|
||||
|
||||
#### 5.4.2 界面字段范围
|
||||
#### 5.4.2 单路网口参数组成
|
||||
|
||||
单路网口参数结构对应 `net_t`,总长度 `14` 字节:
|
||||
|
||||
```c
|
||||
typedef struct
|
||||
{
|
||||
uint8_t id[4];
|
||||
uint8_t mask[4];
|
||||
uint8_t gateway[4];
|
||||
uint8_t protocol;
|
||||
uint8_t rev;
|
||||
} net_t;
|
||||
```
|
||||
|
||||
| 字段 | 类型 | 字节数 | 说明 |
|
||||
| ---- | ---- | ------ | ---- |
|
||||
| `id` | `uint8_t[4]` | 4 | IP 地址,4 个字节 |
|
||||
| `mask` | `uint8_t[4]` | 4 | 子网掩码,4 个字节 |
|
||||
| `gateway` | `uint8_t[4]` | 4 | 默认网关,4 个字节 |
|
||||
| `protocol` | `uint8_t` | 1 | 通讯协议 |
|
||||
| `rev` | `uint8_t` | 1 | 保留 |
|
||||
|
||||
#### 5.4.3 寄存器分配
|
||||
|
||||
| 网口 | 寄存器范围 | 长度 | 说明 |
|
||||
| ---- | ---------- | ---- | ---- |
|
||||
| 网口1 | `0x00AE ~ 0x00B4` | 7 | 对应界面中的一组网口参数 |
|
||||
| 网口2 | `0x00B5 ~ 0x00BB` | 7 | 对应界面中的一组网口参数 |
|
||||
| 网口3 | `0x00BC ~ 0x00C2` | 7 | 预留/扩展网口参数 |
|
||||
| 网口4 | `0x00C3 ~ 0x00C9` | 7 | 预留/扩展网口参数 |
|
||||
|
||||
#### 5.4.4 界面字段对应关系
|
||||
|
||||
对应界面中的典型字段如下:
|
||||
|
||||
| 界面字段 | 当前协议状态 |
|
||||
| -------- | ------------ |
|
||||
| 网卡号 | 未定义寄存器 |
|
||||
| IP地址 | 未定义寄存器 |
|
||||
| 子网掩码 | 未定义寄存器 |
|
||||
| 默认网关 | 未定义寄存器 |
|
||||
| 通讯协议 | 未定义寄存器 |
|
||||
| 界面字段 | 协议字段 |
|
||||
| -------- | -------- |
|
||||
| 网卡号 | 由寄存器块位置区分,不单独占用字段 |
|
||||
| IP地址 | `id[4]` |
|
||||
| 子网掩码 | `mask[4]` |
|
||||
| 默认网关 | `gateway[4]` |
|
||||
| 通讯协议 | `protocol` |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user