| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- from __future__ import annotations
- from typing import Annotated, Any
- from pydantic import BaseModel, ConfigDict, Field
- from .collector_api import (
- create_modbus_devices as api_create_modbus_devices,
- create_modbus_points as api_create_modbus_points,
- edit_modbus_device as api_edit_modbus_device,
- edit_modbus_point as api_edit_modbus_point,
- )
- from .gateway_api import (
- modbus_point_collect_test as api_modbus_point_collect_test,
- modbus_raw_read as api_modbus_raw_read,
- )
- from .mcp_app import mcp
- JsonScalar = str | int | float | bool | None
- ProjectKeyParam = Annotated[str, Field(description="项目标识,来自 project.list 返回的 project_key。")]
- ModbusIpParam = Annotated[str, Field(description="Modbus TCP 设备地址。")]
- ModbusPortParam = Annotated[int, Field(description="Modbus TCP 端口。")]
- ModbusSlaveIdParam = Annotated[int, Field(description="Modbus 从站 ID。")]
- ModbusDeviceTypeParam = Annotated[str, Field(description="网关设备类型,默认 ModbusTCP。")]
- ModbusWordByteOrderParam = Annotated[str, Field(description="字节/字顺序,可选 ABCD、BADC、CDAB、DCBA。")]
- ModbusAddressBaseParam = Annotated[int, Field(description="地址基准或地址偏移,默认 0。")]
- ModbusFunctionCodeParam = Annotated[int, Field(description="功能码:1=线圈,2=离散输入,3=保持寄存器,4=输入寄存器。")]
- ModbusDataTypeParam = Annotated[
- str,
- Field(description="数据类型;支持 bool、int16、uint16、int32、uint32、int64、uint64、float32、float64 及常见别名。"),
- ]
- class FlexibleModel(BaseModel):
- model_config = ConfigDict(extra="allow")
- class ModbusDeviceCreateItem(BaseModel):
- name: str = Field(description="设备名称。")
- ip: str = Field(description="Modbus TCP/UDP 设备地址;RTU 设备可为空。")
- port: int = Field(description="Modbus TCP/UDP 端口;RTU 设备可为 0。")
- slave_id: int = Field(description="Modbus 从站 ID。")
- byte_order: int = Field(description="字节顺序:1=Big Endian,2=Small Endian。")
- word_order: int = Field(description="字顺序:1=Big Endian,2=Small Endian。")
- address_base: int = Field(description="地址基准;会转换为汇采接口 address_offset。")
- serial_port: str = Field(default="", description="RTU 串口名。")
- device_type: int = Field(default=1, description="协议类型:1=TCP,2=RTU,3=UDP,4=RTU OVER TCP,5=RTU OVER UDP。")
- timeout: int = Field(default=3, description="连接超时时间。")
- is_persistent: bool = Field(default=False, description="是否持久化连接。")
- baud_rate: int = Field(default=0, description="RTU 波特率。")
- data_bit: int = Field(default=0, description="RTU 数据位。")
- parity: int = Field(default=0, description="RTU 校验位。")
- stop_bit: int = Field(default=0, description="RTU 停止位。")
- mode: int = Field(default=0, description="连接模式。")
- retry_times: int = Field(default=0, description="重试次数。")
- group_id: int = Field(default=0, description="设备分组 id。")
- alarm_interval: int = Field(default=90, description="告警间隔,单位秒。")
- collect_interval: int = Field(default=5, description="采集周期,单位秒。")
- class ModbusPointCreateItem(BaseModel):
- device_id: int = Field(description="所属 Modbus 设备 id,来自 collector.device_list 或设备创建结果。")
- name: str = Field(description="点位名称。")
- address: int = Field(description="寄存器地址。")
- type: ModbusDataTypeParam
- func_code: int | None = Field(default=3, description="功能码或寄存器类型:1=Read Coils/线圈/0x,2=Read Discrete Inputs/离散输入/1x,3=Read Holding Registers/保持寄存器/4x,4=Read Input Registers/输入寄存器/3x。")
- register_type: str = Field(default="", description="寄存器类型;可用 coil、discrete_input、holding_register、input_register。")
- point_id: str = Field(default="", description="外部点位编码。")
- scale_ratio: float = Field(default=1, description="缩放系数。")
- value_offset: float = Field(default=0, description="值偏移。")
- group_id: int = Field(default=0, description="点位分组 id。")
- invalid_values: str = Field(default="", description="无效值列表,多个值用逗号分隔。")
- valid_range_start: float | None = Field(default=None, description="合法范围最小值。")
- valid_range_end: float | None = Field(default=None, description="合法范围最大值。")
- bit: int = Field(default=0, description="位索引;用于从寄存器中取指定 bit。")
- describe: str = Field(default="", description="点位描述。")
- class ModbusRawReadSpec(BaseModel):
- function_code: ModbusFunctionCodeParam
- address: int = Field(description="起始地址。")
- quantity: int = Field(description="读取数量,范围 1..125。")
- class ModbusPointCollectSpec(FlexibleModel):
- function_code: int | None = Field(default=None, description="功能码或寄存器类型:1=Read Coils/线圈/0x,2=Read Discrete Inputs/离散输入/1x,3=Read Holding Registers/保持寄存器/4x,4=Read Input Registers/输入寄存器/3x。")
- func_code: int | None = Field(default=None, description="功能码或寄存器类型:1=Read Coils/线圈/0x,2=Read Discrete Inputs/离散输入/1x,3=Read Holding Registers/保持寄存器/4x,4=Read Input Registers/输入寄存器/3x。")
- address: int = Field(description="寄存器地址。")
- type: ModbusDataTypeParam
- bit: int | None = Field(default=None, description="位索引;用于读取 bool 位。")
- name: str | None = Field(default=None, description="点位名称;测试读取时可不传。")
- class GatewayBaseOutput(FlexibleModel):
- code: int | str | None = Field(default=None, description="网关状态码;0 表示成功。")
- msg: str | None = Field(default=None, description="网关状态说明或错误信息。")
- class GatewayCommunicationData(FlexibleModel):
- communication: list[str] = Field(default_factory=list, description="本次请求期间捕获的通信报文或通信过程记录。")
- class ModbusRawReadOutput(GatewayBaseOutput):
- data: GatewayCommunicationData | None = Field(default=None, description="Modbus 原始读取结果。")
- class ModbusGatewayPoint(FlexibleModel):
- name: str | None = Field(default=None, description="点位名称。")
- address: int | None = Field(default=None, description="寄存器地址。")
- type: str | None = Field(default=None, description="数据类型。")
- value: JsonScalar = Field(default=None, description="转换后的点位值。")
- class ModbusPointCollectData(GatewayCommunicationData):
- points: list[ModbusGatewayPoint] = Field(default_factory=list, description="转换后的 Modbus 点位值列表。")
- class ModbusPointCollectOutput(GatewayBaseOutput):
- data: ModbusPointCollectData | None = Field(default=None, description="Modbus 点位读取结果。")
- class CollectorBaseOutput(FlexibleModel):
- state: int | str | None = Field(default=None, description="汇采状态码;0 表示成功。")
- state_info: str | None = Field(default=None, description="汇采状态说明。")
- data: Any = Field(default=None, description="汇采接口返回数据。")
- class CollectorPointEditOutput(FlexibleModel):
- state: int | str | None = Field(default=None, description="汇采状态码;0 表示成功。")
- state_info: str | None = Field(default=None, description="汇采状态说明。")
- class BatchError(FlexibleModel):
- index: int | None = Field(default=None, description="输入数组中的下标。")
- name: str | None = Field(default=None, description="输入项名称。")
- stage: str | None = Field(default=None, description="失败阶段。")
- error: str | None = Field(default=None, description="失败原因。")
- class DeviceCreateSummary(FlexibleModel):
- total: int | None = Field(default=None, description="输入设备总数。")
- created: int | None = Field(default=None, description="汇采创建设备成功数量。")
- matched: int | None = Field(default=None, description="创建后从设备列表匹配到设备 id 的数量。")
- failed: int | None = Field(default=None, description="失败数量。")
- class DeviceCreateResult(FlexibleModel):
- index: int | None = Field(default=None, description="输入 devices 数组中的下标。")
- name: str | None = Field(default=None, description="设备名称。")
- device_id: int | None = Field(default=None, description="匹配到的汇采设备 id。")
- create_response: dict[str, Any] | None = Field(default=None, description="汇采创建设备接口原始响应。")
- matched_device: dict[str, Any] | None = Field(default=None, description="设备列表中匹配到的设备详情。")
- class ModbusDeviceCreateOutput(FlexibleModel):
- state: int | str | None = Field(default=None, description="批量创建状态;0 表示全部成功,1 表示存在失败。")
- summary: DeviceCreateSummary | None = Field(default=None, description="批量创建设备汇总。")
- results: list[DeviceCreateResult] = Field(default_factory=list, description="每个设备的创建和匹配结果。")
- errors: list[BatchError] = Field(default_factory=list, description="失败项列表。")
- class PointCreateSummary(FlexibleModel):
- total: int | None = Field(default=None, description="输入点位总数。")
- success: int | None = Field(default=None, description="汇采创建点位成功数量。")
- failed: int | None = Field(default=None, description="失败数量。")
- class PointCreateResult(FlexibleModel):
- index: int | None = Field(default=None, description="输入 points 数组中的下标。")
- name: str | None = Field(default=None, description="点位名称。")
- device_id: int | None = Field(default=None, description="所属 Modbus 设备 id。")
- response: dict[str, Any] | None = Field(default=None, description="汇采创建点位接口原始响应。")
- class ModbusPointCreateOutput(FlexibleModel):
- state: int | str | None = Field(default=None, description="批量创建状态;0 表示全部成功,1 表示存在失败。")
- summary: PointCreateSummary | None = Field(default=None, description="批量创建点位汇总。")
- results: list[PointCreateResult] = Field(default_factory=list, description="每个点位的创建结果。")
- errors: list[BatchError] = Field(default_factory=list, description="失败项列表。")
- @mcp.tool(
- name="modbus.raw_read",
- description=(
- "采集网关-通过 Modbus TCP 读取原始数据,只返回通信报文,不解析业务值。"
- "如需转换后的点位值,请使用 modbus.point_collect_test。"
- ),
- )
- def modbus_raw_read(
- project_key: ProjectKeyParam,
- ip: ModbusIpParam,
- port: ModbusPortParam,
- slave_id: ModbusSlaveIdParam,
- read: Annotated[ModbusRawReadSpec, Field(description="原始读取参数。")],
- device_type: ModbusDeviceTypeParam = "ModbusTCP",
- word_byte_order: ModbusWordByteOrderParam = "ABCD",
- address_base: ModbusAddressBaseParam = 0,
- ) -> ModbusRawReadOutput:
- return api_modbus_raw_read(
- project_key,
- ip=ip,
- port=port,
- slave_id=slave_id,
- read=_dump_model_or_dict(read),
- device_type=device_type,
- word_byte_order=word_byte_order,
- address_base=address_base,
- )
- @mcp.tool(
- name="modbus.point_collect_test",
- description=(
- "采集网关-读取 Modbus TCP 点位并转换为业务值。"
- ),
- )
- def modbus_point_collect_test(
- project_key: ProjectKeyParam,
- ip: ModbusIpParam,
- port: ModbusPortParam,
- slave_id: ModbusSlaveIdParam,
- points: Annotated[list[ModbusPointCollectSpec], Field(description="要读取的 Modbus 点位列表。")],
- device_type: ModbusDeviceTypeParam = "ModbusTCP",
- word_byte_order: ModbusWordByteOrderParam = "ABCD",
- address_base: ModbusAddressBaseParam = 0,
- ) -> ModbusPointCollectOutput:
- return api_modbus_point_collect_test(
- project_key,
- ip=ip,
- port=port,
- slave_id=slave_id,
- points=[_dump_model_or_dict(item) for item in points],
- device_type=device_type,
- word_byte_order=word_byte_order,
- address_base=address_base,
- )
- @mcp.tool(
- name="collector.modbus_device_create",
- description=(
- "汇采-批量创建 Modbus 设备。创建后会读取设备列表并匹配返回设备 id。"
- "word_byte_order 映射到 byte_order/word_order:ABCD=>1/1,BADC=>2/1,CDAB=>1/2,DCBA=>2/2。"
- ),
- )
- def collector_modbus_device_create(
- project_key: ProjectKeyParam,
- devices: Annotated[list[ModbusDeviceCreateItem], Field(description="要创建的 Modbus 设备列表。")],
- ) -> ModbusDeviceCreateOutput:
- return api_create_modbus_devices(project_key, [_dump_model_or_dict(item) for item in devices])
- @mcp.tool(
- name="collector.modbus_device_edit",
- description=(
- "汇采-编辑 Modbus 设备。编辑前设备不能处于已连接状态;"
- "若已连接,请先调用 collector.device_disconnect。"
- ),
- )
- def collector_modbus_device_edit(
- project_key: ProjectKeyParam,
- id: Annotated[int, Field(description="要编辑的原汇采设备 id。")],
- name: Annotated[str, Field(description="设备名称。")],
- device_type: Annotated[int, Field(description="协议类型:1=TCP,2=RTU,3=UDP,4=RTU OVER TCP,5=RTU OVER UDP。")],
- slave_id: ModbusSlaveIdParam,
- byte_order: Annotated[int, Field(description="字节顺序:1=Big Endian,2=Small Endian。")],
- word_order: Annotated[int, Field(description="字顺序:1=Big Endian,2=Small Endian。")],
- ip: Annotated[str, Field(description="Modbus TCP/UDP 设备地址;RTU 设备可为空。")]= "",
- port: Annotated[int, Field(description="Modbus TCP/UDP 端口;RTU 设备可为 0。")]= 0,
- serial_port: Annotated[str, Field(description="RTU 串口名。")]= "",
- timeout: Annotated[int, Field(description="连接超时时间。")]= 3,
- is_persistent: Annotated[bool, Field(description="是否持久化连接。")]= False,
- baud_rate: Annotated[int, Field(description="RTU 波特率。")]= 0,
- data_bit: Annotated[int, Field(description="RTU 数据位。")]= 0,
- parity: Annotated[int, Field(description="RTU 校验位。")]= 0,
- stop_bit: Annotated[int, Field(description="RTU 停止位。")]= 0,
- mode: Annotated[int, Field(description="连接模式。")]= 0,
- address_offset: Annotated[int, Field(description="地址偏移。")]= 0,
- retry_times: Annotated[int, Field(description="重试次数。")]= 0,
- device_group_id: Annotated[int, Field(description="设备分组 id。")]= 0,
- alarm_interval: Annotated[int, Field(description="告警间隔,单位秒。")]= 90,
- collect_interval: Annotated[int, Field(description="采集周期,单位秒。")]= 5,
- ) -> CollectorBaseOutput:
- return api_edit_modbus_device(
- project_key,
- {
- "ori_id": id,
- "name": name,
- "device_type": device_type,
- "ip": ip,
- "port": port,
- "slave_id": slave_id,
- "byte_order": byte_order,
- "word_order": word_order,
- "serial_port": serial_port,
- "timeout": timeout,
- "is_persistent": is_persistent,
- "baud_rate": baud_rate,
- "data_bit": data_bit,
- "parity": parity,
- "stop_bit": stop_bit,
- "mode": mode,
- "address_offset": address_offset,
- "retry_times": retry_times,
- "device_group_id": device_group_id,
- "alarm_interval": alarm_interval,
- "collect_interval": collect_interval,
- },
- )
- @mcp.tool(
- name="collector.modbus_point_create",
- description=(
- "汇采-批量创建 Modbus 采集点位。数据类型和寄存器类型会在调用汇采接口前规范化。"
- ),
- )
- def collector_modbus_point_create(
- project_key: ProjectKeyParam,
- points: Annotated[list[ModbusPointCreateItem], Field(description="要创建的 Modbus 采集点位列表。")],
- ) -> ModbusPointCreateOutput:
- return api_create_modbus_points(project_key, [_dump_model_or_dict(item) for item in points])
- def _dump_model_or_dict(item: Any) -> dict[str, Any]:
- if isinstance(item, BaseModel):
- return item.model_dump(exclude_none=True)
- return dict(item)
- @mcp.tool(
- name="collector.modbus_point_edit",
- description=(
- "汇采-编辑 Modbus 采集点位。数据类型和寄存器类型会在调用汇采接口前规范化。"
- ),
- )
- def collector_modbus_point_edit(
- project_key: ProjectKeyParam,
- id: Annotated[int, Field(description="要编辑的原采集点位 id,对应 collector.device_points 返回的点位 id。")],
- name: Annotated[str, Field(description="点位名称。")],
- address: Annotated[int, Field(description="寄存器地址。")],
- data_type: ModbusDataTypeParam,
- func_code: Annotated[int, Field(description="功能码或寄存器类型:1=Read Coils/线圈/0x,2=Read Discrete Inputs/离散输入/1x,3=Read Holding Registers/保持寄存器/4x,4=Read Input Registers/输入寄存器/3x。")],
- point_id: Annotated[str, Field(description="外部点位编码。")]= "",
- scale_ratio: Annotated[float, Field(description="缩放系数。")]= 1,
- value_offset: Annotated[float, Field(description="值偏移。")]= 0,
- group_id: Annotated[int, Field(description="点位分组 id。")]= 0,
- invalid_values: Annotated[str, Field(description="无效值列表,多个值用逗号分隔。")]= "",
- valid_range_start: Annotated[float | None, Field(description="合法范围最小值。")]= None,
- valid_range_end: Annotated[float | None, Field(description="合法范围最大值。")]= None,
- bit: Annotated[int, Field(description="位索引;用于从寄存器中取指定 bit。")]= 0,
- describe: Annotated[str, Field(description="点位描述。")]= "",
- ) -> CollectorPointEditOutput:
- payload: dict[str, Any] = {
- "ori_id": id,
- "name": name,
- "address": address,
- "type": data_type,
- "point_id": point_id,
- "scale_ratio": scale_ratio,
- "value_offset": value_offset,
- "group_id": group_id,
- "invalid_values": invalid_values,
- "valid_range_start": valid_range_start,
- "valid_range_end": valid_range_end,
- "bit": bit,
- "describe": describe,
- }
- payload["func_code"] = func_code
- return api_edit_modbus_point(project_key, payload)
|