common_server.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. from __future__ import annotations
  2. from typing import Annotated, Any
  3. from pydantic import BaseModel, ConfigDict, Field
  4. from .auth import load_projects_config
  5. from .collector_api import (
  6. connect_device as api_connect_device,
  7. disconnect_device as api_disconnect_device,
  8. list_device_points as api_list_device_points,
  9. list_devices as api_list_devices,
  10. )
  11. from .mcp_app import mcp
  12. JsonScalar = str | int | float | bool | None
  13. ProjectKeyParam = Annotated[str, Field(description="项目标识,来自 project.list 返回的 project_key。")]
  14. DeviceIdParam = Annotated[int, Field(description="汇采设备 id,来自 collector.device_list 返回的设备对象。")]
  15. DeviceTypeParam = Annotated[
  16. str,
  17. Field(description="设备协议类型,可传 modbus、s7、bacnet、ethernet-ip、opc-ua、opc-da、snmp、iec104。"),
  18. ]
  19. DeviceGroupIdParam = Annotated[int, Field(description="设备点位分组 id,默认 0 表示查询设备下默认分组。")]
  20. class FlexibleModel(BaseModel):
  21. model_config = ConfigDict(extra="allow")
  22. class ProjectListItem(BaseModel):
  23. project_key: str = Field(description="项目标识,用于其他采集工具的 project_key 参数。")
  24. project_name: str = Field(description="项目显示名称。")
  25. class ProjectListOutput(BaseModel):
  26. projects: list[ProjectListItem] = Field(description="当前 MCP 服务可用的已启用项目列表。")
  27. total: int = Field(description="可用项目数量。")
  28. class CollectorBaseOutput(FlexibleModel):
  29. state: int | str | None = Field(default=None, description="汇采状态码;0 表示成功。")
  30. state_info: str | None = Field(default=None, description="汇采状态说明。")
  31. class CollectorDeviceItem(FlexibleModel):
  32. id: int | None = Field(default=None, description="设备 id,可用于连接、断开和查询点位。")
  33. name: str | None = Field(default=None, description="设备名称。")
  34. type: str | None = Field(default=None, description="设备类型;devicegroup 表示设备分组。")
  35. device_type: int | str | None = Field(default=None, description="设备子类型或协议内设备类型。")
  36. ip: str | None = Field(default=None, description="设备地址。")
  37. port: int | None = Field(default=None, description="设备端口。")
  38. num_points: int | None = Field(default=None, description="设备点位数量;num_points=true 时返回。")
  39. status: int | None = Field(default=None, description="连接状态:1=未连接,2=已连接,3=连接异常。")
  40. running_status: int | None = Field(default=None, description="运行状态:0=未采集,1=采集中,2=采集异常。")
  41. group_id: int | None = Field(default=None, description="所属设备分组 id。")
  42. groups: list["CollectorDeviceItem"] | None = Field(default=None, description="设备分组下的设备列表。")
  43. class CollectorDeviceListOutput(CollectorBaseOutput):
  44. devices: list[CollectorDeviceItem] = Field(default_factory=list, description="设备列表;type=devicegroup 时继续检查 groups。")
  45. class CollectorDeviceConnectData(FlexibleModel):
  46. status: int | None = Field(default=None, description="操作后的设备连接状态:1=未连接,2=已连接,3=连接异常。")
  47. running_status: int | None = Field(default=None, description="操作后的设备运行状态:0=未采集,1=采集中,2=采集异常。")
  48. msg: str | None = Field(default=None, description="连接或断开失败时的错误信息;成功时通常为空。")
  49. class CollectorDeviceConnectOutput(CollectorBaseOutput):
  50. data: CollectorDeviceConnectData | None = Field(default=None, description="设备连接或断开后的状态。")
  51. class CollectorDevicePointItem(FlexibleModel):
  52. id: int | None = Field(default=None, description="采集点位 id,可用于对应协议的点位编辑工具。")
  53. node_id: str | None = Field(default=None, description="采集节点 id。")
  54. name: str | None = Field(default=None, description="点位名称。")
  55. point_id: str | None = Field(default=None, description="外部点位编码。")
  56. data_type: str | None = Field(default=None, description="数据类型。")
  57. present_value: JsonScalar = Field(default=None, description="当前内存中的点位最新值。")
  58. status: int | None = Field(default=None, description="点位状态:0=未采集,1=采集正常,2=采集异常。")
  59. group_id: int | None = Field(default=None, description="所属点位分组 id。")
  60. created_time: str | None = Field(default=None, description="创建时间。")
  61. updated_time: str | None = Field(default=None, description="更新时间。")
  62. class CollectorDevicePointsData(FlexibleModel):
  63. point: list[CollectorDevicePointItem] = Field(default_factory=list, description="设备下的采集点位列表。")
  64. total: int | None = Field(default=None, description="点位总数。")
  65. class CollectorDevicePointsOutput(CollectorBaseOutput):
  66. data: CollectorDevicePointsData | None = Field(default=None, description="设备点位查询结果。")
  67. @mcp.tool(
  68. name="project.list",
  69. title="Project List",
  70. description=(
  71. "列出当前 MCP 服务可用的已启用采集项目。使用其他采集工具前,应先调用本工具选择 project_key。"
  72. ),
  73. tags={"project", "list"},
  74. )
  75. def project_list() -> ProjectListOutput:
  76. projects = load_projects_config()
  77. result = [
  78. {
  79. "project_key": item["project_key"],
  80. "project_name": item["project_name"],
  81. }
  82. for item in projects
  83. if item["enabled"]
  84. ]
  85. result.sort(key=lambda item: item["project_key"])
  86. return {"projects": result, "total": len(result)}
  87. @mcp.tool(
  88. name="collector.device_list",
  89. description=(
  90. "汇采-返回目前所有设备及其详情信息。type=devicegroup 时表示设备分组,需要继续检查 groups 下的设备。"
  91. ),
  92. )
  93. def collector_device_list(
  94. project_key: ProjectKeyParam,
  95. num_points: Annotated[bool, Field(description="是否返回设备点位数量;默认 false。")]= False,
  96. ) -> CollectorDeviceListOutput:
  97. return api_list_devices(project_key, num_points=num_points)
  98. @mcp.tool(
  99. name="collector.device_connect",
  100. description=(
  101. "汇采-连接设备。连接成功不代表正在采集,设备采集状态请看响应 data.running_status。"
  102. ),
  103. )
  104. def collector_device_connect(
  105. project_key: ProjectKeyParam,
  106. device_id: DeviceIdParam,
  107. device_type: DeviceTypeParam,
  108. ) -> CollectorDeviceConnectOutput:
  109. return api_connect_device(project_key, device_id=device_id, device_type=device_type)
  110. @mcp.tool(
  111. name="collector.device_disconnect",
  112. description=(
  113. "汇采-断开设备。用于停止指定设备连接或采集相关状态,使设备回到未连接或空闲状态。"
  114. ),
  115. )
  116. def collector_device_disconnect(
  117. project_key: ProjectKeyParam,
  118. device_id: DeviceIdParam,
  119. device_type: DeviceTypeParam = "modbus",
  120. ) -> CollectorDeviceConnectOutput:
  121. return api_disconnect_device(project_key, device_id=device_id, device_type=device_type)
  122. @mcp.tool(
  123. name="collector.device_points",
  124. description=(
  125. "汇采-查询设备下的所有采集点位。返回的点位 id 可用于对应协议的点位编辑工具。"
  126. ),
  127. )
  128. def collector_device_points(
  129. project_key: ProjectKeyParam,
  130. device_id: DeviceIdParam,
  131. device_type: DeviceTypeParam,
  132. group_id: DeviceGroupIdParam = 0,
  133. ) -> CollectorDevicePointsOutput:
  134. return api_list_device_points(
  135. project_key,
  136. device_id=device_id,
  137. device_type=device_type,
  138. group_id=group_id,
  139. )