Modbus TCP服务器模拟器

一个具有内存存储、Web UI、REST API和MCP协议的Modbus TCP服务器模拟器,适用于工业自动化测试和开发。

  • by Allexd1992
  • JavaScript
  • 本地部署
  • 更新于
在 GitHub 查看 Allexd1992/modbus-tcp-simulation

使用说明

Modbus TCP服务器仿真

Modbus TCP服务器·内存存储·Web UI·REST·Swagger·基于HTTP的MCP


Web UI v2.2.0:Modbus寄存器矩阵, 地址映射, 模拟规则、自动刷新、Swagger和MCP。



A. Modbus TCP 模拟器与a 单内存存储:web UI, REST API, 昂首阔步,以及 MCP(模型上下文协议) 对于Cursor等客户端,通过HTTP。内置于 火箭, 东京-Modbus,以及 rmcp.

✨ 特征

  • Modbus TCP --保持/输入寄存器、线圈和离散输入。
  • JavaScript模拟脚本 --服务器端QuickJS脚本 script/ 对寄存器写入做出反应,并在计时器上运行(见下文)。
  • 表征状态转移 --与Modbus和MCP相同的数据。
  • Web用户界面 (/ui/)--寄存器矩阵,UInt16/Int32/浮点/双格式,位掩码,可配置间隔的自动刷新,MCP提示 mcp.json 示例和下载。 英语 (默认)和 俄语 通过 EN / 俄 在标题栏中;选择存储在浏览器中。
  • 主控程序 --可流式HTTP /mcp工具,如 modbus_read_holding_registers, modbus_write_holding_registers以及更多。
  • 可安装PWA --在中打开UI 铬 或 边缘 在 http://127.0.0.1:9090/ui/ (或你的主人);页面加载后,使用 安装 在地址栏中(与 油管 / 声破天 web:独立窗口,无标签)。用途 manifest.json +最低限度的服务人员 /ui/.

📋 需求

  • 来源: 生锈的工具链。
  • 集装箱: Docker(或仅从Docker Hub拉取)。

🚀 快速启动

Docker Hub镜像(推荐)

当前标签: 2.2.0.

docker pull allexd2010/modbus-server-sim:2.2.0

docker run -d --name modbus-sim \
  -p 9090:9090 \
  -p 502:502 \
  -e RUST_LOG=info \
  allexd2010/modbus-server-sim:2.2.0
主机 容器 用途
9090 9090 HTTP:REST、Swagger、, /ui/,MCP /mcp
502 502 Modbus TCP

在本地构建映像:

docker build -t allexd2010/modbus-server-sim:2.2.0 .

Docker Compose

docker compose up -d

端口和图像在中定义 docker-compose.yml.

没有Docker

cargo run --release

默认值:web 9090 (UI+neneneba API+MCP位于 /mcp)Modbus 502.UI: http://127.0.0.1:9090/ui/.

作为应用程序安装(PWA)

  1. 启动服务器(cargo run --release 或带有端口的Docker 9090 出版)。
  2. 打开 http://127.0.0.1:9090/ui/ 在 铬 或 微软边缘 (安全上下文: localhost / 127.0.0.1 作品)。
  3. 当浏览器显示 安装 在地址栏(或菜单)中→ 将此网站安装为应用程序),confirm--UI将在其 自己的窗口 没有正常的浏览器工具栏。

已安装的应用程序仍在与 同源 作为页面;保持后端运行。对于远程服务器,请在安装前在URL中使用该主机。

🌐 启动后的服务

服务 URL/地址
Web用户界面 http://<host>:9090/ui/
斯瓦格 http://<host>:9090/api/v1/swagger/
REST 基础前缀 /api/v1/
Modbus TCP <host>:502 (或港口从 MB_SERVER_PORT 以及你的Docker -p 映射)
MCP http://<host>:9090/mcp (与UI/API端口相同)

🖥️ Web用户界面

  • 标签:保持/输入/线圈/离散输入。
  • 偏移 和 计数 设置读取窗口。限制来自服务器(MB_MAX_ADDRESS, MB_MAX_READ_COUNT;违约 65535 每个)。UI从以下位置加载它们 GET /api/v1/ui-config 因此输入保持同步。寄存器矩阵在网格区域内滚动。
  • 自动 --定期读取;间隔(秒);当单元格聚焦时,自动刷新不会覆盖您的输入。
  • 脚本 --顶部酒吧 Modbus/脚本:编辑服务器端JavaScript(*.js),保存发动机重新加载。禁用模拟脚本时隐藏。
  • 人工智能 --MCP帮助文本、光标的当前URL、, mcp.json 下载(主机如页面上所示,端口 18081 默认情况下,或 ?mcpPort= 在页面URL中)。

📍 Modbus寻址

协议和API使用 零基偏移 (第一个持有寄存器是地址 0).Modicon风格文档:持有 40001 → 抵消 0, 40021 → 20.

🔌 REST API(简称)

所有路线都在 /api/v1/;举个例子:

  • GET /api/v1/ui-config --JSON max_modbus_address, max_read_count (与下面的env变量相同的限制;由web UI使用)
  • GET /api/v1/holding-registers/{addr}/{cnt} --阅读
  • POST /api/v1/holding-register/{addr}/{data} --单个单词
  • POST /api/v1/holding-registers/{addr} --JSON正文 {"data":[…]}

超出配置限制的读/写请求将返回 400输入、线圈和离散的URL模式相同——请参见Swagger。

🤖 MCP(光标和其他)

  • 运输: 流式HTTP,端点 /mcp.
  • 与REST相同的商店。
  • 在工具中, addr 是 协议偏移,而不是40001款式号。

Modbus: modbus_read_holding_registers, modbus_write_holding_register,…(之前的所有读/写工具)。

地址图: var_map_get, var_map_save, var_map_import --数据与 /api/v1/var-map (命名变量为 map.* 在脚本中;支持 type: "bit" 和 bit: 0…15).

模拟规则: sim_scripts_list, sim_scripts_read, sim_scripts_write, sim_scripts_delete, sim_scripts_reload, sim_scripts_export, sim_scripts_import (需要脚本引擎;在以下情况下禁用 SIM_SCRIPTS_DISABLE).

组合: simulation_export, simulation_import --脚本+地址映射在一个JSON包中(mode: merge 或 replace).

示例 mcp.json (全球 %USERPROFILE%\.cursor\mcp.json 或项目 .cursor/mcp.json):

{
  "mcpServers": {
    "modbus-tcp-sim": {
      "url": "http://127.0.0.1:9090/mcp"
    }
  }
}

在改变配置之后, 完全重启 光标。

禁用MCP: MCP_SERVER_PORT=0.

📜 模拟脚本(JavaScript)

后端运行 JavaScript 目录中的脚本(默认 ./script)使用嵌入式 QuickJS 运行时。脚本在启动时加载,并与TCP、REST和MCP共享相同的内存Modbus存储。

触发器:

  • modbus.onWrite(kind, fn) --在对寄存器/线圈进行任何写入之后(holding, input, coil, discreteInput):来自Modbus TCP、REST、MCP, 或其他脚本.回拨: (addr, values).
  • modbus.setInterval(ms, fn) --周期性回调(最小间隔1ms)。

来自脚本的API:

方法 说明
modbus.holdingRead(addr, count?) 读取保持寄存器
modbus.inputRead(addr, count?) 读取输入寄存器
modbus.coilRead(addr, count?) 读取线圈
modbus.discreteInputRead(addr, count?) 读取离散输入
modbus.holdingWrite(addr, value) 写一个单词或数组
modbus.inputWrite(addr, value) 输入寄存器也是如此
modbus.coilWrite(addr, value) 写bool或布尔数组
modbus.discreteInputWrite(addr, value) 离散输入也是如此

打字寄存器 (布局与web UI相同:Int32/Float32使用2个单词,单词顺序可选 'HL' 违约或 'LH';Double/Int64使用4个字大端序):

方法 说明
modbus.holdingReadInt32(addr, order?) / holdingWriteInt32 32位签名
modbus.holdingReadFloat(addr, order?) / holdingWriteFloat IEEE754浮点32
modbus.holdingReadDouble(addr) / holdingWriteDouble IEEE754浮点64
modbus.holdingReadInt64(addr) / holdingWriteInt64 64位签名(数字或 {hi, lo} 零件)
modbus.holdingReadInt64Parts(addr) { hi, lo } 没有精度损失
modbus.inputReadInt32 … inputWriteInt64 输入寄存器也是如此

比特 (16位Modbus字,位0=LSB):

方法 说明
modbus.getBit(word, n) 读取位 n (0…15)
modbus.testBit(word, n) true 如果位设置
modbus.setBit(word, n, on) 设置/清除位,返回新单词
modbus.getBits(word, start, len) 提取位字段
modbus.setBits(word, start, len, value) 插入位字段,返回新单词
modbus.int64FromNumber(n) / int64ToNumber(hi, lo) 转换int64部分↔ 数字(安全范围)

地址地图(map) --从中读取/写入命名点 地址映射 选项卡(存储在 conf/var-map.json).与上述布局类型相同:

方法 说明
map.read(name) / map.get(name) 按变量名读取
map.write(name, value) / map.set(name, value) 按姓名书写
map.has(name) true 如果该名称存在于地图中
map.list() 所有变量名的数组
map.def(name) { name, kind, addr, type, order } 或 null
map.def(name).bit 位索引 0…15 当 type 是 "bit" (LSB=b0)
map.onChange(name, fn) 在任何与变量重叠的写入操作之后: fn(value, name)
map.onChange(fn) 查看所有地图变量: fn(name, value)

onChange 从Modbus TCP、REST、MCP或脚本(包括 map.write).回调函数接收 解码 键入值(与 map.read).

例子:

map.onChange("Temperature", function (value) {
  map.write("Alarm", value > 80);
});

map.onChange(function (name, value) {
  if (name === "HR0_counter") {
    /* … */
  }
});

避免反馈循环 onWrite 处理程序(例如,写入与您反应的地址相同的地址)。

示例(script/example.js):

modbus.setInterval(1000, function () {
  var v = modbus.holdingRead(100);
  modbus.holdingWrite(100, (v + 1) % 65536);
});

modbus.onWrite('holding', function (addr, values) {
  if (addr !== 0) return;
  var v = Array.isArray(values) ? values[0] : values;
  modbus.inputWrite(0, v);
});

在Docker中挂载自定义脚本:

docker run -d -p 9090:9090 -p 502:502 \
  -v /path/to/my-scripts:/app/script \
  -v /path/to/my-conf:/app/conf \
  allexd2010/modbus-server-sim:2.2.0

Web用户界面: 地址映射 选项卡——命名为Modbus点; 出口 / 导入 对于地图。 脚本 选项卡--编辑 *.js 文件夹; 出口 / 导入 仅限脚本,或 全部导出 / 全部导入 用于脚本+映射在一起。 保存 写入磁盘并重新加载QuickJS引擎; 重新加载发动机 无需编辑即可应用于磁盘文件。

REST(与UI相同):

  • GET /api/v1/sim-scripts --列表
  • GET /api/v1/sim-scripts/{name} --阅读
  • PUT /api/v1/sim-scripts/{name} --拯救身体 { "content": "…" }
  • POST /api/v1/sim-scripts --创建 { "name": "foo.js", "content": "…" }
  • DELETE /api/v1/sim-scripts/{name}
  • POST /api/v1/sim-scripts/reload --重新装载发动机
  • GET /api/v1/sim-scripts/export --脚本JSON包
  • POST /api/v1/sim-scripts/import --身体 { "version": 1, "scripts": […], "mode": "merge"|"replace" }
  • GET /api/v1/var-map / PUT /api/v1/var-map --读取/保存地址映射
  • GET /api/v1/var-map/export --映射JSON { "version": 1, "variables": […] }
  • POST /api/v1/var-map/import --身体 { "version": 1, "variables": […], "mode": "merge"|"replace" }
  • GET /api/v1/simulation/export --组合 { "version": 1, "scripts": […], "varMap": {…} }
  • POST /api/v1/simulation/import --导入脚本和/或 varMap /顶级 variables
变量 默认值 描述
SIM_SCRIPTS_DIR script (或 /app/script 在Docker中) 仅 目录 *.js 模拟脚本(读取/保存)。遗产 scripts/ 已合并到 script/ 启动并删除。
SIM_SCRIPTS_DISABLE (未设置) 设置为 1 或 true 禁用脚本引擎
VAR_MAP_PATH conf/var-map.json (或 /app/conf/var-map.json 在Docker中) 为 地址映射 选项卡和 map.* 在脚本中

⚙️ 环境变量

变量 默认值 描述
WEB_SERVER_PORT 9090 HTTP(REST、Swagger、静态 /ui)
MB_SERVER_PORT 502 Modbus TCP
MCP_SERVER_PORT 任何非零 0 禁用MCP;启用时,MCP处于 /mcp 上 WEB_SERVER_PORT
MB_MAX_ADDRESS 65535 最大值 协议地址 (含)。窗口 addr … addr + cnt - 1 不得越过此界限。
MB_MAX_READ_COUNT 65535 最大值 单词或比特 在一次HTTP读取中,最大 元素 在一批写入正文中。
RUST_LOG info 日志级别通过 tracing示例: info, debug, modbus_tcp_server_rust=debug,info下的HTTP静态资产 /ui/ 此处省略 info 以降低噪声。

看 更改日志.md 发布历史。

Modbus客户端: 使用从映射的主机端口 MB_SERVER_PORT (默认值 502).这 MB_MAX_* 变量适用于 HTTP REST API (以及web UI调用的内容),而不是内部的原始Modbus TCP帧限制 tokio-modbus.

📋 日志记录

后端使用结构化日志记录(tracing).启动时,您将看到Modbus TCP绑定、web/MCP状态、脚本引擎和var映射路径。

RUST_LOG=info cargo run --release          # default
RUST_LOG=debug cargo run --release         # HTTP requests + script load
RUST_LOG=modbus_tcp_server_rust=debug,info cargo run --release

在Docker/Compose中, RUST_LOG=info 默认设置为。静态文件位于 /ui/ 未登录 info 以降低噪声。

🔧 故障排除

  • 正在使用的端口 --检查 netstat /任务经理;改变 -p 映射或环境变量。
  • MCP在游标中没有响应 --URL必须是 http://<host>:9090/mcp (与web UI端口相同)。
  • UI中的数据为空或错误 -API必须返回 JSON数组;来自代理的HTML不会填充表格。

📦 图像和注册表

  • Docker Hub: allexd2010/modbus-server-sim
  • 标签:例如。 2.2.0

📄 许可证

麻省理工学院


Documentation · latest tag 2.2.0