@airiot/modbus 4.3.7 → 4.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +150 -0
- package/dist/index.js +1 -1
- package/dist/package.json +2 -2
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# @airiot/modbus
|
|
2
|
+
|
|
3
|
+
AIRIOT Modbus 驱动安装程序 - 自动下载、配置和运行 Modbus 驱动
|
|
4
|
+
|
|
5
|
+
## 快速开始
|
|
6
|
+
|
|
7
|
+
### 使用 npx(推荐)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# 自动安装并运行驱动
|
|
11
|
+
npx @airiot/modbus
|
|
12
|
+
|
|
13
|
+
# 传递数据配置
|
|
14
|
+
npx @airiot/modbus -d '{"id":"设备ID","name":"modbus","groupId":"分组ID","driverType":"modbus","runMode":"one","device":{"settings":{"interval":60,"ip":"设备IP","port":502,"unit":1}},"autoReload":{"disable":true},"autoUpdateConfig":true,"tables":[{"id":"modbus2","device":{"settings":{"autoAddr":true,"interval":5,"ip":"设备IP"},"tags":[{"area":3,"dataType":"Int32BE","id":"p1","name":"p1","offset":1,"policy":"save"}],"commands":[{"name":"cmd1","ops":[{"area":"coil","dataType":"Boolean","offset":1,"param":"cmd1"}]}]},"devices":[{"id":"modbus21","name":"modbus21","settings":{"unit":7}}]}]}'
|
|
15
|
+
|
|
16
|
+
# 强制更新到最新版本
|
|
17
|
+
npx @airiot/modbus --update
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### 安装后使用
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# 全局安装
|
|
24
|
+
npm install -g @airiot/modbus
|
|
25
|
+
|
|
26
|
+
# 运行驱动
|
|
27
|
+
@airiot/modbus
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 命令参数
|
|
31
|
+
|
|
32
|
+
| 参数 | 简写 | 说明 |
|
|
33
|
+
|------|------|------|
|
|
34
|
+
| `--data <json>` | `-d` | 传递 data.json 配置数据 |
|
|
35
|
+
| `--update` | `-u` | 强制重新下载并更新驱动 |
|
|
36
|
+
|
|
37
|
+
## 运行方式
|
|
38
|
+
|
|
39
|
+
驱动会根据平台自动选择运行方式:
|
|
40
|
+
|
|
41
|
+
- **macOS/Windows**: 使用 PM2 运行二进制文件
|
|
42
|
+
- **Linux**: 使用 Docker 运行容器
|
|
43
|
+
|
|
44
|
+
### 前置要求
|
|
45
|
+
|
|
46
|
+
**macOS/Windows:**
|
|
47
|
+
```bash
|
|
48
|
+
npm install -g pm2
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Linux:**
|
|
52
|
+
```bash
|
|
53
|
+
# 安装 Docker
|
|
54
|
+
curl -fsSL https://get.docker.com | sh
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 配置文件
|
|
58
|
+
|
|
59
|
+
驱动启动时会自动生成 `etc/config.yaml` 配置文件:
|
|
60
|
+
|
|
61
|
+
```yaml
|
|
62
|
+
http:
|
|
63
|
+
host: 0.0.0.0
|
|
64
|
+
port: 8080
|
|
65
|
+
|
|
66
|
+
driverGrpc:
|
|
67
|
+
|
|
68
|
+
mq:
|
|
69
|
+
type: local
|
|
70
|
+
local:
|
|
71
|
+
logPublish: true
|
|
72
|
+
logConsume: true
|
|
73
|
+
showPayload: false
|
|
74
|
+
|
|
75
|
+
driver:
|
|
76
|
+
id: modbus
|
|
77
|
+
name: Modbus-TCP
|
|
78
|
+
timeout: 10
|
|
79
|
+
idle: 30
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## 目录结构
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
airiot/lib/driver/modbus/
|
|
86
|
+
├── .version # 当前安装版本
|
|
87
|
+
├── archive/ # 下载缓存
|
|
88
|
+
└── extracted/ # 驱动文件
|
|
89
|
+
├── etc/
|
|
90
|
+
│ └── config.yaml
|
|
91
|
+
└── modbus # 二进制文件
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## 故障排除
|
|
95
|
+
|
|
96
|
+
### 端口被占用
|
|
97
|
+
|
|
98
|
+
如果 8080 端口被占用,驱动会自动使用下一个可用端口(8081、8082...)。
|
|
99
|
+
|
|
100
|
+
### PM2 相关问题
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# 查看 PM2 运行状态
|
|
104
|
+
pm2 list
|
|
105
|
+
|
|
106
|
+
# 查看 PM2 日志
|
|
107
|
+
pm2 logs modbus-driver
|
|
108
|
+
|
|
109
|
+
# 停止驱动
|
|
110
|
+
pm2 stop modbus-driver
|
|
111
|
+
|
|
112
|
+
# 重启驱动
|
|
113
|
+
pm2 restart modbus-driver
|
|
114
|
+
|
|
115
|
+
# 删除驱动
|
|
116
|
+
pm2 delete modbus-driver
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Docker 相关问题
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
# 查看运行中的容器
|
|
123
|
+
docker ps
|
|
124
|
+
|
|
125
|
+
# 查看容器日志
|
|
126
|
+
docker logs modbus-driver
|
|
127
|
+
|
|
128
|
+
# 停止容器
|
|
129
|
+
docker stop modbus-driver
|
|
130
|
+
|
|
131
|
+
# 删除容器
|
|
132
|
+
docker rm modbus-driver
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## 版本管理
|
|
136
|
+
|
|
137
|
+
驱动会自动检测版本更新:
|
|
138
|
+
|
|
139
|
+
- 首次运行:自动下载对应平台版本
|
|
140
|
+
- 版本相同:使用已缓存的文件
|
|
141
|
+
- 版本不同:提示使用 `--update` 参数升级
|
|
142
|
+
|
|
143
|
+
## 许可证
|
|
144
|
+
|
|
145
|
+
MIT
|
|
146
|
+
|
|
147
|
+
## 支持
|
|
148
|
+
|
|
149
|
+
- 问题反馈: [GitHub Issues](https://github.com/airiot/modbus-driver/issues)
|
|
150
|
+
- 官方文档: [https://docs.airiot.link](https://docs.airiot.link)
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
(()=>{var e={100:(e,o,n)=>{const t=n(692);const r=n(611);const s=n(896);const
|
|
2
|
+
(()=>{var e={100:(e,o,n)=>{const t=n(692);const r=n(611);const s=n(896);const i=n(928);const c="modbus";const a=i.join(process.cwd(),"airiot","lib","driver");const l="https://d.airiot.link";function getPackageVersion(){const e=n.ab+"package.json";const o=JSON.parse(s.readFileSync(n.ab+"package.json","utf-8"));return o.version}function getPlatformInfo(){const e=process.platform;const o=process.arch;let n=o;if(o==="x64"||o==="ia32"){n="x86_64"}else if(o==="arm64"||o==="aarch64"){n="aarch64"}else if(o==="loong64"||o==="loongarch64"){n="loong64"}let t=e;if(e==="win32"){t="windows"}return{platform:t,arch:n,originalPlatform:e}}function getDownloadUrl(e,o,n){const t=e==="windows"||e==="darwin";let r;let s;if(e==="windows"){r=`${c}-${e}-${o}`;s="zip"}else if(t){r=`${c}-${e}-${o}-binary`;s="tar.gz"}else if(e==="linux"&&o==="loong64"){r=`${c}-abi1-${e}-${o}`;s="tar.gz"}else{r=`${c}-${e}-${o}`;s="tar.gz"}return`${l}/${c}/v${n}/${r}.${s}`}function isBinaryVersion(e){return e.includes("windows")||e.includes("binary")}async function getAvailablePort(e,o="0.0.0.0"){const t=n(278);return new Promise((n=>{const r=t.createServer();r.listen(e,o,(()=>{const e=r.address().port;r.close((()=>n(e)))}));r.on("error",(()=>{n(getAvailablePort(e+1,o))}))}))}async function downloadFile(e,o,n){const i=o+".tmp";const c=new URL(e);const a=c.protocol==="https:"?t:r;return new Promise(((t,r)=>{const cleanup=()=>{try{if(s.existsSync(i))s.unlinkSync(i)}catch{}};const c=a.get(e,(c=>{if(c.statusCode===301||c.statusCode===302||c.statusCode===307||c.statusCode===308){return downloadFile(c.headers.location,o,n).then(t).catch(r)}if(c.statusCode!==200){cleanup();return r(new Error(`HTTP ${c.statusCode} downloading ${e}`))}const a=parseInt(c.headers["content-length"],10);let l=0;const u=s.createWriteStream(i);c.on("data",(e=>{l+=e.length;if(n)n(l,a)}));c.pipe(u);u.on("finish",(()=>{u.close();try{s.renameSync(i,o);t(o)}catch(e){cleanup();r(e)}}))}));c.on("error",(e=>{cleanup();r(e)}))}))}async function extractFile(e,o){if(e.endsWith(".zip")){const t=n(211);const r=new t(e);r.extractAllTo(o,true)}else if(e.endsWith(".tar.gz")){const t=n(218);await s.promises.mkdir(o,{recursive:true});await t.x({file:e,cwd:o})}else{throw new Error(`Unsupported archive format: ${e}`)}}async function generateConfig(e,o){const n=i.join(e,"etc");const t=i.join(n,"config.yaml");if(s.existsSync(t)){let e=s.readFileSync(t,"utf-8");if(e.includes("http:")){e=e.replace(/(http:[\s\S]*?port:\s*)\d+/,`$1${o}`)}else{const n=`http:\n host: 0.0.0.0\n port: ${o}\n\n`;e=n+e}e=e.replace(/driverGrpc:([\s\S]*?)\n\n/,"driverGrpc:\n\n");e=e.replace(/mq:[\s\S]*?\n\ndriver:/,`mq:\n type: local\n local:\n logPublish: true\n logConsume: true\n showPayload: false\n\ndriver:`);await s.promises.writeFile(t,e,"utf-8");return t}const r=`http:\n host: 0.0.0.0\n port: ${o}\n\ndriverGrpc:\n\nmq:\n type: local\n local:\n logPublish: true\n logConsume: true\n showPayload: false\n\ndriver:\n id: modbus\n name: Modbus-TCP\n timeout: 10\n idle: 30\n\npprof:\n enable: false\n port: 9002\n\nlog:\n level: 4\n`;await s.promises.mkdir(n,{recursive:true});await s.promises.writeFile(t,r,"utf-8");return t}e.exports={DRIVER_NAME:c,CACHE_DIR:a,getPackageVersion:getPackageVersion,getPlatformInfo:getPlatformInfo,getDownloadUrl:getDownloadUrl,isBinaryVersion:isBinaryVersion,getAvailablePort:getAvailablePort,downloadFile:downloadFile,extractFile:extractFile,generateConfig:generateConfig}},211:e=>{"use strict";e.exports=require("adm-zip")},317:e=>{"use strict";e.exports=require("child_process")},896:e=>{"use strict";e.exports=require("fs")},611:e=>{"use strict";e.exports=require("http")},692:e=>{"use strict";e.exports=require("https")},278:e=>{"use strict";e.exports=require("net")},928:e=>{"use strict";e.exports=require("path")},218:e=>{"use strict";e.exports=require("tar")}};var o={};function __nccwpck_require__(n){var t=o[n];if(t!==undefined){return t.exports}var r=o[n]={exports:{}};var s=true;try{e[n](r,r.exports,__nccwpck_require__);s=false}finally{if(s)delete o[n]}return r.exports}if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=__dirname+"/";var n={};const{execSync:t}=__nccwpck_require__(317);const r=__nccwpck_require__(896);const s=__nccwpck_require__(928);const{DRIVER_NAME:i,CACHE_DIR:c,getPackageVersion:a,getPlatformInfo:l,getDownloadUrl:u,isBinaryVersion:d,getAvailablePort:f,downloadFile:g,extractFile:p,generateConfig:w}=__nccwpck_require__(100);const h=8080;function parseArgs(){const e=process.argv.slice(2);const o={dataJson:null,update:false};for(let n=0;n<e.length;n++){if(e[n]==="-d"&&n+1<e.length){o.dataJson=e[n+1];n++}else if(e[n]==="--update"||e[n]==="-u"){o.update=true}}return o}function getInstallPaths(){const e=s.join(c,i);const o=s.join(e,"archive");const n=s.join(e,".version");return{INSTALL_DIR:e,ARCHIVE_PATH:o,VERSION_FILE:n}}function getInstalledVersion(e){try{if(r.existsSync(e)){return r.readFileSync(e,"utf-8").trim()}}catch{}return null}function writeVersionFile(e,o){try{r.writeFileSync(e,o,"utf-8")}catch(e){console.warn(`警告: 无法写入版本文件: ${e.message}`)}}function writeDataJson(e,o){try{const n=JSON.parse(o);const t=JSON.stringify(n,null,2);const i=s.join(e,"data.json");r.writeFileSync(i,t,"utf-8");console.log(`已写入 data.json: ${i}`);return i}catch(e){throw new Error(`无效的 JSON 格式: ${e.message}`)}}function showProgress(e,o){const n=o?Math.round(e/o*100):0;const t=(e/(1024*1024)).toFixed(1);const r=o?(o/(1024*1024)).toFixed(1):"?";process.stderr.write(`\r 下载中: ${t}MB / ${r}MB (${n}%)`)}function commandExists(e){try{t(`which ${e}`,{stdio:"ignore"});return true}catch{return false}}async function runWithDocker(e,o,n){console.log("\n使用 Docker 运行驱动...");const r=findDockerImage(e);if(!r){throw new Error("未找到 Docker 镜像")}console.log(`加载 Docker 镜像: ${r}`);try{t(`docker load -i "${r}"`,{stdio:"inherit",cwd:e})}catch(e){throw new Error(`加载 Docker 镜像失败: ${e.message}`)}const c=a();const l=`airiot/${i}:v${c}`;const u=s.resolve(o);const d=["docker","run","-d","--name",`${i}-driver`,"--restart","unless-stopped","-p",`${n}:8080`,"-v",`${u}:/app/etc/config.yaml`,l].join(" ");console.log(`运行命令: ${d}`);t(d,{stdio:"inherit"});console.log("驱动已在 Docker 中启动")}async function runWithPm2(e){console.log("\n使用 PM2 运行驱动...");if(!commandExists("pm2")){console.error("错误: 未安装 PM2");console.error("请运行: npm install -g pm2");process.exit(1)}const o=findBinary(e);if(!o){throw new Error("未找到二进制文件")}if(process.platform!=="win32"){try{r.chmodSync(o,493)}catch(e){console.warn(`警告: 无法设置执行权限: ${e.message}`)}}const n=s.basename(o);const c=`pm2 start "${n}" --name ${i}-driver`;console.log(`运行命令: ${c}`);console.log(`工作目录: ${e}`);try{t(c,{stdio:"inherit",cwd:e});console.log("驱动已通过 PM2 启动")}catch(e){throw new Error(`PM2 启动失败: ${e.message}`)}}function findDockerImage(e){const o=r.readdirSync(e);return o.find((e=>e.endsWith(".tar.gz")&&!e.includes("binary")))}function findBinary(e){const o=r.readdirSync(e);const n=process.platform;const t=[i,`${i}.exe`,"modbus","modbus.exe"];for(const o of t){const n=s.join(e,o);if(r.existsSync(n)){return n}}for(const t of o){const o=s.join(e,t);try{if(r.statSync(o).isFile()){if(n==="win32"&&t.endsWith(".exe")){return o}else if(n!=="win32"&&!t.includes(".")){return o}}}catch{}}return null}async function main(){const{dataJson:e,update:o}=parseArgs();console.log(`AIRIOT Modbus 驱动安装程序`);console.log("=".repeat(40));const{platform:n,arch:t}=l();console.log(`检测到平台: ${n}-${t}`);const i=a();console.log(`驱动版本: ${i}`);const{INSTALL_DIR:c,ARCHIVE_PATH:m,VERSION_FILE:y}=getInstallPaths();console.log(`安装目录: ${c}`);const $=u(n,t,i);console.log(`下载地址: ${$}`);const _=$.split("/").pop();const x=s.join(m,_);const v=getInstalledVersion(y);let k=!r.existsSync(x);if(v){if(v!==i){console.log(`\n检测到已安装版本: ${v}`);console.log(`当前版本: ${i}`);if(!o){console.log(`提示: 使用 --update 参数升级到新版本`)}else{console.log(`升级到新版本...`);k=true}}}else if(o){console.log("\n检测到 --update 参数,重新下载...");k=true}if(k||o){r.mkdirSync(c,{recursive:true});r.mkdirSync(m,{recursive:true});console.log("\n开始下载...");try{await g($,x,showProgress);console.error("\n下载完成!");writeVersionFile(y,i)}catch(e){console.error(`\n下载失败: ${e.message}`);process.exit(1)}}else{console.log("\n使用已缓存的文件")}console.log("\n解压驱动文件...");const P=s.join(c,"extracted");r.mkdirSync(P,{recursive:true});try{await p(x,P);console.log("解压完成!")}catch(e){console.error(`解压失败: ${e.message}`);process.exit(1)}const S=r.readdirSync(P);let b=P;if(S.length===1){const e=s.join(P,S[0]);if(r.statSync(e).isDirectory()){b=e}}console.log("\n检测可用端口...");const I=await f(h);if(I!==h){console.log(`端口 ${h} 被占用,使用端口 ${I}`)}else{console.log(`使用端口 ${I}`)}console.log("\n生成配置文件...");const D=await w(b,I);console.log(`配置文件: ${D}`);if(e){console.log("\n写入数据文件...");writeDataJson(b,e)}const F=d(_);if(F){console.log("\n这是二进制版本,将使用 PM2 运行");await runWithPm2(b)}else{console.log("\n这是 Docker 版本,将使用 Docker 运行");await runWithDocker(b,D,I)}console.log("\n"+"=".repeat(40));console.log("安装完成!");console.log(`驱动正在运行,端口: ${I}`)}main().catch((e=>{console.error("\n致命错误:",e.message);if(process.env.DEBUG){console.error(e.stack)}process.exit(1)}));module.exports=n})();
|
package/dist/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@airiot/modbus",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "4.3.
|
|
4
|
+
"version": "4.3.8",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"@airiot/modbus": "dist/index.js"
|
|
@@ -26,6 +26,6 @@
|
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
28
|
"dist",
|
|
29
|
-
"
|
|
29
|
+
"README.md"
|
|
30
30
|
]
|
|
31
31
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@airiot/modbus",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "4.3.
|
|
4
|
+
"version": "4.3.8",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"@airiot/modbus": "dist/index.js"
|
|
@@ -26,6 +26,6 @@
|
|
|
26
26
|
},
|
|
27
27
|
"files": [
|
|
28
28
|
"dist",
|
|
29
|
-
"
|
|
29
|
+
"README.md"
|
|
30
30
|
]
|
|
31
31
|
}
|