@things-factory/integration-modbus 8.0.0 → 9.0.0-beta.3
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/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/server/engine/connector/index.ts +0 -2
- package/server/engine/connector/modbus-tcp-server.ts +0 -50
- package/server/engine/connector/modbus-tcp.ts +0 -173
- package/server/engine/index.ts +0 -2
- package/server/engine/task/index.ts +0 -2
- package/server/engine/task/modbus-read.ts +0 -58
- package/server/engine/task/modbus-write-single.ts +0 -68
- package/server/index.ts +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/integration-modbus",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0-beta.3",
|
|
4
4
|
"main": "dist-server/index.js",
|
|
5
5
|
"browser": "client/index.js",
|
|
6
6
|
"things-factory": true,
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
"clean": "npm run clean:server"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@things-factory/integration-base": "^
|
|
26
|
+
"@things-factory/integration-base": "^9.0.0-beta.3",
|
|
27
27
|
"jsmodbus": "^4.0.2",
|
|
28
28
|
"p-queue": "^6.4.0",
|
|
29
29
|
"promise-socket": "^7.0.0",
|
|
30
30
|
"serialport": "^9.0.2"
|
|
31
31
|
},
|
|
32
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "1d7e0dd4c88f3c3f3bd311c00e4b1d1542d53634"
|
|
33
33
|
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { ConnectionManager, Connector, Connection } from '@things-factory/integration-base'
|
|
2
|
-
import net from 'net'
|
|
3
|
-
import * as modbus from 'jsmodbus'
|
|
4
|
-
|
|
5
|
-
export class ModbusTCPServer implements Connector {
|
|
6
|
-
async ready(connectionConfigs) {
|
|
7
|
-
await Promise.all(connectionConfigs.map(this.connect))
|
|
8
|
-
|
|
9
|
-
ConnectionManager.logger.info('modbus-tcp-servers are ready')
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
async connect(config) {
|
|
13
|
-
var [host = '0.0.0.0', port = 502] = config.endpoint.split(':')
|
|
14
|
-
|
|
15
|
-
const netServer = new net.Server()
|
|
16
|
-
|
|
17
|
-
const server = new modbus.server.TCP(netServer)
|
|
18
|
-
|
|
19
|
-
netServer.on('error', console.error)
|
|
20
|
-
netServer.listen(port, host)
|
|
21
|
-
|
|
22
|
-
/* default client connection */
|
|
23
|
-
const clientSocket = new net.Socket()
|
|
24
|
-
const client = new modbus.client.TCP(clientSocket)
|
|
25
|
-
clientSocket.on('error', console.error)
|
|
26
|
-
clientSocket.connect({ host: 'localhost', port })
|
|
27
|
-
|
|
28
|
-
client['__server__'] = server
|
|
29
|
-
|
|
30
|
-
ConnectionManager.addConnectionInstance(config, client)
|
|
31
|
-
|
|
32
|
-
ConnectionManager.logger.info(`modbus-tcp-server connection(${config.name}:${config.endpoint}) is connected`)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async disconnect(connection: Connection) {
|
|
36
|
-
var client = ConnectionManager.removeConnectionInstance(connection)
|
|
37
|
-
var server = client['__server__']
|
|
38
|
-
|
|
39
|
-
client.socket.end()
|
|
40
|
-
server && server._server.close()
|
|
41
|
-
|
|
42
|
-
ConnectionManager.logger.info(`modbus-tcp-server connection(${connection.name}) is disconnected`)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
get parameterSpec() {
|
|
46
|
-
return []
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
ConnectionManager.registerConnector('modbus-tcp-server', new ModbusTCPServer())
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
import * as modbus from 'jsmodbus'
|
|
2
|
-
import { Socket } from 'net'
|
|
3
|
-
import PQueue from 'p-queue'
|
|
4
|
-
import PromiseSocket from 'promise-socket'
|
|
5
|
-
|
|
6
|
-
import { Connection, ConnectionManager, Connector } from '@things-factory/integration-base'
|
|
7
|
-
import { sleep } from '@things-factory/utils'
|
|
8
|
-
|
|
9
|
-
const debug = require('debug')('things-factory:modbus-tcp-connector')
|
|
10
|
-
|
|
11
|
-
export class ModbusTCPConnector implements Connector {
|
|
12
|
-
async ready(connectionConfigs) {
|
|
13
|
-
await Promise.all(connectionConfigs.map(this.connect.bind(this)))
|
|
14
|
-
|
|
15
|
-
ConnectionManager.logger.info('modbus-tcp connections are ready')
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
checkConnectionInstance(domain, connectionName): boolean {
|
|
19
|
-
try {
|
|
20
|
-
const connection = ConnectionManager.getConnectionInstanceByName(domain, connectionName)
|
|
21
|
-
|
|
22
|
-
return !!(connection?.client?.connectionState !== 'online')
|
|
23
|
-
} catch (e) {
|
|
24
|
-
return false
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async connect(connection) {
|
|
29
|
-
var [host, port = 502] = connection.endpoint.split(':')
|
|
30
|
-
var {
|
|
31
|
-
params: { keepalive = true }
|
|
32
|
-
} = connection
|
|
33
|
-
|
|
34
|
-
try {
|
|
35
|
-
/*
|
|
36
|
-
try to keep connecting if keepalive is true
|
|
37
|
-
*/
|
|
38
|
-
var clientIdx = 1
|
|
39
|
-
var connected: boolean = false
|
|
40
|
-
var looped: boolean = true
|
|
41
|
-
while (true) {
|
|
42
|
-
var clientSocket = new Socket()
|
|
43
|
-
clientSocket = new Socket()
|
|
44
|
-
var promiseSocket = new PromiseSocket(clientSocket)
|
|
45
|
-
|
|
46
|
-
var client = new modbus.client.TCP(clientSocket, clientIdx++, 10000)
|
|
47
|
-
|
|
48
|
-
clientSocket.on('error', async ex => {
|
|
49
|
-
debug(ex)
|
|
50
|
-
ConnectionManager.logger.error(`modbus tcp connection error: ${ex}`)
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
clientSocket.on('close', async () => {
|
|
54
|
-
debug(`modbus tcp connection closed`)
|
|
55
|
-
if (keepalive && !looped) {
|
|
56
|
-
await this.disconnect(connection)
|
|
57
|
-
await this.connect(connection)
|
|
58
|
-
}
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
debug(`connecting to ${connection.endpoint}...`)
|
|
62
|
-
|
|
63
|
-
looped = true
|
|
64
|
-
await promiseSocket
|
|
65
|
-
.connect(port, host)
|
|
66
|
-
.then(() => {
|
|
67
|
-
connected = true
|
|
68
|
-
})
|
|
69
|
-
.catch(ex => {
|
|
70
|
-
promiseSocket && promiseSocket.destroy()
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
// if the current connections have the connection with the same name, the current try should be stopped.
|
|
74
|
-
if (!keepalive || connected) {
|
|
75
|
-
looped = false
|
|
76
|
-
break
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
await sleep(1000)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
var queue = new PQueue({ concurrency: 1 })
|
|
83
|
-
ConnectionManager.addConnectionInstance(connection, {
|
|
84
|
-
readModBus: async function (objectType, address, quantity, { logger }) {
|
|
85
|
-
return await queue.add(async () => {
|
|
86
|
-
var response
|
|
87
|
-
|
|
88
|
-
switch (objectType) {
|
|
89
|
-
case 'descrete input':
|
|
90
|
-
response = await client.readDiscreteInputs(address, quantity)
|
|
91
|
-
break
|
|
92
|
-
case 'input register':
|
|
93
|
-
response = await client.readInputRegisters(address, quantity)
|
|
94
|
-
break
|
|
95
|
-
case 'holding register':
|
|
96
|
-
response = await client.readHoldingRegisters(address, quantity)
|
|
97
|
-
break
|
|
98
|
-
default:
|
|
99
|
-
response = await client.readCoils(address, quantity)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
var data = response && response.response._body.valuesAsArray.slice(0, quantity)
|
|
103
|
-
logger.info(`${JSON.stringify(data)}`)
|
|
104
|
-
return {
|
|
105
|
-
data
|
|
106
|
-
}
|
|
107
|
-
})
|
|
108
|
-
},
|
|
109
|
-
writeSingleModBus: async function (objectType, address, value, { logger }) {
|
|
110
|
-
return await queue.add(async () => {
|
|
111
|
-
var response
|
|
112
|
-
|
|
113
|
-
switch (objectType) {
|
|
114
|
-
case 'holding register':
|
|
115
|
-
await client.writeSingleRegister(address, parseInt(value))
|
|
116
|
-
response = await client.readHoldingRegisters(address, 1)
|
|
117
|
-
break
|
|
118
|
-
default:
|
|
119
|
-
await client.writeSingleCoil(address, !!Number(value))
|
|
120
|
-
response = await client.readCoils(address, 1)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
var data = response && response.response._body.valuesAsArray[0]
|
|
124
|
-
logger.info(data)
|
|
125
|
-
return {
|
|
126
|
-
data
|
|
127
|
-
}
|
|
128
|
-
})
|
|
129
|
-
},
|
|
130
|
-
close: function () {
|
|
131
|
-
queue.clear()
|
|
132
|
-
keepalive = false
|
|
133
|
-
promiseSocket.destroy()
|
|
134
|
-
}
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
ConnectionManager.logger.info(`modbus-tcp connection(${connection.name}:${connection.endpoint}) is connected`)
|
|
138
|
-
} catch (ex) {
|
|
139
|
-
ConnectionManager.logger.info(`modbus-tcp connection(${connection.name}:${connection.endpoint}) failed to connect`)
|
|
140
|
-
throw ex
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
async disconnect(connection: Connection) {
|
|
145
|
-
var { close } = ConnectionManager.removeConnectionInstance(connection)
|
|
146
|
-
close()
|
|
147
|
-
|
|
148
|
-
ConnectionManager.logger.info(`modbus-tcp connection(${connection.name}) is disconnected`)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
get parameterSpec() {
|
|
152
|
-
return [
|
|
153
|
-
{
|
|
154
|
-
type: 'checkbox',
|
|
155
|
-
name: 'keepalive',
|
|
156
|
-
label: 'keepalive',
|
|
157
|
-
property: {
|
|
158
|
-
value: 'checked'
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
]
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
get taskPrefixes() {
|
|
165
|
-
return ['modbus']
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
get help() {
|
|
169
|
-
return 'integration/connector/modbus-tcp'
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
ConnectionManager.registerConnector('modbus-tcp', new ModbusTCPConnector())
|
package/server/engine/index.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { access } from '@things-factory/utils'
|
|
2
|
-
import { ConnectionManager, TaskRegistry } from '@things-factory/integration-base'
|
|
3
|
-
|
|
4
|
-
const debug = require('debug')('things-factory:modbus-read')
|
|
5
|
-
|
|
6
|
-
async function modbusRead(step, { logger, domain, data }) {
|
|
7
|
-
var {
|
|
8
|
-
connection,
|
|
9
|
-
params: { objectType = 'coil', address, quantity = 1, accessorAddress }
|
|
10
|
-
} = step
|
|
11
|
-
|
|
12
|
-
var connectionInstance = ConnectionManager.getConnectionInstanceByName(domain, connection)
|
|
13
|
-
if (!connectionInstance) {
|
|
14
|
-
debug(`no connection : ${connection}`)
|
|
15
|
-
throw new Error(`no connection : ${connection}`)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
address = accessorAddress ? access(accessorAddress, data) : address
|
|
19
|
-
if (isNaN(address)) {
|
|
20
|
-
debug(`invalid number address : ${address}`)
|
|
21
|
-
throw new Error(`invalid number address : ${address}`)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
var { readModBus } = connectionInstance
|
|
25
|
-
var content = await readModBus(objectType, address, quantity, { logger })
|
|
26
|
-
debug(`result : ${JSON.stringify(content)}`)
|
|
27
|
-
return content
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
modbusRead.parameterSpec = [
|
|
31
|
-
{
|
|
32
|
-
type: 'select',
|
|
33
|
-
name: 'objectType',
|
|
34
|
-
label: 'object-type',
|
|
35
|
-
property: {
|
|
36
|
-
options: ['', 'coil', 'descrete input', 'input register', 'holding register']
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
type: 'number',
|
|
41
|
-
name: 'address',
|
|
42
|
-
label: 'address'
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
type: 'number',
|
|
46
|
-
name: 'quantity',
|
|
47
|
-
label: 'quantity'
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
type: 'scenario-step-input',
|
|
51
|
-
name: 'accessorAddress',
|
|
52
|
-
label: 'accessor-address'
|
|
53
|
-
}
|
|
54
|
-
]
|
|
55
|
-
|
|
56
|
-
modbusRead.help = 'integration/task/modbus-read'
|
|
57
|
-
|
|
58
|
-
TaskRegistry.registerTaskHandler('modbus-read', modbusRead)
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { access } from '@things-factory/utils'
|
|
2
|
-
import { ConnectionManager, TaskRegistry } from '@things-factory/integration-base'
|
|
3
|
-
|
|
4
|
-
const debug = require('debug')('things-factory:modbus-write-single')
|
|
5
|
-
|
|
6
|
-
async function modbusWriteSingle(step, { logger, domain, data }) {
|
|
7
|
-
var {
|
|
8
|
-
connection,
|
|
9
|
-
params: { objectType = 'coil', accessorAddress, address, accessor, value }
|
|
10
|
-
} = step
|
|
11
|
-
|
|
12
|
-
var connectionInstance = ConnectionManager.getConnectionInstanceByName(domain, connection)
|
|
13
|
-
if (!connectionInstance) {
|
|
14
|
-
debug(`no connection : ${connection}`)
|
|
15
|
-
throw new Error(`no connection : ${connection}`)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
address = accessorAddress ? access(accessorAddress, data) : address
|
|
19
|
-
if (isNaN(address)) {
|
|
20
|
-
debug(`invalid number address : ${address}`)
|
|
21
|
-
throw new Error(`invalid number address : ${address}`)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// determine accessor or value as a input value value
|
|
25
|
-
value = accessor ? access(accessor, data) : value
|
|
26
|
-
if (isNaN(value)) {
|
|
27
|
-
throw new Error(`invalid number value : ${value}`)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
var { writeSingleModBus } = connectionInstance
|
|
31
|
-
var content = await writeSingleModBus(objectType, address, value, { logger })
|
|
32
|
-
return content
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
modbusWriteSingle.parameterSpec = [
|
|
36
|
-
{
|
|
37
|
-
type: 'select',
|
|
38
|
-
name: 'objectType',
|
|
39
|
-
label: 'object-type',
|
|
40
|
-
property: {
|
|
41
|
-
options: ['', 'coil', 'holding register']
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
type: 'number',
|
|
46
|
-
name: 'address',
|
|
47
|
-
label: 'address'
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
type: 'number',
|
|
51
|
-
name: 'value',
|
|
52
|
-
label: 'value'
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
type: 'scenario-step-input',
|
|
56
|
-
name: 'accessor',
|
|
57
|
-
label: 'accessor-value'
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
type: 'scenario-step-input',
|
|
61
|
-
name: 'accessorAddress',
|
|
62
|
-
label: 'accessor-address'
|
|
63
|
-
}
|
|
64
|
-
]
|
|
65
|
-
|
|
66
|
-
modbusWriteSingle.help = 'integration/task/modbus-write-single'
|
|
67
|
-
|
|
68
|
-
TaskRegistry.registerTaskHandler('modbus-write-single', modbusWriteSingle)
|
package/server/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import './engine'
|