ads-client 1.14.3 → 2.0.0-beta.2
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/CHANGELOG.md +34 -0
- package/LICENSE.txt +1 -1
- package/README.md +326 -1756
- package/dist/ads-client.d.ts +1562 -0
- package/dist/ads-client.js +5660 -0
- package/dist/ads-client.js.map +1 -0
- package/dist/ads-commons.d.ts +810 -0
- package/dist/ads-commons.js +1247 -0
- package/dist/ads-commons.js.map +1 -0
- package/dist/client-error.d.ts +6 -0
- package/dist/client-error.js +30 -0
- package/dist/client-error.js.map +1 -0
- package/dist/types/ads-client-types.d.ts +626 -0
- package/dist/types/ads-client-types.js +5 -0
- package/dist/types/ads-client-types.js.map +1 -0
- package/dist/types/ads-protocol-types.d.ts +460 -0
- package/dist/types/ads-protocol-types.js +35 -0
- package/dist/types/ads-protocol-types.js.map +1 -0
- package/package.json +23 -14
- package/src/ads-client-ads.js +0 -1163
- package/src/ads-client.js +0 -6884
package/README.md
CHANGED
|
@@ -2,1800 +2,370 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
[](https://www.npmjs.org/package/ads-client)
|
|
5
|
-
[](https://www.paypal.com/donate/?business=KUWBXXCVGZZME&no_recurring=0¤cy_code=EUR)
|
|
6
5
|
[](https://github.com/jisotalo/ads-client)
|
|
7
6
|
[](https://choosealicense.com/licenses/mit/)
|
|
7
|
+
[](https://www.paypal.com/donate/?business=KUWBXXCVGZZME&no_recurring=0¤cy_code=EUR)
|
|
8
8
|
|
|
9
|
-
Beckhoff TwinCAT ADS client library for Node.js (unofficial).
|
|
9
|
+
Beckhoff TwinCAT ADS client library for Node.js (unofficial).
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Connect to a Beckhoff TwinCAT automation system using the ADS protocol from a Node.js app.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
## v2 project status
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
This project is currently "ready". It's maintained actively and used in projects by the author and others (also lot's of commercial projects)
|
|
15
|
+
**24.08.2024:** Beta 1 released!
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
All breaking changes are (hopefully) now done.
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
* See [milestone #2](https://github.com/jisotalo/ads-client/milestone/2) for tasks to be done.
|
|
20
|
+
* See [MIGRATION.md](https://github.com/jisotalo/ads-client/blob/v2-dev/MIGRATION.md) for guide of migrating v1->v2
|
|
21
|
+
* See [CHANGELOG.md](https://github.com/jisotalo/ads-client/blob/v2-dev/CHANGELOG.md) for some more changes
|
|
22
|
+
* Clone repository and open `docs/index.html` in browser for documentation
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
## Getting started
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
Version 2 is under development in [`v2-dev`](https://github.com/jisotalo/ads-client/tree/v2-dev) branch. It's written in TypeScript (including all types!) and will also be more optimized. At the moment basic functions *might* work but it's not ready for production use.
|
|
26
|
+
README is under construction.
|
|
26
27
|
|
|
28
|
+
### Install
|
|
29
|
+
`npm install ads-client@beta`
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
Check out the [node-red-contrib-ads-client](https://www.npmjs.com/package/node-red-contrib-ads-client) package. It's an `ads-client` wrapper for Node-RED to get the same functionality.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
# Table of contents
|
|
33
|
-
- [Installation](#installation)
|
|
34
|
-
- [Features](#features)
|
|
35
|
-
- [Supported and tested platforms](#supported-and-tested-platforms)
|
|
36
|
-
- [Connection setups and possibilities](#connection-setups-and-possibilities)
|
|
37
|
-
- [Enabling localhost support on TwinCAT 3](#enabling-localhost-support-on-twincat-3)
|
|
38
|
-
- [IMPORTANT: Writing STRUCT variables](#important-writing-struct-variables)
|
|
39
|
-
- [IMPORTANT: Things to know when using with TwinCAT 2](#important-things-to-know-when-using-with-twincat-2)
|
|
40
|
-
- [Connecting to systems without PLC runtime](#connecting-to-systems-without-plc-runtime)
|
|
41
|
-
- [Getting started](#getting-started)
|
|
42
|
-
* [Data types used in getting started](#data-types-used-in-getting-started)
|
|
43
|
-
* [Creating a new Client instance](#creating-a-new-client-instance)
|
|
44
|
-
* [Available settings](#available-settings)
|
|
45
|
-
* [Connecting and disconnecting](#connecting-and-disconnecting)
|
|
46
|
-
* [Reading any type PLC variable](#reading-any-type-plc-variable)
|
|
47
|
-
+ [Example: Reading `INT` type variable](#example-reading-int-type-variable)
|
|
48
|
-
+ [Example: Reading `STRING` type variable](#example-reading-string-type-variable)
|
|
49
|
-
+ [Example: Reading `ENUM` type variable](#example-reading-enum-type-variable)
|
|
50
|
-
+ [Example: Reading `STRUCT` type variable](#example-reading-struct-type-variable)
|
|
51
|
-
+ [Example: Reading `ARRAY OF INT` type variable](#example-reading-array-of-int-type-variable)
|
|
52
|
-
+ [Example: Reading `ARRAY OF STRUCT` type variable](#example-reading-array-of-struct-type-variable)
|
|
53
|
-
+ [Example: Reading `FUNCTION BLOCK` type variable](#example-reading-function-block-type-variable)
|
|
54
|
-
* [Writing any type PLC variable](#writing-any-type-plc-variable)
|
|
55
|
-
+ [Example: Writing `INT` type variable](#example-writing-int-type-variable)
|
|
56
|
-
+ [Example: Writing `STRING` type variable](#example-writing-string-type-variable)
|
|
57
|
-
+ [Example: Writing `ENUM` type variable](#example-writing-enum-type-variable)
|
|
58
|
-
+ [Example: Writing `STRUCT` type variable](#example-writing-struct-type-variable)
|
|
59
|
-
+ [Example: Writing `STRUCT` type variable (with autoFill parameter)](#example-writing-struct-type-variable-with-autofill-parameter-)
|
|
60
|
-
+ [Example: Writing `ARRAY OF INT` type variable](#example-writing-array-of-int-type-variable)
|
|
61
|
-
+ [Example: Writing `ARRAY of STRUCT` type variable](#example-writing-array-of-struct-type-variable)
|
|
62
|
-
+ [Example: Writing `FUNCTION BLOCK` type variable](#example-writing-function-block-type-variable)
|
|
63
|
-
* [Subscribing to PLC variables (device notifications)](#subscribing-to-plc-variables-device-notifications-)
|
|
64
|
-
+ [Subcribe to variable value (on-change)](#subcribe-to-variable-value-on-change-)
|
|
65
|
-
+ [Subcribe to variable value (cyclic)](#subcribe-to-variable-value-cyclic-)
|
|
66
|
-
* [Reading and writing raw data](#reading-and-writing-raw-data)
|
|
67
|
-
+ [Getting symbol index group, offset and size](#getting-symbol-index-group-offset-and-size)
|
|
68
|
-
+ [Reading a single raw value](#reading-a-single-raw-value)
|
|
69
|
-
+ [Writing a single raw value](#writing-a-single-raw-value)
|
|
70
|
-
+ [Reading multiple raw values](#reading-multiple-raw-values)
|
|
71
|
-
+ [Writing multiple raw values](#writing-multiple-raw-values)
|
|
72
|
-
+ [Creating a variable handle and reading a raw value](#creating-a-variable-handle-and-reading-a-raw-value)
|
|
73
|
-
+ [Creating a variable handle and writing a raw value](#creating-a-variable-handle-and-writing-a-raw-value)
|
|
74
|
-
+ [Creating and deleting multiple variable handles](#creating-and-deleting-multiple-variable-handles)
|
|
75
|
-
+ [Converting a raw value to Javascript object](#converting-a-raw-value-to-javascript-object)
|
|
76
|
-
+ [Converting a Javascript object to raw value](#converting-a-javascript-object-to-raw-value)
|
|
77
|
-
* [Reading and writing `POINTER TO` and `REFERENCE TO` variables](#reading-and-writing-pointer-to-and-reference-to-variables)
|
|
78
|
-
+ [Reading a `REFERENCE TO` value](#reading-a-reference-to-value)
|
|
79
|
-
+ [Writing a `REFERENCE TO` value](#writing-a-reference-to-value)
|
|
80
|
-
+ [Reading a `POINTER TO` value](#reading-a-pointer-to-value)
|
|
81
|
-
+ [Writing a `POINTER TO` value](#writing-a-pointer-to-value)
|
|
82
|
-
* [Calling a function block method with parameters using RPC (remote procedure call)](#calling-a-function-block-method-with-parameters-using-rpc-remote-procedure-call-)
|
|
83
|
-
+ [Calling a simple RPC method](#calling-a-simple-rpc-method)
|
|
84
|
-
+ [Using structs with RPC methods](#using-structs-with-rpc-methods)
|
|
85
|
-
* [Starting and stopping the PLC](#starting-and-stopping-the-plc)
|
|
86
|
-
* [Starting and stopping the TwinCAT system](#starting-and-stopping-the-twincat-system)
|
|
87
|
-
* [Sending custom ADS commands](#sending-custom-ads-commands)
|
|
88
|
-
- [Available ads-client events](#available-ads-client-events)
|
|
89
|
-
* [Example: Printing to console when PLC runtime state changes:](#example-printing-to-console-when-plc-runtime-state-changes-)
|
|
90
|
-
* [Example: Catching an error that is not directly from a method call](#example-catching-an-error-that-is-not-directly-from-a-method-call)
|
|
91
|
-
- [Debugging](#debugging)
|
|
92
|
-
* [Enabling debug from code](#enabling-debug-from-code)
|
|
93
|
-
* [Enabling debugging from terminal](#enabling-debugging-from-terminal)
|
|
94
|
-
- [FAQ](#faq)
|
|
95
|
-
- [Automatic testing](#automatic-testing)
|
|
96
|
-
- [Documentation](#documentation)
|
|
97
|
-
- [License](#license)
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
# Installation
|
|
101
|
-
|
|
102
|
-
Install the [npm package](https://www.npmjs.com/package/ads-client) using npm command:
|
|
103
|
-
```bash
|
|
104
|
-
npm i ads-client
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
If you are using TypeScript, install unofficial types using npm command (thanks [Christian Rishøj](https://github.com/crishoj)):
|
|
108
|
-
```bash
|
|
109
|
-
npm install --save @types/ads-client
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
*Note: Version 2 under development will be written in 100% TypeScript*
|
|
113
|
-
|
|
114
|
-
Include the module in your code
|
|
115
|
-
```js
|
|
116
|
-
const ads = require('ads-client')
|
|
117
|
-
```
|
|
118
|
-
# Features
|
|
119
|
-
|
|
120
|
-
- TwinCAT 2 and TwinCAT 3 support
|
|
121
|
-
- Promise and async/await support
|
|
122
|
-
- Supports connecting to the local TwinCAT 3 runtime (see [enabling localhost support](#localhost-support))
|
|
123
|
-
- Supports multiple connections from the same host
|
|
124
|
-
- Reading and writing all any PLC variable
|
|
125
|
-
- Subscribing to PLC variables (ADS notifications)
|
|
126
|
-
- Automatic conversion between PLC<->Javascript objects
|
|
127
|
-
- PLC symbol and data type handling and caching
|
|
128
|
-
- Reading PLC runtime and system manager states
|
|
129
|
-
- Automatic 32/64 bit variable support (PVOID, XINT, etc.)
|
|
130
|
-
- Automatic cache and subscription refreshing when PLC program changes or system starts
|
|
131
|
-
- Automatic byte alignment support (all pack-modes automatically supported)
|
|
132
|
-
- **From version 1.6.0 upwards**
|
|
133
|
-
- Older versions: `{attribute 'pack_mode' := '1'}` is required above STRUCT definition
|
|
134
|
-
- Possibility to call function block methods (RPC - remote procedure call)
|
|
135
|
-
- **From version 1.8.0 upwards**
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
# Supported and tested platforms
|
|
139
|
-
|
|
140
|
-
The ads-client package is tested so far with the following setups:
|
|
141
|
-
- TwinCAT 2.11 running on VirtualBox Windows XP 32bit **(from version 1.9.0 upwards)**
|
|
142
|
-
- TwinCAT 2 support not tested with hardware PLC yet but should work
|
|
143
|
-
- TwinCAT 3 4020 running on 64bit Windows 10 **(TC3 builds <= 4020 are working from version 1.9.0 upwards)**
|
|
144
|
-
- TwinCAT 3 4022.27 running on 64bit Windows 10
|
|
145
|
-
- TwinCAT 3 4024.4 running on 64bit Windows 10
|
|
146
|
-
- TwinCAT 3 4022.27 running on 32bit and 64bit Windows 7 Embedded on Beckhoff PLC
|
|
147
|
-
- TwinCAT 3 4024.12 running on 64bit Windows 7 Embedded on Beckhoff PLC
|
|
148
|
-
- Node.js v10.16.3 and newer
|
|
149
|
-
- NOTE: 64 bit integer values are supported only with Node.js v.12+
|
|
150
|
-
|
|
151
|
-
Tested on the following operating systems / platforms
|
|
152
|
-
- Windows 7 and 10
|
|
153
|
-
- Raspberry Pi 4
|
|
154
|
-
- OSX Catalina
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
# Connection setups and possibilities
|
|
158
|
-
|
|
159
|
-
The ads-client can be used in different system configurations. The following figure has different possible setups:
|
|
160
|
-
|
|
161
|
-
[](https://user-images.githubusercontent.com/13457157/82724547-8dde0800-9cdf-11ea-8dd1-0a1f06f8559f.PNG)
|
|
162
|
-
|
|
163
|
-
## Setup 1 - Connect from Windows PC to the PLC
|
|
164
|
-
|
|
165
|
-
Suggested use cases:
|
|
166
|
-
- When using Windows operating system and TwinCAT runtime can be installed
|
|
167
|
-
- When opening a TCP port from PLC is a no-go
|
|
168
|
-
|
|
169
|
-
Requirements:
|
|
170
|
-
- Client has TwinCAT runtime or XAE installed
|
|
171
|
-
- ADS route is created between the client and the PLC
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
Example connection to PLC with AmsNetId of `192.168.1.120.1.1`.
|
|
175
|
-
```js
|
|
176
|
-
const client = new ads.Client({
|
|
177
|
-
targetAmsNetId: '192.168.1.120.1.1',
|
|
178
|
-
targetAdsPort: 851,
|
|
179
|
-
})
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
## Setup 2 - Connecting from Unix/Windows/etc. system to the PLC
|
|
183
|
-
|
|
184
|
-
Suggested use cases:
|
|
185
|
-
- On Windows when TwinCAT installation is not possible, but .NET Core is available
|
|
186
|
-
- Unix based system and .NET Core available
|
|
187
|
-
|
|
188
|
-
Requirements:
|
|
189
|
-
- Client has [AdsRouterConsole](https://www.nuget.org/packages/Beckhoff.TwinCAT.Ads.AdsRouterConsole/5.0.0-preview4) running
|
|
190
|
-
- ADS route is created between the client and the PLC (as in the nuget package instructions)
|
|
191
|
-
|
|
192
|
-
Example connection when PLC has AmsNetId of `192.168.1.120.1.1`.
|
|
193
|
-
```js
|
|
194
|
-
const client = new ads.Client({
|
|
195
|
-
targetAmsNetId: '192.168.1.120.1.1',
|
|
196
|
-
targetAdsPort: 851,
|
|
197
|
-
})
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
## Setup 3 - Connecting from any Node.js supported system to the PLC
|
|
203
|
-
|
|
204
|
-
Suggested use cases:
|
|
205
|
-
- When opening TCP port from PLC is possible
|
|
206
|
-
- When fast connection is required (1 router less -> faster response)
|
|
207
|
-
|
|
208
|
-
Requirements:
|
|
209
|
-
- NOTE: Only one connection / client is possible!
|
|
210
|
-
- PLC has TCP port 48898 open (default router port)
|
|
211
|
-
- NOTE: Windows Firewall might block, make sure Ethernet connection is handled as "private"
|
|
212
|
-
- Local AmsNetId and ADS port are given manually
|
|
213
|
-
- Given `localAmsNetId` is not already in use
|
|
214
|
-
- Given `localAdsPort` is not already in use
|
|
215
|
-
- PLC has ADS route manually created to client IP address and client `localAmsNetId`
|
|
216
|
-
- See example after code sample
|
|
217
|
-
|
|
218
|
-
Example connection when PLC has AmsNetId of `192.168.1.120.1.1` and IP of `192.168.1.120`.
|
|
219
|
-
```js
|
|
220
|
-
const client = new ads.Client({
|
|
221
|
-
localAmsNetId: '192.168.1.10.1.1', //Can be anything but needs to be in PLC StaticRoutes.xml file
|
|
222
|
-
localAdsPort: 32750, //Can be anything that is not used
|
|
223
|
-
targetAmsNetId: '192.168.1.120.1.1',
|
|
224
|
-
targetAdsPort: 851,
|
|
225
|
-
routerAddress: '192.168.1.120', //PLC ip address
|
|
226
|
-
routerTcpPort: 48898 //PLC needs to have this port opened. Test disabling all firewalls if problems
|
|
227
|
-
})
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
Adding a route to the PLC can be done editing `TwinCAT\3.1\Target\StaticRoutes.xml` file from PLC. Add the following inside `<RemoteConnections>` tag. In this example, client should use meanual AmsNetId `192.168.1.10.1.1` and client has IP address `192.168.1.10`.
|
|
231
|
-
```xml
|
|
232
|
-
<Route>
|
|
233
|
-
<Name>UI</Name>
|
|
234
|
-
<Address>192.168.1.10</Address>
|
|
235
|
-
<NetId>192.168.1.10.1.1</NetId>
|
|
236
|
-
<Type>TCP_IP</Type>
|
|
237
|
-
<Flags>64</Flags>
|
|
238
|
-
</Route>
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
See also [this issue comment](https://github.com/jisotalo/ads-client/issues/51#issuecomment-758016428) by hansipete for one possible way how to do adding the route.
|
|
242
|
-
|
|
243
|
-
## Setup 4 - Connect to the localhost (PLC and client on the same machine)
|
|
244
|
-
|
|
245
|
-
Suggested use cases:
|
|
246
|
-
- When testing PLC systems on the local computer
|
|
247
|
-
- When using panel PC/PLC combination
|
|
248
|
-
- When PLC has monitor and it's used as user interface
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
Requirements:
|
|
252
|
-
- AMS router TCP loopback enabled (see [Enabling localhost support](#enabling-localhost-support))
|
|
253
|
-
|
|
254
|
-
Example connection to local PLC runtime
|
|
255
|
-
|
|
256
|
-
```js
|
|
257
|
-
const client = new ads.Client({
|
|
258
|
-
targetAmsNetId: '127.0.0.1.1.1', //or 'localhost'
|
|
259
|
-
targetAdsPort: 851,
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
# Enabling localhost support on TwinCAT 3
|
|
266
|
-
*NOTE: Only required for TwinCAT 3 versions older than 4024.5. Newer versions should have this already enabled.*
|
|
267
|
-
|
|
268
|
-
***Note:** Probably not working for TwinCAT 2*
|
|
269
|
-
|
|
270
|
-
If you want to connect to the local TwinCAT runtime (Node.js and the TwinCAT on the same computer - **as example setup 4**), the ADS router TCP loopback feature has to be enabled. Tested with TwinCAT 4022.27 and 4024.4.
|
|
271
|
-
|
|
272
|
-
The following method is from [Beckhoff.TwinCAT.Ads nuget package](https://www.nuget.org/packages/Beckhoff.TwinCAT.Ads/5.0.0-preview6) installation guide.
|
|
273
|
-
|
|
274
|
-
1. Stop TwinCAT System Service
|
|
275
|
-
|
|
276
|
-
2. Open registery editor (`regedit`)
|
|
277
|
-
|
|
278
|
-
3. Depending on the operating system, navigate to
|
|
279
|
-
```
|
|
280
|
-
32 bit operating system:
|
|
281
|
-
HKEY_LOCAL_MACHINE\SOFTWARE\Beckhoff\TwinCAT3\System\
|
|
282
|
-
|
|
283
|
-
64 bit it operating system:
|
|
284
|
-
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Beckhoff\TwinCAT3\System\
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
4. Create new DWORD registery named `EnableAmsTcpLoopback` and set value to 1 (example figure below from 64 bit system)
|
|
288
|
-
|
|
289
|
-

|
|
290
|
-
|
|
291
|
-
5. Restart TwinCAT system
|
|
292
|
-
|
|
293
|
-
Now you can connect to the localhost using `targetAmsNetId` address of `127.0.0.1.1.1` or `localhost`.
|
|
294
|
-
|
|
295
|
-
# IMPORTANT: Writing STRUCT variables
|
|
296
|
-
|
|
297
|
-
When writing a struct using `writeSymbol`, the given Javascript object keys are handled as case-insensitive because the TwinCAT 3 system is case-insensitive.
|
|
298
|
-
|
|
299
|
-
In practise this means that the following Javascript objects are used as-equals if passing to the `writeSymbol` method:
|
|
300
|
-
|
|
301
|
-
```js
|
|
302
|
-
{
|
|
303
|
-
sometext: 'hello',
|
|
304
|
-
somereal: 3.14
|
|
305
|
-
}
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
```js
|
|
309
|
-
{
|
|
310
|
-
SOmeTEXT: 'hello',
|
|
311
|
-
SOMEreal: 3.14
|
|
312
|
-
}
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
**NOTE** If the object has multiple keys with same name, `writeSymbol` tries to find the same case as in PLC. If it's not found, it depends on the `Object.find()` method which one is selected.
|
|
316
|
-
|
|
317
|
-
```js
|
|
318
|
-
//In this case, probably the first one (sometext) is selected and the SOMEtext is skipped.
|
|
319
|
-
{
|
|
320
|
-
sometext: 'hello',
|
|
321
|
-
SOMEtext: 'good day'
|
|
322
|
-
}
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
# IMPORTANT: Things to know when using with TwinCAT 2
|
|
327
|
-
|
|
328
|
-
Almost everything should work with TwinCAT 2 but please understand the following
|
|
329
|
-
|
|
330
|
-
## TwinCAT 2 first PLC runtime ADS port is 801 instead of 851
|
|
331
|
-
|
|
332
|
-
```js
|
|
333
|
-
const client = new ads.Client({
|
|
334
|
-
targetAmsNetId: '...',
|
|
335
|
-
targetAdsPort: 801, //NOTE
|
|
336
|
-
})
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
## Variable names are returned in UPPERCASE on TC2 systems
|
|
340
|
-
This might cause problems if your app is used with both TC2 & TC3 systems.
|
|
341
|
-
|
|
342
|
-

|
|
343
|
-
|
|
344
|
-
## Global variable paths are given different on TC2
|
|
345
|
-
|
|
346
|
-
The GVL name is not given, dot (.) is used instead.
|
|
347
|
-
|
|
348
|
-
```js
|
|
349
|
-
await client.readSymbol('GVL_Test.ExampleSTRUCT') //TwinCAT 3
|
|
350
|
-
await client.readSymbol('.ExampleSTRUCT') //TwinCAT 2
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
## InvokeRpcMethod is not possible on TC2
|
|
354
|
-
This is the only one non-working feature as there are no methods in TC2.
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
# Connecting to systems without PLC runtime
|
|
358
|
-
|
|
359
|
-
Since version 1.13.0 it's possible to connect to systems without PLC runtime and/or system manager using `ads-client`.
|
|
360
|
-
|
|
361
|
-
In previous versions, the client always checked the system state (RUN, CONFIG). However when connecting to different systems (non-PLC systems), there might be no system manager service. With default configuration this causes an error:
|
|
362
|
-
```
|
|
363
|
-
ClientException: Connection failed: Device system manager state read failed
|
|
364
|
-
```
|
|
365
|
-
|
|
366
|
-
By providing `bareClient` setting, the client connects to the router or target and nothing else. After that, the client can be used to read/write data. However, connection losses etc. need to be handled by the user.
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
```js
|
|
370
|
-
const client = new ads.Client({
|
|
371
|
-
targetAmsNetId: '192.168.5.131.3.1',
|
|
372
|
-
targetAdsPort: 1002,
|
|
373
|
-
bareClient: true //NOTE
|
|
374
|
-
})
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
# Getting started
|
|
378
|
-
|
|
379
|
-
This chapter includes some short getting started examples. See the JSDoc [documentation](#documentation) for detailed description of library classes and methods.
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
## Data types used in getting started
|
|
383
|
-
|
|
384
|
-
These examples assume that the PLC has the following Global Variable List (GVL):
|
|
385
|
-
```
|
|
386
|
-
//GVL_Test
|
|
387
|
-
VAR_GLOBAL
|
|
388
|
-
TestINT : INT := 1234;
|
|
389
|
-
TestSTRING : STRING := 'Hello this is a test string';
|
|
390
|
-
ExampleSTRUCT : ST_Example;
|
|
391
|
-
TestARRAY : ARRAY[0..4] OF INT := [0, 10, 200, 3000, 4000];
|
|
392
|
-
TestARRAY2 : ARRAY[0..4] OF ST_Example := [(SomeText := 'Just for demo purposes')];
|
|
393
|
-
TestENUM : E_TestEnum := E_TestEnum.Running;
|
|
394
|
-
IncrementingValue : INT; //This should change every 500 ms or so
|
|
395
|
-
TestTimer : TON := (PT := T#2S500MS);
|
|
396
|
-
END_VAR
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
The `ST_Example` should be defined as below:
|
|
400
|
-
```
|
|
401
|
-
TYPE ST_Example :
|
|
402
|
-
STRUCT
|
|
403
|
-
SomeText : STRING(50) := 'Hello ads-client';
|
|
404
|
-
SomeReal : REAL := 3.14159265359;
|
|
405
|
-
SomeDate : DT := DT#2020-4-13-12:25:33;
|
|
406
|
-
END_STRUCT
|
|
407
|
-
END_TYPE
|
|
408
|
-
```
|
|
409
|
-
|
|
410
|
-
The `E_TestEnum` should be defined as below:
|
|
411
|
-
```
|
|
412
|
-
TYPE E_TestEnum :
|
|
413
|
-
(
|
|
414
|
-
Disabled := 0,
|
|
415
|
-
Starting := 50,
|
|
416
|
-
Running := 100,
|
|
417
|
-
Stopping := 200
|
|
418
|
-
);
|
|
419
|
-
END_TYPE
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
## Creating a new Client instance
|
|
423
|
-
|
|
424
|
-
The constructor takes settings as its parameter. Two settings are mandatory: `targetAmsNetId` and `targetAdsPort`. The first is the target PLC system AmsNetId (like *127.0.0.1.1.1* or *192.168.1.10.1.1*) and the latter is target system ADS port (*like 851 for TwinCAT 3 runtime 1*).
|
|
425
|
-
|
|
426
|
-
```js
|
|
427
|
-
const ads = require('ads-client')
|
|
428
|
-
|
|
429
|
-
//Creates a new Client instance and sets the local system and TC3 runtime 1 as target
|
|
430
|
-
const client = new ads.Client({
|
|
431
|
-
targetAmsNetId: '127.0.0.1.1.1', //Loopback address, same as 'localhost' since version 1.1.0
|
|
432
|
-
targetAdsPort: 851,
|
|
433
|
-
})
|
|
434
|
-
```
|
|
435
|
-
### Available settings
|
|
436
|
-
**REQUIRED:**
|
|
437
|
-
* `targetAmsNetId`
|
|
438
|
-
* Target system AmsNetId
|
|
439
|
-
* Use `127.0.0.1.1.1` or `localhost` if connecting to a local system
|
|
440
|
-
* `targetAdsPort`
|
|
441
|
-
* Target system ADS port.
|
|
442
|
-
* TwinCAT 3: 851 (1st runtime), 852 (2nd runtime) and so on
|
|
443
|
-
* TwinCAT 2: 801 (1st runtime)
|
|
444
|
-
|
|
445
|
-
**Optional:**
|
|
446
|
-
* `objectifyEnumerations`
|
|
447
|
-
* Default value: `true`
|
|
448
|
-
* If true, read ENUM data types are converted to objects instead of numbers, e.g. `{name: 'enumValue', value: 5}` instead of 5
|
|
449
|
-
* `convertDatesToJavascript`
|
|
450
|
-
* Default value: `true`
|
|
451
|
-
* If true, PLC DT (DATE_AND_TIME) and DATE types are converted to Javascript dates
|
|
452
|
-
* `readAndCacheSymbols`
|
|
453
|
-
* Default value: `false`
|
|
454
|
-
* If true, **all** PLC symbols are cached during connecting. Otherwise they are read and cached only when needed
|
|
455
|
-
* `readAndCacheDataTypes`
|
|
456
|
-
* Default value: `false`
|
|
457
|
-
* If true, **all** PLC data types are cached during connecting. Otherwise they are read and cached only when needed
|
|
458
|
-
* `disableSymbolVersionMonitoring`
|
|
459
|
-
* Default value: `false`
|
|
460
|
-
* If true, PLC symbol version changes aren't monitored and cached symbols and datatypes won't be updated after PLC program download
|
|
461
|
-
* `routerTcpPort`
|
|
462
|
-
* Default value: `48898`
|
|
463
|
-
* Target ADS router TCP port
|
|
464
|
-
* `routerAddress`
|
|
465
|
-
* Default value: `localhost`
|
|
466
|
-
* Target ADS router IP address/hostname
|
|
467
|
-
* `localAddress`
|
|
468
|
-
* Default value: `system default`
|
|
469
|
-
* Local IP address to use, use this to change used network interface if required
|
|
470
|
-
* `localTcpPort`
|
|
471
|
-
* Default value: `system default`
|
|
472
|
-
* Local TCP port to use for outgoing connections
|
|
473
|
-
* `localAmsNetId`
|
|
474
|
-
* Default value: `AMS router provides`
|
|
475
|
-
* Local AmsNetId to use
|
|
476
|
-
* Used especially when connecting from systems without own AMS router, like Raspberry Pi
|
|
477
|
-
* See: [Connecting from any Node.js supported system to the PLC](#setup-3---connecting-from-any-nodejs-supported-system-to-the-plc)
|
|
478
|
-
* `localAdsPort`
|
|
479
|
-
* Default value: `AMS router provides`
|
|
480
|
-
* Local ADS port to use
|
|
481
|
-
* Used especially when connecting from systems without own AMS router, like Raspberry Pi
|
|
482
|
-
* See: [Connecting from any Node.js supported system to the PLC](#setup-3---connecting-from-any-nodejs-supported-system-to-the-plc)
|
|
483
|
-
* `timeoutDelay`
|
|
484
|
-
* Default value: `2000`
|
|
485
|
-
* Time (milliseconds) after connecting to the router or waiting for command response is canceled to a timeout
|
|
486
|
-
* `hideConsoleWarnings`
|
|
487
|
-
* Default value: `false`
|
|
488
|
-
* If true, no warnings are written to console (=nothing is **ever** written to console)
|
|
489
|
-
* `autoReconnect`
|
|
490
|
-
* Default value: `true`
|
|
491
|
-
* If true and connection is lost, the client tries to reconnect automatically
|
|
492
|
-
* `reconnectInterval`
|
|
493
|
-
* Default value: `2000`
|
|
494
|
-
* Time (milliseconds) how often the lost connection is tried to re-establish
|
|
495
|
-
* `checkStateInterval`
|
|
496
|
-
* Default value: `1000`
|
|
497
|
-
* Time (milliseconds) how often the system manager state is read to see if connection is OK
|
|
498
|
-
* `connectionDownDelay`
|
|
499
|
-
* Default value: `5000`
|
|
500
|
-
* Time (milliseconds) after no successful reading of the system manager state the connection is determined to be lost
|
|
501
|
-
* `allowHalfOpen`
|
|
502
|
-
* Default value: `false`
|
|
503
|
-
* If true, connect() is successful even if no PLC runtime is found (but target and system manager are available) - Can be useful if it's ok that after connecting the PLC runtime is not immediately available (example: connecting before uploading PLC code and reading data later) -
|
|
504
|
-
* WARNING: If true, reinitializing subscriptions might fail after connection loss.
|
|
505
|
-
* `disableBigInt`
|
|
506
|
-
* Default value: `false`
|
|
507
|
-
* If true, 64-bit integer PLC variables are kept as `Buffer` objects instead of converting to Javascript `BigInt` variables (JSON.strigify and libraries that use it have no BigInt support)
|
|
508
|
-
* `bareClient`
|
|
509
|
-
* Default value: `false`
|
|
510
|
-
* If true, only direct ads connection is established (no system manager)
|
|
511
|
-
* Can be used to connect to systems without PLC runtime
|
|
512
|
-
* See [Connecting to systems without PLC runtime](#connecting-to-systems-without-plc-runtime)
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
## Connecting and disconnecting
|
|
516
|
-
|
|
517
|
-
Connecting to the local TwinCAT 3 runtime 1 (port is 851) using local ADS router (= you have TwinCAT ADS router installed).
|
|
518
|
-
|
|
519
|
-
```js
|
|
520
|
-
const ads = require('ads-client')
|
|
521
|
-
|
|
522
|
-
const client = new ads.Client({
|
|
523
|
-
targetAmsNetId: '127.0.0.1.1.1',
|
|
524
|
-
targetAdsPort: 851
|
|
525
|
-
})
|
|
526
|
-
|
|
527
|
-
client.connect()
|
|
528
|
-
.then(res => {
|
|
529
|
-
console.log(`Connected to the ${res.targetAmsNetId}`)
|
|
530
|
-
console.log(`Router assigned us AmsNetId ${res.localAmsNetId} and port ${res.localAdsPort}`)
|
|
531
|
-
|
|
532
|
-
return client.disconnect()
|
|
533
|
-
})
|
|
534
|
-
.then(() => {
|
|
535
|
-
console.log('Disconnected')
|
|
536
|
-
})
|
|
537
|
-
.catch(err => {
|
|
538
|
-
console.log('Something failed:', err)
|
|
539
|
-
})
|
|
540
|
-
|
|
541
|
-
/*
|
|
542
|
-
Example console output:
|
|
543
|
-
|
|
544
|
-
Connected to the 127.0.0.1.1.1
|
|
545
|
-
Router assigned us AmsNetId 192.168.1.1.1.1 and port 36837
|
|
546
|
-
Disconnected
|
|
547
|
-
*/
|
|
548
|
-
```
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
## Reading any type PLC variable
|
|
552
|
-
|
|
553
|
-
Using `readSymbol` method it is possible to read variables of **any type** from the PLC (except references and pointers). These include base scalar type variables, structs, function blocks, arrays and so on. These examples cover just a few cases.
|
|
554
|
-
|
|
555
|
-
See full `readSymbol` documentation [from the docs](https://jisotalo.github.io/ads-client/Client.html#readSymbol).
|
|
556
|
-
|
|
557
|
-
For references and pointers see chapter [Reading and writing POINTER TO and REFERENCE TO variables](#reading-and-writing-pointer-to-and-reference-to-variables).
|
|
558
|
-
|
|
559
|
-
### Example: Reading `INT` type variable
|
|
560
|
-
```js
|
|
561
|
-
//Using Promises
|
|
562
|
-
client.readSymbol('GVL_Test.TestSTRING')
|
|
563
|
-
.then(res => {
|
|
564
|
-
console.log(`Value read: ${res.value}`)
|
|
565
|
-
})
|
|
566
|
-
.catch(err => {
|
|
567
|
-
console.log('Something failed:', err)
|
|
568
|
-
})
|
|
569
|
-
|
|
570
|
-
/*
|
|
571
|
-
Example console output:
|
|
572
|
-
|
|
573
|
-
Value read: 1234
|
|
574
|
-
*/
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
---
|
|
578
|
-
|
|
579
|
-
### Example: Reading `STRING` type variable
|
|
580
|
-
```js
|
|
581
|
-
client.readSymbol('GVL_Test.TestSTRING')
|
|
582
|
-
.then(res => {
|
|
583
|
-
console.log(`Value read: ${res.value}`)
|
|
584
|
-
})
|
|
585
|
-
.catch(err => {
|
|
586
|
-
console.log('Something failed:', err)
|
|
587
|
-
})
|
|
588
|
-
/*
|
|
589
|
-
Example console output:
|
|
590
|
-
|
|
591
|
-
Value read: Hello this is a test string
|
|
592
|
-
*/
|
|
593
|
-
```
|
|
594
|
-
|
|
595
|
-
---
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
### Example: Reading `ENUM` type variable
|
|
600
|
-
|
|
601
|
-
If setting `objectifyEnumerations` is set to false, only ENUM value (number) is returned. As default, both string representation and integer value are returned.
|
|
602
|
-
|
|
603
|
-
```js
|
|
604
|
-
//objectifyEnumerations: true
|
|
605
|
-
client.readSymbol('GVL_Test.TestENUM')
|
|
606
|
-
.then(res => {
|
|
607
|
-
console.log(`Value read: ${res.value}`)
|
|
608
|
-
})
|
|
609
|
-
.catch(err => {
|
|
610
|
-
console.log('Something failed:', err)
|
|
611
|
-
})
|
|
612
|
-
|
|
613
|
-
/*
|
|
614
|
-
Example console output:
|
|
615
|
-
|
|
616
|
-
Value read: { name: 'Running', value: 100 }
|
|
617
|
-
*/
|
|
618
|
-
```
|
|
619
|
-
|
|
620
|
-
---
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
### Example: Reading `STRUCT` type variable
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
```js
|
|
627
|
-
//Using await for example purposes
|
|
628
|
-
try {
|
|
629
|
-
const res = await client.readSymbol('GVL_Test.ExampleSTRUCT')
|
|
630
|
-
|
|
631
|
-
console.log('Value read:', res.value)
|
|
632
|
-
} catch (err) {
|
|
633
|
-
console.log('Reading failed:', err)
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
/*
|
|
637
|
-
Example console output:
|
|
638
|
-
|
|
639
|
-
Value read: { SomeText: 'Hello ads-client',
|
|
640
|
-
SomeReal: 3.1415927410125732,
|
|
641
|
-
SomeDate: 2020-04-13T12:25:33.000Z }
|
|
642
|
-
*/
|
|
643
|
-
```
|
|
644
|
-
|
|
645
|
-
---
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
### Example: Reading `ARRAY OF INT` type variable
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
```js
|
|
653
|
-
try {
|
|
654
|
-
const res = await client.readSymbol('GVL_Test.TestARRAY')
|
|
655
|
-
|
|
656
|
-
console.log('Value read:', res.value)
|
|
657
|
-
} catch (err) {
|
|
658
|
-
console.log('Reading failed:', err)
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
/*
|
|
662
|
-
Example console output:
|
|
663
|
-
|
|
664
|
-
Value read: [ 0, 10, 200, 3000, 4000 ]
|
|
665
|
-
*/
|
|
666
|
-
```
|
|
667
|
-
|
|
668
|
-
---
|
|
669
|
-
|
|
670
|
-
### Example: Reading `ARRAY OF STRUCT` type variable
|
|
671
|
-
|
|
672
|
-
```javascript
|
|
673
|
-
try {
|
|
674
|
-
const res = await client.readSymbol('GVL_Test.TestARRAY2')
|
|
675
|
-
|
|
676
|
-
console.log('Value read:', res.value)
|
|
677
|
-
} catch (err) {
|
|
678
|
-
console.log('Reading failed:', err)
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
/*
|
|
682
|
-
Example console output:
|
|
683
|
-
|
|
684
|
-
Value read: [ { SomeText: 'Just for demo purposes',
|
|
685
|
-
SomeReal: 3.1415927410125732,
|
|
686
|
-
SomeDate: 2020-04-13T12:25:33.000Z },
|
|
687
|
-
{ SomeText: 'Hello ads-client',
|
|
688
|
-
SomeReal: 3.1415927410125732,
|
|
689
|
-
SomeDate: 2020-04-13T12:25:33.000Z },
|
|
690
|
-
{ SomeText: 'Hello ads-client',
|
|
691
|
-
SomeReal: 3.1415927410125732,
|
|
692
|
-
SomeDate: 2020-04-13T12:25:33.000Z },
|
|
693
|
-
{ SomeText: 'Hello ads-client',
|
|
694
|
-
SomeReal: 3.1415927410125732,
|
|
695
|
-
SomeDate: 2020-04-13T12:25:33.000Z },
|
|
696
|
-
{ SomeText: 'Hello ads-client',
|
|
697
|
-
SomeReal: 3.1415927410125732,
|
|
698
|
-
SomeDate: 2020-04-13T12:25:33.000Z } ]
|
|
699
|
-
*/
|
|
700
|
-
```
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
----
|
|
704
|
-
|
|
705
|
-
### Example: Reading `FUNCTION BLOCK` type variable
|
|
706
|
-
|
|
707
|
-
Example of reading `TON` timer function block.
|
|
708
|
-
|
|
709
|
-
```javascript
|
|
710
|
-
try {
|
|
711
|
-
const res = await client.readSymbol('GVL_Test.TestTimer')
|
|
712
|
-
|
|
713
|
-
console.log('Value read:', res.value)
|
|
714
|
-
} catch (err) {
|
|
715
|
-
console.log('Reading failed:', err)
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
/*
|
|
719
|
-
Example console output:
|
|
720
|
-
|
|
721
|
-
Value read: { IN: false, PT: 2500, Q: false, ET: 0, M: false, StartTime: 0 }
|
|
722
|
-
*/
|
|
723
|
-
```
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
----
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
## Writing any type PLC variable
|
|
730
|
-
|
|
731
|
-
Using `writeSymbol` method it is possible to write variables of **any type** to the PLC (except references and pointers). These include base scalar type variables, structs, function blocks, arrays and so on. These examples cover just a few cases.
|
|
732
|
-
|
|
733
|
-
See full `writeSymbol` documentation [from the docs](https://jisotalo.github.io/ads-client/Client.html#writeSymbol).
|
|
734
|
-
|
|
735
|
-
For references and pointers see chapter [Reading and writing POINTER TO and REFERENCE TO variables](#reading-and-writing-pointer-to-and-reference-to-variables).
|
|
736
|
-
|
|
737
|
-
---
|
|
738
|
-
|
|
739
|
-
### Example: Writing `INT` type variable
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
```js
|
|
743
|
-
try {
|
|
744
|
-
const res = await client.writeSymbol('GVL_Test.TestINT', 5)
|
|
745
|
-
|
|
746
|
-
console.log('Value written:', res.value)
|
|
747
|
-
} catch (err) {
|
|
748
|
-
console.log('Something failed:', err)
|
|
749
|
-
}
|
|
750
|
-
```
|
|
751
|
-
|
|
752
|
-
---
|
|
753
|
-
|
|
754
|
-
### Example: Writing `STRING` type variable
|
|
755
|
-
|
|
756
|
-
```js
|
|
757
|
-
|
|
758
|
-
try {
|
|
759
|
-
const res = await client.writeSymbol('GVL_Test.TestSTRING', 'Changing the string value to this')
|
|
760
|
-
|
|
761
|
-
console.log('Value written:', res.value)
|
|
762
|
-
} catch (err) {
|
|
763
|
-
console.log('Something failed:', err)
|
|
764
|
-
}
|
|
765
|
-
```
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
---
|
|
769
|
-
|
|
770
|
-
### Example: Writing `ENUM` type variable
|
|
771
|
-
|
|
772
|
-
When writing `ENUM` value, it can always be given as number or string.
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
```js
|
|
776
|
-
try {
|
|
777
|
-
const res = await client.writeSymbol('GVL_Test.TestENUM', 'Starting')
|
|
778
|
-
//The following does the same:
|
|
779
|
-
//const res = await client.writeSymbol('GVL_Test.TestENUM', 50)
|
|
780
|
-
|
|
781
|
-
console.log('Value written:', res.value)
|
|
782
|
-
} catch (err) {
|
|
783
|
-
console.log('Something failed:', err)
|
|
784
|
-
}
|
|
785
|
-
```
|
|
786
|
-
|
|
787
|
-
---
|
|
788
|
-
### Example: Writing `STRUCT` type variable
|
|
789
|
-
|
|
790
|
-
```js
|
|
791
|
-
try {
|
|
792
|
-
const res = await client.writeSymbol('GVL_Test.ExampleSTRUCT', {
|
|
793
|
-
SomeText: 'Hello to you too, Mr. PLC!',
|
|
794
|
-
SomeReal: 5456.06854,
|
|
795
|
-
SomeDate: new Date()
|
|
796
|
-
})
|
|
797
|
-
|
|
798
|
-
console.log('Value written:', res.value)
|
|
799
|
-
} catch (err) {
|
|
800
|
-
console.log('Something failed:', err)
|
|
801
|
-
}
|
|
802
|
-
```
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
### Example: Writing `STRUCT` type variable (with autoFill parameter)
|
|
806
|
-
|
|
807
|
-
---
|
|
808
|
-
|
|
809
|
-
The following code will not work. The PLC struct has three members but we provide only two, which causes an exception.
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
```js
|
|
813
|
-
//NOTE: This won't work
|
|
814
|
-
try {
|
|
815
|
-
const res = await client.writeSymbol('GVL_Test.ExampleSTRUCT', {
|
|
816
|
-
SomeReal: 123.45,
|
|
817
|
-
SomeText: 'This will not work...'
|
|
818
|
-
})
|
|
819
|
-
|
|
820
|
-
} catch (err) {
|
|
821
|
-
console.log('Something failed:', err)
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
/*
|
|
825
|
-
Example console output
|
|
826
|
-
|
|
827
|
-
Something failed: { ClientException: Writing symbol GVL_Test.ExampleSTRUCT failed: Given Javascript object is missing key/value for at least ".SomeDate" (DATE_AND_TIME) - Set writeSymbol() 3rd parameter (autoFill) to true to allow uncomplete objects
|
|
828
|
-
at Promise ...
|
|
829
|
-
*/
|
|
830
|
-
```
|
|
831
|
-
|
|
832
|
-
We need to tell the `WriteSymbol` that we **indeed** want to write just some members and the rest will stay the same. This happens by setting the 3rd parameter `autoFill` to true.
|
|
833
|
-
|
|
834
|
-
If `autoFill` is true, the method first reads the latest value and then writes it with only the new given changes.
|
|
835
|
-
|
|
836
|
-
```js
|
|
837
|
-
try {
|
|
838
|
-
const res = await client.writeSymbol('GVL_Test.ExampleSTRUCT', {
|
|
839
|
-
SomeReal: 123.45,
|
|
840
|
-
SomeText: 'But this works!'
|
|
841
|
-
}, true) //Note this!
|
|
842
|
-
|
|
843
|
-
console.log('Value written:', res.value)
|
|
844
|
-
|
|
845
|
-
} catch (err) {
|
|
846
|
-
console.log('Something failed:', err)
|
|
847
|
-
}
|
|
848
|
-
```
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
Of course, it would be possible to do with two separate commands too:
|
|
852
|
-
```js
|
|
853
|
-
res = await client.writeSymbol('GVL_Test.ExampleSTRUCT.SomeReal', 123.45)
|
|
854
|
-
res = await client.writeSymbol('GVL_Test.ExampleSTRUCT.SomeText', 'This is also possible')
|
|
855
|
-
```
|
|
856
|
-
|
|
857
|
-
---
|
|
858
|
-
|
|
859
|
-
### Example: Writing `ARRAY OF INT` type variable
|
|
860
|
-
|
|
861
|
-
```js
|
|
862
|
-
try {
|
|
863
|
-
const res = await client.writeSymbol('GVL_Test.TestArray', [9999, 8888, 6666, 5555, 4444])
|
|
864
|
-
|
|
865
|
-
console.log('Value written:', res.value)
|
|
866
|
-
|
|
867
|
-
} catch (err) {
|
|
868
|
-
console.log('Something failed:', err)
|
|
869
|
-
return
|
|
870
|
-
}
|
|
871
|
-
```
|
|
872
|
-
|
|
873
|
-
---
|
|
874
|
-
|
|
875
|
-
### Example: Writing `ARRAY of STRUCT` type variable
|
|
876
|
-
|
|
877
|
-
```js
|
|
878
|
-
try {
|
|
879
|
-
const res = await client.writeSymbol('GVL_Test.TestArray',
|
|
880
|
-
[ { SomeText: 'First value',
|
|
881
|
-
SomeReal: 1.0,
|
|
882
|
-
SomeDate: new Date()},
|
|
883
|
-
{ SomeText: 'Second',
|
|
884
|
-
SomeReal: 10.10,
|
|
885
|
-
SomeDate: new Date()},
|
|
886
|
-
{ SomeText: 'Third',
|
|
887
|
-
SomeReal: 20.20,
|
|
888
|
-
SomeDate: new Date()},
|
|
889
|
-
{ SomeText: 'Fourth',
|
|
890
|
-
SomeReal: 30.30,
|
|
891
|
-
SomeDate: new Date()},
|
|
892
|
-
{ SomeText: 'Fifth',
|
|
893
|
-
SomeReal: 40.40,
|
|
894
|
-
SomeDate: new Date()}]
|
|
895
|
-
)
|
|
896
|
-
|
|
897
|
-
console.log('Value written:', res.value)
|
|
898
|
-
|
|
899
|
-
} catch (err) {
|
|
900
|
-
console.log('Something failed:', err)
|
|
901
|
-
return
|
|
902
|
-
}
|
|
903
|
-
```
|
|
904
|
-
|
|
905
|
-
---
|
|
906
|
-
### Example: Writing `FUNCTION BLOCK` type variable
|
|
907
|
-
|
|
908
|
-
Starting a timer from Node.js and setting time to 60 seconds.
|
|
909
|
-
|
|
910
|
-
**NOTE:** Using autoFill parameter to keep the rest as-is.
|
|
911
|
-
```js
|
|
912
|
-
try {
|
|
913
|
-
const res = await client.writeSymbol('GVL_Test.TestTimer', {
|
|
914
|
-
IN: true,
|
|
915
|
-
PT: 60000,
|
|
916
|
-
}, true)
|
|
917
|
-
|
|
918
|
-
console.log('Value written:', res.value)
|
|
919
|
-
} catch (err) {
|
|
920
|
-
console.log('Something failed:', err)
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
/*
|
|
924
|
-
Example console output
|
|
925
|
-
|
|
926
|
-
Value written: { IN: true, PT: 60000, Q: false, ET: 0, M: false, StartTime: 0 }
|
|
927
|
-
*/
|
|
928
|
-
```
|
|
929
|
-
|
|
930
|
-
## Subscribing to PLC variables (device notifications)
|
|
931
|
-
|
|
932
|
-
By using `subscribe` method, we can receive variable values automatically from the PLC if they change or periodically.
|
|
933
|
-
|
|
934
|
-
See full `subscribe` documentation [from the docs](https://jisotalo.github.io/ads-client/Client.html#subscribe).
|
|
935
|
-
|
|
936
|
-
---
|
|
937
|
-
|
|
938
|
-
### Subcribe to variable value (on-change)
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
The following wants the PLC to check if the value has changed every 1000 milliseconds. If it has changed, callback is called with the latest value in `data` parameter.
|
|
942
|
-
|
|
943
|
-
After 10 seconds the subscription is unsubscribed.
|
|
944
|
-
|
|
945
|
-
```js
|
|
946
|
-
try {
|
|
947
|
-
let subscription = await client.subscribe('GVL_Test.IncrementingValue', (data, sub) => {
|
|
948
|
-
//Note: The sub parameter is the same as returned by client.subcribe()
|
|
949
|
-
console.log(`${data.timeStamp}: Value changed to ${data.value}`)
|
|
950
|
-
|
|
951
|
-
}, 1000)
|
|
952
|
-
|
|
953
|
-
console.log(`Subscribed to ${subscription.target}`)
|
|
954
|
-
|
|
955
|
-
//Unsubscribe and disconnect after 10 seconds
|
|
956
|
-
setTimeout((async () => {
|
|
957
|
-
//The subscribe() returns object that contains unsubscribe() method
|
|
958
|
-
await subscription.unsubscribe()
|
|
959
|
-
|
|
960
|
-
console.log('Unsubscribed')
|
|
961
|
-
}), 10000)
|
|
962
|
-
|
|
963
|
-
} catch (err) {
|
|
964
|
-
console.log('Something failed:', err)
|
|
965
|
-
return
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
/*
|
|
969
|
-
Example console output
|
|
970
|
-
|
|
971
|
-
Subscribed to GVL_Test.IncrementingValue
|
|
972
|
-
Mon Apr 13 2020 13:02:26 GMT+0300 (GMT+03:00): Value changed to 1586
|
|
973
|
-
Mon Apr 13 2020 13:02:27 GMT+0300 (GMT+03:00): Value changed to 1588
|
|
974
|
-
Mon Apr 13 2020 13:02:28 GMT+0300 (GMT+03:00): Value changed to 1590
|
|
975
|
-
...
|
|
976
|
-
Mon Apr 13 2020 13:02:34 GMT+0300 (GMT+03:00): Value changed to 1602
|
|
977
|
-
Unsubscribed
|
|
978
|
-
```
|
|
979
|
-
|
|
980
|
-
---
|
|
981
|
-
|
|
982
|
-
### Subcribe to variable value (cyclic)
|
|
983
|
-
|
|
984
|
-
The following wants the PLC to send the variable value every 1000 milliseconds. The callback is called with the latest value in `data` parameter (it might have changed or not).
|
|
985
|
-
|
|
986
|
-
```js
|
|
987
|
-
//Our callback function
|
|
988
|
-
const onChange = (data, sub) => {
|
|
989
|
-
console.log(`${data.timeStamp}: ${sub.target} changed to ${data.value}`)
|
|
990
|
-
|
|
991
|
-
//We can call sub.unsubscribe() here if we want
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
try {
|
|
995
|
-
let subscription = await client.subscribe('GVL_Test.IncrementingValue', onChange, 1000, false)
|
|
996
|
-
|
|
997
|
-
console.log(`Subscribed to ${subscription.target}`)
|
|
998
|
-
|
|
999
|
-
} catch (err) {
|
|
1000
|
-
console.log('Something failed:', err)
|
|
1001
|
-
return
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
/*
|
|
1005
|
-
Example console output
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
Subscribed to GVL_Test.IncrementingValue
|
|
1009
|
-
Mon Apr 13 2020 13:09:04 GMT+0300 (GMT+03:00): GVL_Test.IncrementingValue changed to 273
|
|
1010
|
-
Mon Apr 13 2020 13:09:05 GMT+0300 (GMT+03:00): GVL_Test.IncrementingValue changed to 275
|
|
1011
|
-
Mon Apr 13 2020 13:09:06 GMT+0300 (GMT+03:00): GVL_Test.IncrementingValue changed to 277
|
|
1012
|
-
...
|
|
1013
|
-
```
|
|
1014
|
-
|
|
1015
|
-
## Reading and writing raw data
|
|
1016
|
-
|
|
1017
|
-
It's possible to read and write raw data using ads-client. Reading will result in a `Buffer` object, that contains the read data as bytes. Writing accepts a `Buffer` object that is then written to the PLC.
|
|
1018
|
-
|
|
1019
|
-
Handling raw data is usually the most fastest and efficient way, as there is usually much less network traffic required. The methods require known `indexGroup` and `indexOffset` values.
|
|
1020
|
-
|
|
1021
|
-
### Getting symbol index group, offset and size
|
|
1022
|
-
|
|
1023
|
-
Variable symbol information can be acquired with method `getSymbolInfo`. The symbol infoc contains required `indexGroup`, `indexOffset` and `size`.
|
|
1024
|
-
|
|
1025
|
-
```js
|
|
1026
|
-
const info = await client.getSymbolInfo('GVL_Test.TestINT')
|
|
1027
|
-
console.log(info)
|
|
1028
|
-
/*
|
|
1029
|
-
{ indexGroup: 16448,
|
|
1030
|
-
indexOffset: 414816,
|
|
1031
|
-
size: 2,
|
|
1032
|
-
dataType: 2,
|
|
1033
|
-
dataTypeStr: 'ADST_INT16',
|
|
1034
|
-
flags: 8,
|
|
1035
|
-
flagsStr: [ 'TypeGuid' ],
|
|
1036
|
-
arrayDimension: 0,
|
|
1037
|
-
nameLength: 16,
|
|
1038
|
-
typeLength: 3,
|
|
1039
|
-
commentLength: 0,
|
|
1040
|
-
name: 'GVL_Test.TestINT',
|
|
1041
|
-
type: 'INT',
|
|
1042
|
-
comment: '' }
|
|
1043
|
-
*/
|
|
1044
|
-
```
|
|
1045
|
-
|
|
1046
|
-
### Reading a single raw value
|
|
1047
|
-
```js
|
|
1048
|
-
//Reading DINT from indexGroup 16448 and indexOffset 411836 (4 bytes)
|
|
1049
|
-
const result = await client.readRaw(16448, 411836, 4)
|
|
1050
|
-
console.log(result) //<Buffer 37 61 00 00>
|
|
1051
|
-
```
|
|
1052
|
-
|
|
1053
|
-
### Writing a single raw value
|
|
1054
|
-
```js
|
|
1055
|
-
//Writing value 123 to DINT from indexGroup 16448 and indexOffset 411836 (4 bytes)
|
|
1056
|
-
const data = Buffer.alloc(4)
|
|
1057
|
-
data.writeInt32LE(123)
|
|
1058
|
-
|
|
1059
|
-
await client.writeRaw(16448, 411836, data)
|
|
1060
|
-
```
|
|
1061
|
-
|
|
1062
|
-
### Reading multiple raw values
|
|
1063
|
-
|
|
1064
|
-
Starting from version 1.3.0 you can use ADS sum commands to read multiple values in a single request. This is faster than reading one by one.
|
|
1065
|
-
|
|
1066
|
-
Method returns an array of results, one result object for each read operation. If result has `success` of true, the read was succesful and data is located in `data`. Otherwise error information can be read from `errorInfo`.
|
|
1067
|
-
|
|
1068
|
-
```js
|
|
1069
|
-
const result = await client.readRawMulti([
|
|
1070
|
-
{
|
|
1071
|
-
indexGroup: 16448,
|
|
1072
|
-
indexOffset: 411836,
|
|
1073
|
-
size: 4
|
|
1074
|
-
},{
|
|
1075
|
-
indexGroup: 123, //Note: Incorrect on purpose
|
|
1076
|
-
indexOffset: 436040,
|
|
1077
|
-
size: 255
|
|
1078
|
-
}
|
|
1079
|
-
])
|
|
1080
|
-
console.log(result)
|
|
1081
|
-
/*
|
|
1082
|
-
[ { success: true,
|
|
1083
|
-
errorInfo: { error: false, errorCode: 0, errorStr: 'No error' },
|
|
1084
|
-
target: { indexGroup: 16448, indexOffset: 411836 },
|
|
1085
|
-
data: <Buffer 00 43 3a d4> },
|
|
1086
|
-
{ success: false,
|
|
1087
|
-
errorInfo:
|
|
1088
|
-
{ error: true, errorCode: 1794, errorStr: 'Invalid index group' },
|
|
1089
|
-
target: { indexGroup: 123, indexOffset: 436040 },
|
|
1090
|
-
data:
|
|
1091
|
-
<Buffer 00 00 00 00 ... > } ]
|
|
1092
|
-
*/
|
|
1093
|
-
```
|
|
1094
|
-
|
|
1095
|
-
### Writing multiple raw values
|
|
1096
|
-
|
|
1097
|
-
Starting from version 1.3.0 you can use ADS sum commands to write multiple values in a single request. This is faster than writing one by one.
|
|
1098
|
-
|
|
1099
|
-
Method returns an array of results, one result object for each write operation. If result has `success` of true, the write was succesful. Otherwise error information can be read from `errorInfo`.
|
|
1100
|
-
|
|
1101
|
-
```js
|
|
1102
|
-
//Create raw data for DINT with value 555
|
|
1103
|
-
const data = Buffer.alloc(4)
|
|
1104
|
-
data.writeInt32LE(555)
|
|
1105
|
-
|
|
1106
|
-
const result = await client.writeRawMulti([
|
|
1107
|
-
{
|
|
1108
|
-
indexGroup: 16448,
|
|
1109
|
-
indexOffset: 411836,
|
|
1110
|
-
data: data
|
|
1111
|
-
},{
|
|
1112
|
-
indexGroup: 123, //Note: Incorrect on purpose
|
|
1113
|
-
indexOffset: 436040,
|
|
1114
|
-
data: Buffer.alloc(255)
|
|
1115
|
-
}
|
|
1116
|
-
])
|
|
1117
|
-
console.log(result)
|
|
1118
|
-
/*
|
|
1119
|
-
[ { success: true,
|
|
1120
|
-
errorInfo: { error: false, errorCode: 0, errorStr: 'No error' },
|
|
1121
|
-
target: { indexGroup: 16448, indexOffset: 411836 } },
|
|
1122
|
-
{ success: false,
|
|
1123
|
-
errorInfo:
|
|
1124
|
-
{ error: true,
|
|
1125
|
-
errorCode: 1793,
|
|
1126
|
-
errorStr: 'Service is not supported by server' },
|
|
1127
|
-
target: { indexGroup: 123, indexOffset: 436040 } } ]
|
|
1128
|
-
*/
|
|
1129
|
-
```
|
|
1130
|
-
|
|
1131
|
-
### Creating a variable handle and reading a raw value
|
|
1132
|
-
|
|
1133
|
-
Using handles is another alternative for reading and writing raw data. A handle is first made using variable name and then using the returned handle, read and write operations can be made. No need to know `indexGroup` and `indexOffset`.
|
|
1134
|
-
|
|
1135
|
-
NOTE: Handles should always be deleted if no more used. This doesn't mean that it wouldn't be a good habit to use the same handle all the time (for example in application backend until app is terminated). The reason is that there are limited number of handles available.
|
|
1136
|
-
|
|
1137
|
-
```js
|
|
1138
|
-
const handle = await client.createVariableHandle('GVL_Test.TestINT')
|
|
1139
|
-
console.log(handle)
|
|
1140
|
-
//{ handle: 905969897, size: 2, type: 'INT' }
|
|
1141
|
-
|
|
1142
|
-
const result = await client.readRawByHandle(handle)
|
|
1143
|
-
console.log(result)
|
|
1144
|
-
//<Buffer d2 04>
|
|
1145
|
-
|
|
1146
|
-
await client.deleteVariableHandle(handle)
|
|
1147
|
-
```
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
### Creating a variable handle and writing a raw value
|
|
1151
|
-
|
|
1152
|
-
See *Creating a variable handle and reading a raw value* for more info about handles.
|
|
1153
|
-
|
|
1154
|
-
```js
|
|
1155
|
-
const handle = await client.createVariableHandle('GVL_Test.TestINT')
|
|
1156
|
-
console.log(handle)
|
|
1157
|
-
//{ handle: 905969897, size: 2, type: 'INT' }
|
|
1158
|
-
|
|
1159
|
-
//Create raw data for INT with value 12345
|
|
1160
|
-
const data = Buffer.alloc(2)
|
|
1161
|
-
data.writeInt32LE(12345)
|
|
1162
|
-
|
|
1163
|
-
await client.writeRawByHandle(handle, data)
|
|
1164
|
-
await client.deleteVariableHandle(handle)
|
|
1165
|
-
```
|
|
1166
|
-
|
|
1167
|
-
### Creating and deleting multiple variable handles
|
|
1168
|
-
|
|
1169
|
-
Since version 1.10.0 it is possible to create and delete multiple handles at once. This is faster than one by one as everything is sent in one packet.
|
|
1170
|
-
|
|
1171
|
-
**NOTE:** It `createVariableHandleMulti()` returns only the handle as number. No size and data type are returned as in `createVariableHandle()`.
|
|
1172
|
-
|
|
1173
|
-
```js
|
|
1174
|
-
//Create three handles at once
|
|
1175
|
-
const handles = (await client.createVariableHandleMulti([
|
|
1176
|
-
'GVL_Test.TestINT',
|
|
1177
|
-
'GVL_Test.TestENUM',
|
|
1178
|
-
'GVL_Test.THIS_IS_NOT_FOUND'
|
|
1179
|
-
]))
|
|
1180
|
-
|
|
1181
|
-
console.log(handles)
|
|
1182
|
-
/*
|
|
1183
|
-
[
|
|
1184
|
-
{
|
|
1185
|
-
success: true,
|
|
1186
|
-
errorInfo: { error: false, errorCode: 0, errorStr: 'No error' },
|
|
1187
|
-
target: 'GVL_Test.TestINT',
|
|
1188
|
-
handle: 570425600
|
|
1189
|
-
},
|
|
1190
|
-
{
|
|
1191
|
-
success: true,
|
|
1192
|
-
errorInfo: { error: false, errorCode: 0, errorStr: 'No error' },
|
|
1193
|
-
target: 'GVL_Test.TestENUM',
|
|
1194
|
-
handle: 570425601
|
|
1195
|
-
},
|
|
1196
|
-
{
|
|
1197
|
-
success: false,
|
|
1198
|
-
errorInfo: { error: true, errorCode: 1808, errorStr: 'Symbol not found' },
|
|
1199
|
-
target: 'GVL_Test.THIS_IS_NOT_FOUND',
|
|
1200
|
-
handle: null
|
|
1201
|
-
}
|
|
1202
|
-
]
|
|
1203
|
-
*/
|
|
1204
|
-
|
|
1205
|
-
//Read data from first handle, note that raw methods understand object as input
|
|
1206
|
-
console.log(await client.readRawByHandle(handles[0])) //Output: <Buffer d2 04>
|
|
1207
|
-
|
|
1208
|
-
//Read data from second handle, note that only handle number is given
|
|
1209
|
-
console.log(await client.readRawByHandle(handles[1].handle)) //Output: <Buffer 64 00>
|
|
1210
|
-
|
|
1211
|
-
//Delete handles
|
|
1212
|
-
await client.deleteVariableHandleMulti(handles)
|
|
1213
|
-
```
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
### Converting a raw value to Javascript object
|
|
1218
|
-
Using `convertFromRaw` method, raw data can be converted to Javascript object. The conversion works internally like in `readSymbol`.
|
|
1219
|
-
|
|
1220
|
-
```js
|
|
1221
|
-
//result = <Buffer 37 61 00 00>
|
|
1222
|
-
const value = await client.convertFromRaw(result, 'DINT')
|
|
1223
|
-
console.log(value) //24887
|
|
1224
|
-
```
|
|
1225
|
-
|
|
1226
|
-
Example with `readRawMulti` and custom struct:
|
|
1227
|
-
```js
|
|
1228
|
-
const result = await client.readRawMulti([
|
|
1229
|
-
{
|
|
1230
|
-
indexGroup: 16448,
|
|
1231
|
-
indexOffset: 449659,
|
|
1232
|
-
size: 59
|
|
1233
|
-
},{
|
|
1234
|
-
indexGroup: 16448,
|
|
1235
|
-
indexOffset: 436040,
|
|
1236
|
-
size: 255
|
|
1237
|
-
}
|
|
1238
|
-
])
|
|
1239
|
-
|
|
1240
|
-
const value = await client.convertFromRaw(result[0].data, 'ST_Example')
|
|
1241
|
-
console.log(value)
|
|
1242
|
-
/*
|
|
1243
|
-
{ SomeText: 'Hello ads-client',
|
|
1244
|
-
SomeReal: 3.1415927410125732,
|
|
1245
|
-
SomeDate: 2020-04-13T12:25:33.000Z }
|
|
1246
|
-
*/
|
|
1247
|
-
```
|
|
1248
|
-
|
|
1249
|
-
### Converting a Javascript object to raw value
|
|
1250
|
-
Using `convertToRaw` method, Javascript object can be converted to raw data. The conversion works internally like in `writeSymbol`.
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
```js
|
|
1254
|
-
const data = await client.convertToRaw(12345, 'INT')
|
|
1255
|
-
console.log(data) //<Buffer 39 30>
|
|
1256
|
-
```
|
|
1257
|
-
|
|
1258
|
-
The 3rd parameter `autoFill` works as in `writeSymbol`.
|
|
1259
|
-
```js
|
|
1260
|
-
const data = await client.convertToRaw(
|
|
1261
|
-
{
|
|
1262
|
-
SomeText: 'Hello ads-client',
|
|
1263
|
-
SomeReal: 3.1415927410125732,
|
|
1264
|
-
}, 'ST_Example', true //NOTE: autoFill=true (as there is no SomeDate given)
|
|
1265
|
-
)
|
|
1266
|
-
console.log(data)
|
|
1267
|
-
//<Buffer 48 65 6c 6c 6f 20 ... >
|
|
1268
|
-
```
|
|
1269
|
-
|
|
1270
|
-
## Reading and writing `POINTER TO` and `REFERENCE TO` variables
|
|
1271
|
-
|
|
1272
|
-
It's also possible to work with both `POINTER TO xxx` and `REFERENCE TO xxx` variables with `ads-client`.
|
|
1273
|
-
|
|
1274
|
-
- Note that using `ReadSymbol()` and `WriteSymbol()` is not possible
|
|
1275
|
-
- Easiest way to read is `readRawByName()`
|
|
1276
|
-
- Writing is can be done with `createVariableHandle()`, which is suitable for reading too
|
|
1277
|
-
- Pointers require dereference operator ^ in order to work
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
### Reading a `REFERENCE TO` value
|
|
1281
|
-
|
|
1282
|
-
The value needs to be read as a raw Buffer first and then it can be converted to correct data type. `createVariableHandle()` works too but this is slightly faster and easier.
|
|
1283
|
-
|
|
1284
|
-
```js
|
|
1285
|
-
//GVL_Test has variable TestREFERENCE : REFERENCE TO ST_Example;
|
|
1286
|
-
|
|
1287
|
-
const value = await client.convertFromRaw(
|
|
1288
|
-
await client.readRawByName('GVL_Test.TestREFERENCE'),
|
|
1289
|
-
'ST_Example'
|
|
1290
|
-
)
|
|
1291
|
-
console.log(value)
|
|
1292
|
-
/*
|
|
1293
|
-
{
|
|
1294
|
-
SomeText: 'Hello ads-client',
|
|
1295
|
-
SomeReal: 3.1415927410125732,
|
|
1296
|
-
SomeDate: 2020-04-13T12:25:33.000Z
|
|
1297
|
-
}
|
|
1298
|
-
*/
|
|
1299
|
-
```
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
### Writing a `REFERENCE TO` value
|
|
1303
|
-
|
|
1304
|
-
In order to write a reference, a variable handle needs to be created. Also the data neeeds to be converted to a raw Buffer before writing.
|
|
1305
|
-
|
|
1306
|
-
```js
|
|
1307
|
-
//GVL_Test has variable TestREFERENCE : REFERENCE TO ST_Example;
|
|
1308
|
-
|
|
1309
|
-
const value = {
|
|
1310
|
-
SomeText: 'Hello reference variable',
|
|
1311
|
-
SomeReal: 12345,
|
|
1312
|
-
SomeDate: new Date()
|
|
1313
|
-
}
|
|
1314
|
-
|
|
1315
|
-
//Creating handle to the varialbe
|
|
1316
|
-
const handle = await client.createVariableHandle('GVL_Test.TestREFERENCE')
|
|
1317
|
-
|
|
1318
|
-
//Writing raw Buffer data that is first created from object
|
|
1319
|
-
await client.writeRawByHandle(
|
|
1320
|
-
handle,
|
|
1321
|
-
await client.convertToRaw(value, 'ST_Example')
|
|
1322
|
-
)
|
|
1323
|
-
|
|
1324
|
-
//Always delete the handle if not reusing it later
|
|
1325
|
-
await client.deleteVariableHandle(handle)
|
|
1326
|
-
```
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
### Reading a `POINTER TO` value
|
|
1331
|
-
|
|
1332
|
-
Similar as with reference except that a dereference operator `^` is required.
|
|
1333
|
-
|
|
1334
|
-
```js
|
|
1335
|
-
//GVL_Test has variable TestPOINTER : POINTER TO ST_Example;
|
|
1336
|
-
|
|
1337
|
-
const value = await client.convertFromRaw(
|
|
1338
|
-
await client.readRawByName('GVL_Test.TestPOINTER^'), //note ^
|
|
1339
|
-
'ST_Example'
|
|
1340
|
-
)
|
|
1341
|
-
console.log(value)
|
|
1342
|
-
/*
|
|
1343
|
-
{
|
|
1344
|
-
SomeText: 'Hello ads-client',
|
|
1345
|
-
SomeReal: 3.1415927410125732,
|
|
1346
|
-
SomeDate: 2020-04-13T12:25:33.000Z
|
|
1347
|
-
}
|
|
1348
|
-
*/
|
|
1349
|
-
```
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
### Writing a `POINTER TO` value
|
|
1353
|
-
|
|
1354
|
-
Similar as with reference except that a dereference operator `^` is required.
|
|
1355
|
-
|
|
1356
|
-
```js
|
|
1357
|
-
//GVL_Test has variable TestPOINTER : POINTER TO ST_Example;
|
|
1358
|
-
|
|
1359
|
-
const value = {
|
|
1360
|
-
SomeText: 'Hello pointer variable',
|
|
1361
|
-
SomeReal: 12345,
|
|
1362
|
-
SomeDate: new Date()
|
|
1363
|
-
}
|
|
1364
|
-
|
|
1365
|
-
//Creating handle to the varialbe
|
|
1366
|
-
const handle = await client.createVariableHandle('GVL_Test.TestPOINTER^') //note ^
|
|
1367
|
-
|
|
1368
|
-
//Writing raw Buffer data that is first created from object
|
|
1369
|
-
await client.writeRawByHandle(
|
|
1370
|
-
handle,
|
|
1371
|
-
await client.convertToRaw(value, 'ST_Example')
|
|
1372
|
-
)
|
|
1373
|
-
|
|
1374
|
-
//Always delete the handle if not reusing it later
|
|
1375
|
-
await client.deleteVariableHandle(handle)
|
|
1376
|
-
```
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
## Calling a function block method with parameters using RPC (remote procedure call)
|
|
1380
|
-
Starting from version 1.8.0, it is possible to call function block methods directly from Node.js. Input and output parameters are available as well as method return value. **Only supported on TwinCAT 3.**
|
|
1381
|
-
|
|
1382
|
-
---
|
|
1383
|
-
**WARNING - IMPORTANT NOTE**
|
|
1384
|
-
- Do not use online change if you change RPC method parameters or return data types
|
|
1385
|
-
- Make sure that parameters and return value have no `pack-mode` pragmas defined, otherwise data might be corrupted
|
|
1386
|
-
- Do not use `ARRAY` values directly in parameters or return value, encapsulate arrays inside struct and use the struct instead
|
|
1387
|
-
- The feature is new and there might some bugs as it's not well documented by Beckhoff
|
|
1388
|
-
---
|
|
1389
|
-
|
|
1390
|
-
In the following example we have a function block named `FB_RpcTest`. There is an instance of it at global variable list with a path of `GVL_Test.RpcTest`.
|
|
1391
|
-
|
|
1392
|
-

|
|
1393
|
-
|
|
1394
|
-
### Calling a simple RPC method
|
|
1395
|
-
|
|
1396
|
-
The code of `Calculator` method is the following. It has also VAR_OUTPUT parameters. Idea is that calculation is returned as outputs and return value tells if it was succesful.
|
|
1397
|
-
```
|
|
1398
|
-
{attribute 'TcRpcEnable'}
|
|
1399
|
-
METHOD Calculator : BOOL
|
|
1400
|
-
VAR_INPUT
|
|
1401
|
-
Value1 : REAL;
|
|
1402
|
-
Value2 : REAL;
|
|
1403
|
-
END_VAR
|
|
1404
|
-
VAR_OUTPUT
|
|
1405
|
-
Sum : REAL;
|
|
1406
|
-
Product : REAL;
|
|
1407
|
-
Division : REAL;
|
|
1408
|
-
END_VAR
|
|
1409
|
-
|
|
1410
|
-
//--- Code begins ---
|
|
1411
|
-
|
|
1412
|
-
//Return TRUE if all success
|
|
1413
|
-
Calculator := TRUE;
|
|
1414
|
-
|
|
1415
|
-
Sum := Value1 + Value2;
|
|
1416
|
-
Product := Value1 * Value2;
|
|
1417
|
-
|
|
1418
|
-
IF Value2 <> 0 THEN
|
|
1419
|
-
Division := Value1 / Value2;
|
|
1420
|
-
ELSE
|
|
1421
|
-
Division := 0;
|
|
1422
|
-
Calculator := FALSE;
|
|
1423
|
-
END_IF
|
|
1424
|
-
```
|
|
1425
|
-
|
|
1426
|
-
The method can be called from Node.js:
|
|
31
|
+
### Import the client
|
|
1427
32
|
```js
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
Value2: 2.2
|
|
1431
|
-
})
|
|
1432
|
-
|
|
1433
|
-
console.log(result)
|
|
1434
|
-
|
|
1435
|
-
/*
|
|
1436
|
-
Example console output:
|
|
1437
|
-
|
|
1438
|
-
{
|
|
1439
|
-
returnValue: true,
|
|
1440
|
-
outputs: {
|
|
1441
|
-
Sum: 102.69999694824219,
|
|
1442
|
-
Product: 221.10000610351562,
|
|
1443
|
-
Division: 45.68181610107422
|
|
1444
|
-
}
|
|
1445
|
-
}
|
|
1446
|
-
*/
|
|
1447
|
-
```
|
|
1448
|
-
|
|
1449
|
-
### Using structs with RPC methods
|
|
1450
|
-
|
|
1451
|
-
The code of the `StructTest` method is following. It has only one input and a return value (both are structs).
|
|
33
|
+
//Javascript:
|
|
34
|
+
const { Client } = require('ads-client');
|
|
1452
35
|
|
|
36
|
+
//Typescript:
|
|
37
|
+
import { Client } from 'ads-client';
|
|
1453
38
|
```
|
|
1454
|
-
{attribute 'TcRpcEnable'}
|
|
1455
|
-
METHOD StructTest : ST_Example
|
|
1456
|
-
VAR_INPUT
|
|
1457
|
-
Input : ST_Example;
|
|
1458
|
-
END_VAR
|
|
1459
39
|
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
StructTest.SomeText := CONCAT('Response: ', Input.SomeText);
|
|
1463
|
-
StructTest.SomeReal := Input.SomeReal * 10.0;
|
|
1464
|
-
StructTest.SomeDate := Input.SomeDate + T#24H;
|
|
1465
|
-
```
|
|
1466
|
-
The method can be called from Node.js:
|
|
40
|
+
### Create a client and connect
|
|
1467
41
|
|
|
1468
42
|
```js
|
|
1469
|
-
const
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
SomeDate: new Date() //2020-07-03T14:57:22.000Z
|
|
1474
|
-
}
|
|
1475
|
-
})
|
|
1476
|
-
|
|
1477
|
-
console.log(result)
|
|
1478
|
-
/*
|
|
1479
|
-
Example console output:
|
|
1480
|
-
|
|
1481
|
-
{
|
|
1482
|
-
returnValue: {
|
|
1483
|
-
SomeText: 'Response: Hello RPC method',
|
|
1484
|
-
SomeReal: 12005,
|
|
1485
|
-
SomeDate: 2020-07-03T15:57:22.000Z
|
|
1486
|
-
},
|
|
1487
|
-
outputs: {}
|
|
1488
|
-
}
|
|
1489
|
-
*/
|
|
1490
|
-
```
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
## Starting and stopping the PLC
|
|
1494
|
-
|
|
1495
|
-
The PLC runtime(s) can be started and stopped using following methods. Internally `WriteControl()` is used.
|
|
1496
|
-
|
|
1497
|
-
Note that all following methods will fail, if the PLC is already in the target state.
|
|
43
|
+
const client = new Client({
|
|
44
|
+
targetAmsNetId: "localhost",
|
|
45
|
+
targetAdsPort: 851
|
|
46
|
+
});
|
|
1498
47
|
|
|
1499
|
-
|
|
1500
|
-
```js
|
|
1501
|
-
await client.startPlc() //Start the PLC from settings.targetAdsPort
|
|
1502
|
-
await client.startPlc(852) //Start the PLC from ADS port 852
|
|
48
|
+
await client.connect();
|
|
1503
49
|
```
|
|
1504
50
|
|
|
1505
|
-
|
|
1506
|
-
```js
|
|
1507
|
-
await client.stopPlc() //Stop the PLC from settings.targetAdsPort
|
|
1508
|
-
await client.stopPlc(852) //Stop the PLC from ADS port 852
|
|
1509
|
-
```
|
|
51
|
+
### Read a value
|
|
1510
52
|
|
|
1511
|
-
**Restarting the PLC**
|
|
1512
53
|
```js
|
|
1513
|
-
await client.
|
|
1514
|
-
|
|
1515
|
-
```
|
|
54
|
+
const res = await client.readValue('GVL_Example.StringValue');
|
|
55
|
+
console.log(res.value);
|
|
1516
56
|
|
|
1517
|
-
|
|
57
|
+
//Typescript:
|
|
58
|
+
const res = await client.readValue<string>('GVL_Example.StringValue');
|
|
59
|
+
console.log(res.value); //res.value is typed as string (instead of any)
|
|
1518
60
|
|
|
1519
|
-
The PLC runtime state can always be read using `readPlcRuntimeState()` and the latest known state of PLC runtime at `settings.targetAdsPort` is located in `metaData.plcRuntimeState`.
|
|
1520
|
-
```js
|
|
1521
|
-
await client.readPlcRuntimeState() //Read PLC runtime status from settings.targetAdsPort
|
|
1522
|
-
//Example result: { adsState: 5, adsStateStr: 'Run', deviceState: 0 }
|
|
1523
|
-
await client.readPlcRuntimeState(852) //Read PLC runtime status from ADS port 852
|
|
1524
61
|
```
|
|
1525
62
|
|
|
1526
|
-
|
|
63
|
+
### Write a value
|
|
1527
64
|
|
|
1528
|
-
The TwinCAT system can be started and set to config mode using following methods. This can be useful for example when updating the PLC software. Internally `WriteControl()` is used.
|
|
1529
|
-
|
|
1530
|
-
Note that all following methods will fail, if the system is already in the target state/mode.
|
|
1531
|
-
|
|
1532
|
-
**Setting the TwinCAT system to run mode**
|
|
1533
|
-
```js
|
|
1534
|
-
await client.setSystemManagerToRun()
|
|
1535
|
-
```
|
|
1536
|
-
**Setting the TwinCAT system to config mode**
|
|
1537
65
|
```js
|
|
1538
|
-
await client.
|
|
1539
|
-
```
|
|
1540
|
-
**Restart TwinCAT system**
|
|
1541
|
-
```js
|
|
1542
|
-
//Same as calling setSystemManagerToRun()
|
|
1543
|
-
await client.restartSystemManager()
|
|
66
|
+
await client.writeValue('GVL_Example.StringValue', 'a new value');
|
|
1544
67
|
```
|
|
68
|
+
### Subscribe to value changes
|
|
1545
69
|
|
|
1546
|
-
**Reading the TwinCAT system state**
|
|
1547
|
-
|
|
1548
|
-
The TwinCAT system state can always be read using `readSystemManagerState()` and the latest known state is located in `metaData.systemManagerState`.
|
|
1549
70
|
```js
|
|
1550
|
-
await client.
|
|
1551
|
-
|
|
71
|
+
await client.subscribe({
|
|
72
|
+
target: 'GVL_Example.ChangingValue',
|
|
73
|
+
cycleTime: 100,
|
|
74
|
+
callback: (data, subscription) => console.log(`${data.timestamp}: Value changed to ${data.value}`)
|
|
75
|
+
});
|
|
1552
76
|
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
const result = await client.sendAdsCommand(
|
|
1561
|
-
ads.ADS.ADS_COMMAND.ReadDeviceInfo,
|
|
1562
|
-
Buffer.alloc(0),
|
|
1563
|
-
10000
|
|
1564
|
-
)
|
|
1565
|
-
console.log(result)
|
|
1566
|
-
/*
|
|
1567
|
-
{
|
|
1568
|
-
amsTcp: { command: 0, commandStr: 'Ads command', dataLength: 56 },
|
|
1569
|
-
ams: {
|
|
1570
|
-
targetAmsNetId: '192.168.5.131.1.1',
|
|
1571
|
-
targetAdsPort: 37538,
|
|
1572
|
-
sourceAmsNetId: '192.168.5.131.1.1',
|
|
1573
|
-
sourceAdsPort: 10000,
|
|
1574
|
-
adsCommand: 1,
|
|
1575
|
-
adsCommandStr: 'ReadDeviceInfo',
|
|
1576
|
-
stateFlags: 5,
|
|
1577
|
-
stateFlagsStr: 'Response, AdsCommand, Tcp',
|
|
1578
|
-
dataLength: 24,
|
|
1579
|
-
errorCode: 0,
|
|
1580
|
-
invokeId: 7,
|
|
1581
|
-
error: false,
|
|
1582
|
-
errorStr: ''
|
|
1583
|
-
},
|
|
1584
|
-
ads: {
|
|
1585
|
-
rawData: <Buffer 00 00 00 00 03 01 b8 0f 54 77 69 6e 43 41 54 20 53 79 73 74 65 6d 00 00>,
|
|
1586
|
-
errorCode: 0,
|
|
1587
|
-
data: {
|
|
1588
|
-
majorVersion: 3,
|
|
1589
|
-
minorVersion: 1,
|
|
1590
|
-
versionBuild: 4024,
|
|
1591
|
-
deviceName: 'TwinCAT System'
|
|
1592
|
-
},
|
|
1593
|
-
error: false
|
|
77
|
+
//Typescript:
|
|
78
|
+
await client.subscribe<number>({
|
|
79
|
+
target: 'GVL_Example.ChangingValue',
|
|
80
|
+
cycleTime: 100,
|
|
81
|
+
callback: (data, subscription) => {
|
|
82
|
+
//data.value is typed as string (instead of any)
|
|
83
|
+
console.log(`${data.timestamp}: Value changed to ${data.value}`);
|
|
1594
84
|
}
|
|
1595
|
-
}
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
https://
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Support
|
|
89
|
+
|
|
90
|
+
* Bugs and feature requests:
|
|
91
|
+
* [Github Issues](https://github.com/jisotalo/ads-client/issues)
|
|
92
|
+
* Help, support and discussion:
|
|
93
|
+
* [Github Discussions](https://github.com/jisotalo/ads-client/discussions)
|
|
94
|
+
* Professional support:
|
|
95
|
+
* Support and also development available
|
|
96
|
+
* Contact me!
|
|
97
|
+
|
|
98
|
+
If the library has been useful for you or your business, Paypal can be used to support my work. Or contact me and let's discuss the options!
|
|
99
|
+
|
|
100
|
+
[](https://www.paypal.com/donate/?business=KUWBXXCVGZZME&no_recurring=0¤cy_code=EUR)
|
|
101
|
+
|
|
102
|
+
## Library testing
|
|
103
|
+
|
|
104
|
+
Target is that all functions of the library are always tested before releasing.
|
|
105
|
+
|
|
106
|
+
`npm test` results - updated 24.08.2024
|
|
107
|
+
|
|
108
|
+
<details>
|
|
109
|
+
<summary>Click to show test results</summary>
|
|
110
|
+
<pre>
|
|
111
|
+
√ IMPORTANT NOTE: This test requires running a specific PLC project locally (https://github.com/jisotalo/ads-client-test-plc-project)
|
|
112
|
+
connection
|
|
113
|
+
√ client is not connected at beginning (1 ms)
|
|
114
|
+
√ checking ads client settings (1 ms)
|
|
115
|
+
√ connecting to the target (31 ms)
|
|
116
|
+
√ checking that test PLC project is active (13 ms)
|
|
117
|
+
√ checking that test PLC project version is correct (10 ms)
|
|
118
|
+
√ caching of symbols and data types (1 ms)
|
|
119
|
+
√ reconnecting (36 ms)
|
|
120
|
+
resetting PLC to original state
|
|
121
|
+
√ resetting PLC (514 ms)
|
|
122
|
+
√ checking that reset was successful (8 ms)
|
|
123
|
+
√ checking that PLC is not running (12 ms)
|
|
124
|
+
√ setting IsReset to false (4 ms)
|
|
125
|
+
√ starting PLC (8 ms)
|
|
126
|
+
√ checking that test PLC project is running (506 ms)
|
|
127
|
+
testing PLC runtime stop, start, restart
|
|
128
|
+
√ stopping PLC (16 ms)
|
|
129
|
+
√ starting PLC (15 ms)
|
|
130
|
+
√ restarting PLC (538 ms)
|
|
131
|
+
system state, PLC runtime states and device information
|
|
132
|
+
√ reading TwinCAT system state (7 ms)
|
|
133
|
+
√ reading PLC runtime (port 851) state (5 ms)
|
|
134
|
+
√ reading PLC runtime (port 852) state (5 ms)
|
|
135
|
+
√ reading PLC runtime device info (4 ms)
|
|
136
|
+
√ reading TwinCAT system device info (5 ms)
|
|
137
|
+
√ reading PLC runtime symbol version (5 ms)
|
|
138
|
+
symbols and data types
|
|
139
|
+
√ reading upload info (4 ms)
|
|
140
|
+
√ reading all symbols (15 ms)
|
|
141
|
+
√ reading single symbol information (2 ms)
|
|
142
|
+
√ reading all data type information (19 ms)
|
|
143
|
+
√ reading single data type information (2 ms)
|
|
144
|
+
data conversion
|
|
145
|
+
√ converting a raw PLC value to a Javascript variable (5 ms)
|
|
146
|
+
√ converting a Javascript value to a raw PLC value (40 ms)
|
|
147
|
+
reading values
|
|
148
|
+
reading standard values
|
|
149
|
+
√ reading BOOL (15 ms)
|
|
150
|
+
√ reading BYTE (8 ms)
|
|
151
|
+
√ reading WORD (7 ms)
|
|
152
|
+
√ reading DWORD (6 ms)
|
|
153
|
+
√ reading SINT (11 ms)
|
|
154
|
+
√ reading USINT (6 ms)
|
|
155
|
+
√ reading INT (13 ms)
|
|
156
|
+
√ reading UINT (7 ms)
|
|
157
|
+
√ reading DINT (15 ms)
|
|
158
|
+
√ reading UDINT (8 ms)
|
|
159
|
+
√ reading REAL (32 ms)
|
|
160
|
+
√ reading STRING (15 ms)
|
|
161
|
+
√ reading DATE (8 ms)
|
|
162
|
+
√ reading DT (13 ms)
|
|
163
|
+
√ reading TOD (16 ms)
|
|
164
|
+
√ reading TIME (9 ms)
|
|
165
|
+
√ reading LWORD (8 ms)
|
|
166
|
+
√ reading LINT (12 ms)
|
|
167
|
+
√ reading ULINT (7 ms)
|
|
168
|
+
√ reading LREAL (29 ms)
|
|
169
|
+
√ reading WSTRING (12 ms)
|
|
170
|
+
√ reading LDATE (---- TODO: Needs TC 3.1.4026 ----)
|
|
171
|
+
√ reading LDT (---- TODO: Needs TC 3.1.4026 ----)
|
|
172
|
+
√ reading LTOD (---- TODO: Needs TC 3.1.4026 ----)
|
|
173
|
+
√ reading LTIME (7 ms)
|
|
174
|
+
reading standard array values
|
|
175
|
+
√ reading ARRAY OF BOOL (13 ms)
|
|
176
|
+
√ reading ARRAY OF BYTE (8 ms)
|
|
177
|
+
√ reading ARRAY OF WORD (7 ms)
|
|
178
|
+
√ reading ARRAY OF DWORD (8 ms)
|
|
179
|
+
√ reading ARRAY OF SINT (13 ms)
|
|
180
|
+
√ reading ARRAY OF USINT (7 ms)
|
|
181
|
+
√ reading ARRAY OF INT (15 ms)
|
|
182
|
+
√ reading ARRAY OF UINT (8 ms)
|
|
183
|
+
√ reading ARRAY OF DINT (16 ms)
|
|
184
|
+
√ reading ARRAY OF UDINT (10 ms)
|
|
185
|
+
√ reading ARRAY OF REAL (31 ms)
|
|
186
|
+
√ reading ARRAY OF STRING (15 ms)
|
|
187
|
+
√ reading ARRAY OF DATE (3 ms)
|
|
188
|
+
√ reading ARRAY OF DT (14 ms)
|
|
189
|
+
√ reading ARRAY OF TOD (14 ms)
|
|
190
|
+
√ reading ARRAY OF TIME (8 ms)
|
|
191
|
+
√ reading ARRAY OF LWORD (4 ms)
|
|
192
|
+
√ reading ARRAY OF LINT (10 ms)
|
|
193
|
+
√ reading ARRAY OF ULINT (4 ms)
|
|
194
|
+
√ reading ARRAY OF LREAL (24 ms)
|
|
195
|
+
√ reading ARRAY OF WSTRING (13 ms)
|
|
196
|
+
√ reading ARRAY OF LDATE (---- TODO: Needs TC 3.1.4026 ----)
|
|
197
|
+
√ reading ARRAY OF LDT (---- TODO: Needs TC 3.1.4026 ----)
|
|
198
|
+
√ reading ARRAY OF LTOD (---- TODO: Needs TC 3.1.4026 ----)
|
|
199
|
+
√ reading ARRAY OF LTIME (6 ms)
|
|
200
|
+
reading complex values
|
|
201
|
+
√ reading STRUCT (15 ms)
|
|
202
|
+
√ reading ALIAS (5 ms)
|
|
203
|
+
√ reading ENUM (41 ms)
|
|
204
|
+
√ reading POINTER (address) (7 ms)
|
|
205
|
+
√ reading SUBRANGE (7 ms)
|
|
206
|
+
√ reading UNION (23 ms)
|
|
207
|
+
√ reading FUNCTION_BLOCK (29 ms)
|
|
208
|
+
√ reading INTERFACE (9 ms)
|
|
209
|
+
reading complex array values
|
|
210
|
+
√ reading ARRAY OF STRUCT (18 ms)
|
|
211
|
+
√ reading ARRAY OF ALIAS (8 ms)
|
|
212
|
+
√ reading ARRAY OF ENUM (40 ms)
|
|
213
|
+
√ reading ARRAY OF POINTER (address) (7 ms)
|
|
214
|
+
√ reading ARRAY OF SUBRANGE (7 ms)
|
|
215
|
+
√ reading ARRAY OF UNION (8 ms)
|
|
216
|
+
√ reading ARRAY OF FUNCTION_BLOCK (32 ms)
|
|
217
|
+
√ reading ARRAY OF INTERFACE (6 ms)
|
|
218
|
+
reading special types / cases
|
|
219
|
+
√ reading ARRAY with negative index (7 ms)
|
|
220
|
+
√ reading multi-dimensional ARRAY (8 ms)
|
|
221
|
+
√ reading ARRAY OF ARRAY (8 ms)
|
|
222
|
+
√ reading STRUCT with pragma: {attribute 'pack_mode' := '1'} (9 ms)
|
|
223
|
+
√ reading STRUCT with pragma: {attribute 'pack_mode' := '8'} (9 ms)
|
|
224
|
+
√ reading an empty FUNCTION_BLOCK (7 ms)
|
|
225
|
+
√ reading an empty STRUCT (8 ms)
|
|
226
|
+
√ reading an empty ARRAY (7 ms)
|
|
227
|
+
√ reading a single BIT (14 ms)
|
|
228
|
+
√ reading a struct with BIT types (8 ms)
|
|
229
|
+
reading dereferenced POINTER and REFERENCE values
|
|
230
|
+
√ reading POINTER (value) (8 ms)
|
|
231
|
+
√ reading REFERENCE (value) (8 ms)
|
|
232
|
+
reading raw data
|
|
233
|
+
√ reading a raw value (5 ms)
|
|
234
|
+
√ reading a raw value using symbol (3 ms)
|
|
235
|
+
√ reading a raw value using path (2 ms)
|
|
236
|
+
√ reading multiple raw values (multi/sum command) (5 ms)
|
|
237
|
+
reading (misc)
|
|
238
|
+
√ reading a value using symbol (5 ms)
|
|
239
|
+
writing values
|
|
240
|
+
writing standard values
|
|
241
|
+
√ writing BOOL (23 ms)
|
|
242
|
+
√ writing BYTE (11 ms)
|
|
243
|
+
√ writing WORD (9 ms)
|
|
244
|
+
√ writing DWORD (9 ms)
|
|
245
|
+
√ writing SINT (24 ms)
|
|
246
|
+
√ writing USINT (12 ms)
|
|
247
|
+
√ writing INT (22 ms)
|
|
248
|
+
√ writing UINT (11 ms)
|
|
249
|
+
√ writing DINT (22 ms)
|
|
250
|
+
√ writing UDINT (9 ms)
|
|
251
|
+
√ writing REAL (47 ms)
|
|
252
|
+
√ writing STRING (24 ms)
|
|
253
|
+
√ writing DATE (12 ms)
|
|
254
|
+
√ writing DT (22 ms)
|
|
255
|
+
√ writing TOD (23 ms)
|
|
256
|
+
√ writing TIME (12 ms)
|
|
257
|
+
√ writing LWORD (12 ms)
|
|
258
|
+
√ writing LINT (25 ms)
|
|
259
|
+
√ writing ULINT (12 ms)
|
|
260
|
+
√ writing LREAL (47 ms)
|
|
261
|
+
√ writing WSTRING (21 ms)
|
|
262
|
+
√ writing LDATE (---- TODO: Needs TC 3.1.4026 ----)
|
|
263
|
+
√ writing LDT (---- TODO: Needs TC 3.1.4026 ----)
|
|
264
|
+
√ writing LTOD (---- TODO: Needs TC 3.1.4026 ----)
|
|
265
|
+
√ writing LTIME (11 ms)
|
|
266
|
+
writing standard array values
|
|
267
|
+
√ writing ARRAY OF BOOL (25 ms)
|
|
268
|
+
√ writing ARRAY OF BYTE (12 ms)
|
|
269
|
+
√ writing ARRAY OF WORD (10 ms)
|
|
270
|
+
√ writing ARRAY OF DWORD (13 ms)
|
|
271
|
+
√ writing ARRAY OF SINT (18 ms)
|
|
272
|
+
√ writing ARRAY OF USINT (9 ms)
|
|
273
|
+
√ writing ARRAY OF INT (19 ms)
|
|
274
|
+
√ writing ARRAY OF UINT (12 ms)
|
|
275
|
+
√ writing ARRAY OF DINT (19 ms)
|
|
276
|
+
√ writing ARRAY OF UDINT (10 ms)
|
|
277
|
+
√ writing ARRAY OF REAL (48 ms)
|
|
278
|
+
√ writing ARRAY OF STRING (25 ms)
|
|
279
|
+
√ writing ARRAY OF DATE (11 ms)
|
|
280
|
+
√ writing ARRAY OF DT (21 ms)
|
|
281
|
+
√ writing ARRAY OF TOD (25 ms)
|
|
282
|
+
√ writing ARRAY OF TIME (12 ms)
|
|
283
|
+
√ writing ARRAY OF LWORD (13 ms)
|
|
284
|
+
√ writing ARRAY OF LINT (23 ms)
|
|
285
|
+
√ writing ARRAY OF ULINT (12 ms)
|
|
286
|
+
√ writing ARRAY OF LREAL (48 ms)
|
|
287
|
+
√ writing ARRAY OF WSTRING (23 ms)
|
|
288
|
+
√ writing ARRAY OF LDATE (---- TODO: Needs TC 3.1.4026 ----)
|
|
289
|
+
√ writing ARRAY OF LDT (---- TODO: Needs TC 3.1.4026 ----) (1 ms)
|
|
290
|
+
√ writing ARRAY OF LTOD (---- TODO: Needs TC 3.1.4026 ----)
|
|
291
|
+
√ writing ARRAY OF LTIME (12 ms)
|
|
292
|
+
writing complex values
|
|
293
|
+
√ writing STRUCT (22 ms)
|
|
294
|
+
√ writing ALIAS (11 ms)
|
|
295
|
+
√ writing ENUM (54 ms)
|
|
296
|
+
√ writing POINTER (address) (17 ms)
|
|
297
|
+
√ writing SUBRANGE (20 ms)
|
|
298
|
+
√ writing UNION (48 ms)
|
|
299
|
+
√ writing FUNCTION_BLOCK (47 ms)
|
|
300
|
+
√ writing INTERFACE (15 ms)
|
|
301
|
+
writing complex array values
|
|
302
|
+
√ writing ARRAY OF STRUCT (26 ms)
|
|
303
|
+
√ writing ARRAY OF ALIAS (13 ms)
|
|
304
|
+
√ writing ARRAY OF ENUM (55 ms)
|
|
305
|
+
√ writing ARRAY OF POINTER (address) (16 ms)
|
|
306
|
+
√ writing ARRAY OF SUBRANGE (11 ms)
|
|
307
|
+
√ writing ARRAY OF UNION (13 ms)
|
|
308
|
+
√ writing ARRAY OF FUNCTION_BLOCK (48 ms)
|
|
309
|
+
√ writing ARRAY OF INTERFACE (15 ms)
|
|
310
|
+
writing special types / cases
|
|
311
|
+
√ writing ARRAY with negative index (17 ms)
|
|
312
|
+
√ writing multi-dimensional ARRAY (16 ms)
|
|
313
|
+
√ writing ARRAY OF ARRAY (16 ms)
|
|
314
|
+
√ writing STRUCT with pragma: {attribute 'pack_mode' := '1'} (14 ms)
|
|
315
|
+
√ writing STRUCT with pragma: {attribute 'pack_mode' := '8'} (14 ms)
|
|
316
|
+
√ writing an empty FUNCTION_BLOCK (7 ms)
|
|
317
|
+
√ writing an empty STRUCT (8 ms)
|
|
318
|
+
√ writing an empty ARRAY (7 ms)
|
|
319
|
+
√ writing a single BIT (39 ms)
|
|
320
|
+
√ writing a struct with BIT types (15 ms)
|
|
321
|
+
writing dereferenced POINTER and REFERENCE values
|
|
322
|
+
√ writing POINTER (value) (21 ms)
|
|
323
|
+
√ writing REFERENCE (value) (21 ms)
|
|
324
|
+
writing raw data
|
|
325
|
+
√ writing a raw value (7 ms)
|
|
326
|
+
√ writing a raw value using symbol (5 ms)
|
|
327
|
+
√ writing a raw value using path (16 ms)
|
|
328
|
+
√ writing multiple raw values (multi/sum command) (18 ms)
|
|
329
|
+
writing (misc)
|
|
330
|
+
√ writing a value using symbol (7 ms)
|
|
331
|
+
variable handles
|
|
332
|
+
√ creating and deleting a varible handle (14 ms)
|
|
333
|
+
√ reading value using a variable handle (10 ms)
|
|
334
|
+
√ writing value using a variable handle (31 ms)
|
|
335
|
+
√ creating and deleting multiple varible handles (multi/sum command) (9 ms)
|
|
336
|
+
subscriptions (ADS notifications)
|
|
337
|
+
√ subscribing and unsubscribing successfully (2036 ms)
|
|
338
|
+
√ subscribing to a changing value (10 ms) with default cycle time (3028 ms)
|
|
339
|
+
√ subscribing to a changing value (10 ms) with 10 ms cycle time (27 ms)
|
|
340
|
+
√ subscribing to a constant value with maximum delay of 2000 ms (2036 ms)
|
|
341
|
+
√ subscribing to a raw ADS address (229 ms)
|
|
342
|
+
√ subscribing using subscribeSymbol() (2020 ms)
|
|
343
|
+
√ subscribing to a raw ADS address using subscribeRaw() (219 ms)
|
|
344
|
+
remote procedure calls (RPC methods)
|
|
345
|
+
√ calling a RPC method (15 ms)
|
|
346
|
+
√ calling a RPC method with struct parameters (14 ms)
|
|
347
|
+
√ calling a RPC method without return value and without parameters (9 ms)
|
|
348
|
+
miscellaneous
|
|
349
|
+
√ sending read write ADS command (6 ms)
|
|
350
|
+
√ sending multiple read write ADS commands (multi/sum command) (10 ms)
|
|
351
|
+
issue specific tests
|
|
352
|
+
issue 103 (https://github.com/jisotalo/ads-client/issues/103)
|
|
353
|
+
√ calling unsubscribeAll() multiple times (should not crash to unhandled exception) (51 ms)
|
|
354
|
+
disconnecting
|
|
355
|
+
√ disconnecting client (5 ms)
|
|
356
|
+
controlling TwinCAT system service
|
|
357
|
+
√ connecting (1 ms)
|
|
358
|
+
√ setting TwinCAT system to config (2022 ms)
|
|
359
|
+
√ setting TwinCAT system to run (2027 ms)
|
|
360
|
+
√ disconnecting (2 ms)
|
|
361
|
+
</pre>
|
|
362
|
+
</details>
|
|
1792
363
|
|
|
1793
364
|
# License
|
|
1794
365
|
|
|
1795
|
-
Licensed under [MIT License](http://www.opensource.org/licenses/MIT)
|
|
1796
|
-
|
|
366
|
+
Licensed under [MIT License](http://www.opensource.org/licenses/MIT).
|
|
1797
367
|
|
|
1798
|
-
Copyright (c)
|
|
368
|
+
Copyright (c) Jussi Isotalo <<j.isotalo91@gmail.com>>
|
|
1799
369
|
|
|
1800
370
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1801
371
|
of this software and associated documentation files (the "Software"), to deal
|