@things-factory/integration-melsec 8.0.0-beta.1 → 8.0.0-beta.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/integration-melsec",
3
- "version": "8.0.0-beta.1",
3
+ "version": "8.0.0-beta.4",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "client/index.js",
6
6
  "things-factory": true,
@@ -23,8 +23,8 @@
23
23
  "clean": "npm run clean:server"
24
24
  },
25
25
  "dependencies": {
26
- "@things-factory/integration-base": "^8.0.0-beta.1",
26
+ "@things-factory/integration-base": "^8.0.0-beta.4",
27
27
  "p-queue": "^6.4.0"
28
28
  },
29
- "gitHead": "36c494e587640c1490318ef7b95adab02606e0c2"
29
+ "gitHead": "d83d12ed4ba07177dff1dac26e37be347d156b43"
30
30
  }
@@ -1 +0,0 @@
1
- import './melsec-plc'
@@ -1,211 +0,0 @@
1
- import net from 'net'
2
- import PromiseSocket from 'promise-socket'
3
- import PQueue from 'p-queue'
4
- import { sleep } from '@things-factory/utils'
5
-
6
- import { ConnectionManager, Connector, Connection } from '@things-factory/integration-base'
7
-
8
- const subHeader = '5000'
9
- const networkNumber = '00'
10
- const requireNumber = 'FF'
11
- const requireIoNumber = '03FF'
12
- const requireMultiNumber = '00'
13
- const readrequireLength = '0018'
14
- const writerequireLength = '0019'
15
- const writewordrequireLength = '001C'
16
- const reserve = '0000'
17
- const readCommand = '0401'
18
- const readWordSubCommand = '0000'
19
- const readCoilSubCommand = '0001'
20
- const writeCommand = '1401'
21
- const writeWordSubCommand = '0000'
22
- const writeSubCommand = '0001'
23
- const readLengthDevice = '0001'
24
- const writeLengthDevice = '0001'
25
-
26
- export class MelsecPLCConnector implements Connector {
27
- static getWriteCoilCommand(deviceCode, writeStartDevice, writeCoilValue, writeLength) {
28
- if (writeLength) {
29
- writeLength = writeLength.toString().padStart(4, '0')
30
- }
31
-
32
- return (
33
- subHeader +
34
- networkNumber +
35
- requireNumber +
36
- requireIoNumber +
37
- requireMultiNumber +
38
- writerequireLength +
39
- reserve +
40
- writeCommand +
41
- writeSubCommand +
42
- deviceCode +
43
- writeStartDevice +
44
- (writeLength || writeLengthDevice) +
45
- writeCoilValue
46
- )
47
- }
48
-
49
- static getWriteWordCommand(deviceCode, writeStartDevice, writeWordValue) {
50
- return (
51
- subHeader +
52
- networkNumber +
53
- requireNumber +
54
- requireIoNumber +
55
- requireMultiNumber +
56
- writewordrequireLength +
57
- reserve +
58
- writeCommand +
59
- writeWordSubCommand +
60
- deviceCode +
61
- writeStartDevice +
62
- writeLengthDevice +
63
- writeWordValue
64
- )
65
- }
66
-
67
- static getReadCoilCommand(deviceCode, readStartDevice, readLength) {
68
- if (readLength) {
69
- readLength = readLength.toString().padStart(4, '0')
70
- }
71
-
72
- return (
73
- subHeader +
74
- networkNumber +
75
- requireNumber +
76
- requireIoNumber +
77
- requireMultiNumber +
78
- readrequireLength +
79
- reserve +
80
- readCommand +
81
- readCoilSubCommand +
82
- deviceCode +
83
- readStartDevice +
84
- (readLength || readLengthDevice)
85
- )
86
- }
87
-
88
- static getReadWordCommand(deviceCode, readStartDevice) {
89
- return (
90
- subHeader +
91
- networkNumber +
92
- requireNumber +
93
- requireIoNumber +
94
- requireMultiNumber +
95
- readrequireLength +
96
- reserve +
97
- readCommand +
98
- readWordSubCommand +
99
- deviceCode +
100
- readStartDevice +
101
- readLengthDevice
102
- )
103
- }
104
-
105
- async ready(connectionConfigs) {
106
- await Promise.all(connectionConfigs.map(this.connect))
107
-
108
- ConnectionManager.logger.info('mitsubishi-plc connections are ready')
109
- }
110
-
111
- async connect(config) {
112
- if (ConnectionManager.getConnectionInstance(config)) {
113
- return
114
- }
115
-
116
- var [host, port = 1025] = config.endpoint.split(':')
117
-
118
- var socket = new PromiseSocket(new net.Socket())
119
-
120
- await socket.connect(port, host)
121
-
122
- var queue = new PQueue({ concurrency: 1 })
123
- var keepalive = true
124
-
125
- ConnectionManager.addConnectionInstance(config, {
126
- request: async function (message, length, { logger }) {
127
- return await queue.add(async () => {
128
- while (keepalive) {
129
- try {
130
- /* send request message */
131
- await socket.write(message)
132
- logger && logger.info(`Request : ${message}`)
133
- var responseCompleted = ''
134
-
135
- if (length > 0) {
136
- while (length - responseCompleted.length > 0) {
137
- logger && logger.info(`Will read ${length - responseCompleted.length}`)
138
- let response = await socket.read() // length - responseCompleted.length)
139
- if (!response) {
140
- // socket ended or closed
141
- throw new Error('socket closed')
142
- }
143
- responseCompleted += response.toString()
144
- }
145
- } else {
146
- let response = await socket.read()
147
- if (!response) {
148
- // socket ended or closed
149
- throw new Error('socket closed')
150
- }
151
- responseCompleted = response.toString()
152
- }
153
-
154
- logger && logger.info(`Response : ${responseCompleted}`)
155
- return responseCompleted
156
- } catch (e) {
157
- logger.error('plc command(write-read) failed.', e)
158
-
159
- if (keepalive) {
160
- socket && socket.destroy()
161
-
162
- socket = new PromiseSocket(new net.Socket())
163
- await socket.connect(port, host)
164
-
165
- await sleep(1000)
166
- } else {
167
- throw e
168
- }
169
- }
170
- }
171
-
172
- throw new Error(`${config.name} maybe disconnected normally`)
173
- })
174
- },
175
- close: function () {
176
- queue.clear()
177
- keepalive = false
178
- socket.destroy()
179
- }
180
- })
181
-
182
- ConnectionManager.logger.info(`mitsubishi-plc connection(${config.name}:${config.endpoint}) is connected`)
183
- }
184
-
185
- async disconnect(connection: Connection) {
186
- var { close } = ConnectionManager.removeConnectionInstance(connection)
187
- close()
188
-
189
- ConnectionManager.logger.info(`mitsubishi-plc connection(${connection.name}) is disconnected`)
190
- }
191
-
192
- get parameterSpec() {
193
- return [
194
- {
195
- type: 'checkbox',
196
- name: 'keepalive',
197
- label: 'keepalive'
198
- }
199
- ]
200
- }
201
-
202
- get taskPrefixes() {
203
- return ['melsec']
204
- }
205
-
206
- get help() {
207
- return 'integration/connector/melsec-plc'
208
- }
209
- }
210
-
211
- ConnectionManager.registerConnector('melsec-plc', new MelsecPLCConnector())
@@ -1,2 +0,0 @@
1
- import './connector'
2
- import './task'
@@ -1,5 +0,0 @@
1
- import './melsec-read-coil'
2
- import './melsec-wait-coil'
3
- import './melsec-write-coil'
4
- import './melsec-read-word'
5
- import './melsec-write-word'
@@ -1,61 +0,0 @@
1
- import { ConnectionManager, TaskRegistry } from '@things-factory/integration-base'
2
- import { MelsecPLCConnector } from '../connector/melsec-plc'
3
-
4
- async function MelsecReadCoil(step, { logger, domain }) {
5
- var {
6
- connection: connectionName,
7
- params: { plcAddress: address, readLength: readLength }
8
- } = step
9
-
10
- var connection = ConnectionManager.getConnectionInstanceByName(domain, connectionName)
11
- if (!connection) {
12
- throw new Error(`connection '${connectionName}' is not established.`)
13
- }
14
-
15
- var { request } = connection
16
-
17
- var deviceCode = address.substring(0, 1) + '*'
18
- var af_address = Number(address.substring(1)).toString()
19
- var len = af_address.length
20
- for (var i = 0; i < 6 - len; i++) {
21
- af_address = '0' + af_address
22
- }
23
- var readStartDevice = af_address
24
- var sendMessage = MelsecPLCConnector.getReadCoilCommand(deviceCode, readStartDevice, readLength)
25
-
26
- // Request : 500000FF03FF000018000004010001M*0001000001
27
- // Response : D00000FF03FF00000500000
28
- var content = await request(sendMessage, 23, { logger }) // (22 + readLength)에서 현재 readLength는 1로 고정
29
-
30
- // TODO readLength가 1이 아닐때 데이터 처리.
31
- if (content.substring(17, 18) == '5') {
32
- var data = content.substring(22, 23)
33
-
34
- logger.info(content)
35
- logger.info(`received response is ok. received: ${data}`)
36
-
37
- return {
38
- data
39
- }
40
- } else {
41
- // error
42
- throw new Error('response not applicable')
43
- }
44
- }
45
-
46
- MelsecReadCoil.parameterSpec = [
47
- {
48
- type: 'string',
49
- name: 'plcAddress',
50
- label: 'plc_address'
51
- },
52
- {
53
- type: 'number',
54
- name: 'readLength',
55
- label: 'read_length'
56
- }
57
- ]
58
-
59
- MelsecReadCoil.help = 'integration/task/melsec-read-coil'
60
-
61
- TaskRegistry.registerTaskHandler('melsec-read-coil', MelsecReadCoil)
@@ -1,60 +0,0 @@
1
- import { ConnectionManager, TaskRegistry } from '@things-factory/integration-base'
2
- import { MelsecPLCConnector } from '../connector/melsec-plc'
3
-
4
- async function MelsecReadWord(step, { logger, domain }) {
5
- var {
6
- connection: connectionName,
7
- params: { plcAddress: address, signed = false }
8
- } = step
9
-
10
- var connection = ConnectionManager.getConnectionInstanceByName(domain, connectionName)
11
- if (!connection) {
12
- throw new Error(`connection '${connectionName}' is not established.`)
13
- }
14
-
15
- var { request } = connection
16
-
17
- var deviceCode = address.substring(0, 1) + '*'
18
- var af_address = Number(address.substring(1)).toString()
19
- var len = af_address.length
20
- for (var i = 0; i < 6 - len; i++) {
21
- af_address = '0' + af_address
22
- }
23
- var readStartDevice = af_address
24
- var sendMessage = MelsecPLCConnector.getReadWordCommand(deviceCode, readStartDevice)
25
-
26
- // Request : 500000FF03FF000018000004010000D*0001010001
27
- // Response : D00000FF03FF000008000003E9
28
- var content = await request(sendMessage, 26, { logger })
29
-
30
- var wordValue = content.substring(22, 26)
31
- var data = parseInt(wordValue, 16)
32
-
33
- if (signed && (data & 0x8000) > 0) {
34
- data -= 0x10000
35
- }
36
-
37
- logger.info(content)
38
- logger.info(`received response is ok. received: ${data}`)
39
-
40
- return {
41
- data
42
- }
43
- }
44
-
45
- MelsecReadWord.parameterSpec = [
46
- {
47
- type: 'string',
48
- name: 'plcAddress',
49
- label: 'plc_address'
50
- },
51
- {
52
- type: 'checkbox',
53
- name: 'signed',
54
- label: 'signed'
55
- }
56
- ]
57
-
58
- MelsecReadWord.help = 'integration/task/melsec-read-word'
59
-
60
- TaskRegistry.registerTaskHandler('melsec-read-word', MelsecReadWord)
@@ -1,80 +0,0 @@
1
- import { ConnectionManager, Context, ScenarioInstanceStatus, TaskRegistry } from '@things-factory/integration-base'
2
- import { sleep } from '@things-factory/utils'
3
-
4
- import { MelsecPLCConnector } from '../connector/melsec-plc'
5
-
6
- async function MelsecWaitForCoil(step, { logger, root, domain }: Context) {
7
- var {
8
- connection: connectionName,
9
- params: { plcAddress: address, value, waitTerm = 50 }
10
- } = step
11
-
12
- var connection = ConnectionManager.getConnectionInstanceByName(domain, connectionName)
13
- if (!connection) {
14
- throw new Error(`connection '${connectionName}' is not established.`)
15
- }
16
-
17
- var { request } = connection
18
-
19
- var deviceCode = address.substring(0, 1) + '*'
20
- var af_address = Number(address.substring(1)).toString()
21
- var len = af_address.length
22
- for (var i = 0; i < 6 - len; i++) {
23
- af_address = '0' + af_address
24
- }
25
- var readStartDevice = af_address
26
- var sendMessage = MelsecPLCConnector.getReadCoilCommand(deviceCode, readStartDevice, undefined)
27
-
28
- while (true) {
29
- let state = root.getState()
30
- if (state == ScenarioInstanceStatus.STARTED /* STARTED */) {
31
- var content = await request(sendMessage, 23, { logger })
32
-
33
- if (content.substring(17, 18) == '5') {
34
- var coilValue = content.substring(22, 23)
35
-
36
- if (value == coilValue) {
37
- logger.info('received response is ok. required: %s, received: %s', value, coilValue)
38
-
39
- return {
40
- data: coilValue
41
- }
42
- } else {
43
- logger.info('received response, but not accepted. required: %s, received: %s', value, coilValue)
44
- await sleep(waitTerm)
45
- continue
46
- }
47
- } else {
48
- // error
49
- throw new Error('response not applicable')
50
- }
51
- } else if (state == ScenarioInstanceStatus.STOPPED /* PAUSED */) {
52
- await sleep(waitTerm)
53
- } else {
54
- throw new Error('scenario stopped unexpectedly')
55
- }
56
- }
57
- }
58
-
59
- MelsecWaitForCoil.parameterSpec = [
60
- {
61
- type: 'string',
62
- name: 'plcAddress',
63
- label: 'plc_address'
64
- },
65
- {
66
- type: 'string',
67
- name: 'value',
68
- label: 'expected_value'
69
- },
70
- {
71
- type: 'number',
72
- name: 'waitTerm',
73
- placeholder: 'milli-seconds',
74
- label: 'wait_term'
75
- }
76
- ]
77
-
78
- MelsecWaitForCoil.help = 'integration/task/melsec-wait-coil'
79
-
80
- TaskRegistry.registerTaskHandler('melsec-wait-coil', MelsecWaitForCoil)
@@ -1,99 +0,0 @@
1
- import { ConnectionManager, TaskRegistry } from '@things-factory/integration-base'
2
- import { MelsecPLCConnector } from '../connector/melsec-plc'
3
-
4
- import { sleep } from '@things-factory/utils'
5
-
6
- async function MelsecWriteCoil(step, { logger, domain }) {
7
- var {
8
- connection: connectionName,
9
- params: { plcAddress: address, value, writeLength = 1, autoReset, delay = 50 }
10
- } = step
11
-
12
- var connection = ConnectionManager.getConnectionInstanceByName(domain, connectionName)
13
- if (!connection) {
14
- throw new Error(`connection '${connectionName}' is not established.`)
15
- }
16
-
17
- var { request } = connection
18
-
19
- var w_address = address
20
- var w_value = value
21
- var deviceCode = w_address.substring(0, 1) + '*'
22
-
23
- var af_address = Number(w_address.substring(1)).toString()
24
- var len = af_address.length
25
- for (var i = 0; i < 6 - len; i++) {
26
- af_address = '0' + af_address
27
- }
28
- var writeStartDevice = af_address
29
-
30
- if (w_value == 1) {
31
- var writeCoilValue = '1'
32
- } else {
33
- var writeCoilValue = '0'
34
- }
35
-
36
- await doRequest(request, deviceCode, writeStartDevice, writeCoilValue, writeLength, logger)
37
-
38
- if (autoReset) {
39
- await sleep(delay)
40
-
41
- await doRequest(request, deviceCode, writeStartDevice, Number(!Number(writeCoilValue)), writeLength, logger)
42
- }
43
-
44
- return {
45
- data: writeCoilValue
46
- }
47
- }
48
-
49
- async function doRequest(request, deviceCode, writeStartDevice, writeCoilValue, writeLength, logger) {
50
- var sendMessage = MelsecPLCConnector.getWriteCoilCommand(deviceCode, writeStartDevice, writeCoilValue, writeLength)
51
- // Request : 500000FF03FF000019000014010001M*00033300011
52
- // Response : D00000FF03FF0000040000
53
- var content = await request(sendMessage, 22, { logger })
54
-
55
- // TODO writeLength 1이 아닐때 데이터 처리.
56
- if (content.substring(17, 18) == '4') {
57
- // ok
58
- return {
59
- data: writeCoilValue
60
- }
61
- } else {
62
- // error
63
- throw new Error('response not applicable')
64
- }
65
- }
66
-
67
- MelsecWriteCoil.parameterSpec = [
68
- {
69
- type: 'string',
70
- name: 'plcAddress',
71
- placeholder: 'M0,Y1,..',
72
- label: 'plc_address'
73
- },
74
- {
75
- type: 'number',
76
- name: 'writeLength',
77
- label: 'write_length'
78
- },
79
- {
80
- type: 'number',
81
- name: 'value',
82
- label: 'value'
83
- },
84
- {
85
- type: 'checkbox',
86
- name: 'autoReset',
87
- label: 'auto_reset'
88
- },
89
- {
90
- type: 'number',
91
- name: 'delay',
92
- placeholder: 'milisecodes, default is 50ms',
93
- label: 'reset_delay'
94
- }
95
- ]
96
-
97
- MelsecWriteCoil.help = 'integration/task/melsec-write-coil'
98
-
99
- TaskRegistry.registerTaskHandler('melsec-write-coil', MelsecWriteCoil)
@@ -1,81 +0,0 @@
1
- import { ConnectionManager, TaskRegistry } from '@things-factory/integration-base'
2
- import { access } from '@things-factory/utils'
3
- import { MelsecPLCConnector } from '../connector/melsec-plc'
4
-
5
- async function MelsecWriteWord(step, { data, logger, domain }) {
6
- var {
7
- connection: connectionName,
8
- params: { plcAddress: address, accessor, value }
9
- } = step
10
-
11
- var connection = ConnectionManager.getConnectionInstanceByName(domain, connectionName)
12
- if (!connection) {
13
- throw new Error(`connection '${connectionName}' is not established.`)
14
- }
15
-
16
- var { request } = connection
17
-
18
- var w_address = address
19
- var deviceCode = w_address.substring(0, 1) + '*'
20
-
21
- var af_address = Number(w_address.substring(1)).toString()
22
- var len = af_address.length
23
- for (var i = 0; i < 6 - len; i++) {
24
- af_address = '0' + af_address
25
- }
26
- var writeStartDevice = af_address
27
-
28
- value = accessor ? access(accessor, data) : value
29
- if (isNaN(value)) {
30
- throw new Error(`invalid number value : ${value}`)
31
- }
32
-
33
- var valueDefine = Number(value).toString(16)
34
- var writeWordValue = ''
35
-
36
- if (valueDefine.length == 1) {
37
- writeWordValue = '000' + Number(value).toString(16)
38
- } else if (valueDefine.length == 2) {
39
- writeWordValue = '00' + Number(value).toString(16)
40
- } else if (valueDefine.length == 3) {
41
- writeWordValue = '0' + Number(value).toString(16)
42
- } else if (valueDefine.length == 4) {
43
- writeWordValue = Number(value).toString(16)
44
- }
45
-
46
- // Request : 500000FF03FF00001C000014010000D*00010000010001
47
- // Response : D00000FF03FF0000040000
48
- logger.info(`will send request value: ${value}:${writeWordValue}`)
49
-
50
- var sendMessage = MelsecPLCConnector.getWriteWordCommand(deviceCode, writeStartDevice, writeWordValue)
51
- var content = await request(sendMessage, 22, { logger })
52
-
53
- logger.info(`received response: ${content}`)
54
-
55
- return {
56
- data: writeWordValue
57
- }
58
- }
59
-
60
- MelsecWriteWord.parameterSpec = [
61
- {
62
- type: 'string',
63
- name: 'plcAddress',
64
- placeholder: 'M0,Y1,..',
65
- label: 'plc_address'
66
- },
67
- {
68
- type: 'scenario-step-input',
69
- name: 'accessor',
70
- label: 'accessor'
71
- },
72
- {
73
- type: 'number',
74
- name: 'value',
75
- label: 'value'
76
- }
77
- ]
78
-
79
- MelsecWriteWord.help = 'integration/task/melsec-write-word'
80
-
81
- TaskRegistry.registerTaskHandler('melsec-write-word', MelsecWriteWord)
package/server/index.ts DELETED
@@ -1 +0,0 @@
1
- import './engine'