ads-client 2.0.0-beta.3 → 2.0.0-beta.5
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 +35 -0
- package/README.md +1536 -303
- package/dist/ads-client.d.ts +350 -76
- package/dist/ads-client.js +585 -147
- package/dist/ads-client.js.map +1 -1
- package/dist/ads-commons.d.ts +17 -2
- package/dist/ads-commons.js +46 -8
- package/dist/ads-commons.js.map +1 -1
- package/dist/types/ads-client-types.d.ts +22 -22
- package/dist/types/ads-client-types.js.map +1 -1
- package/dist/types/ads-protocol-types.d.ts +37 -0
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -4,30 +4,117 @@
|
|
|
4
4
|
[](https://www.npmjs.org/package/ads-client)
|
|
5
5
|
[](https://github.com/jisotalo/ads-client)
|
|
6
6
|
[](https://choosealicense.com/licenses/mit/)
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
|
|
9
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
|
+
If you are using Node-RED, check out the [node-red-contrib-ads-client](https://www.npmjs.com/package/node-red-contrib-ads-client).
|
|
14
|
+
|
|
15
|
+
# Project status
|
|
16
|
+
|
|
17
|
+
<u><b>NOTE: DOCUMENTATION LINKS ARE NOT WORKING UNTIL RELEASED - CLONE REPOSITORY FOR DOCS</b></u>
|
|
18
|
+
|
|
19
|
+
Version 2 is almost ready!
|
|
20
|
+
|
|
21
|
+
- Rewritten in Typescript
|
|
22
|
+
- See [CHANGELOG.md](https://github.com/jisotalo/ads-client/blob/v2-dev/CHANGELOG.md) for details.
|
|
23
|
+
- See [MIGRATION.md](https://github.com/jisotalo/ads-client/blob/v2-dev/MIGRATION.md) for guide of migrating v1 -> v2 (breaking changes!)
|
|
24
|
+
- See the new [documentation](TODO-DOC-URL-HERE/classes/Client.html)
|
|
25
|
+
|
|
26
|
+
# Features
|
|
27
|
+
- Supports TwinCAT 2 and 3
|
|
28
|
+
- Supports any kind of target systems with ADS protocol (local runtime, PLC, EtherCAT I/O...)
|
|
29
|
+
- Supports multiple connections from the same host
|
|
30
|
+
- Reading and writing any kind of variables
|
|
31
|
+
- Subscribing to variable value changes (ADS notifications)
|
|
32
|
+
- Automatic conversion between PLC and Javascript objects
|
|
33
|
+
- Calling function block methods (RPC)
|
|
34
|
+
- Automatic 32/64 bit variable support (PVOID, XINT, etc.)
|
|
35
|
+
- Automatic byte alignment support (all pack-modes automatically supported)
|
|
14
36
|
|
|
15
|
-
|
|
37
|
+
# Table of contents
|
|
38
|
+
- [Support](#support)
|
|
39
|
+
- [Installing](#installing)
|
|
40
|
+
- [Connection setup](#connection-setup)
|
|
41
|
+
- [Setup 1 - Connect from Windows](#setup-1---connect-from-windows)
|
|
42
|
+
- [Setup 2 - Connect from Linux/Windows](#setup-2---connect-from-linuxwindows)
|
|
43
|
+
- [Setup 3 - Connect from any Node.js system](#setup-3---connect-from-any-nodejs-system)
|
|
44
|
+
- [Setup 4 - Connect from local system](#setup-4---connect-from-local-system)
|
|
45
|
+
- [Setup 5 - Docker container](#setup-5---docker-container)
|
|
46
|
+
- [Important](#important)
|
|
47
|
+
- [Enabling localhost support on TwinCAT 3](#enabling-localhost-support-on-twincat-3)
|
|
48
|
+
- [Structured variables](#structured-variables)
|
|
49
|
+
- [Connecting to targets without a PLC runtime](#connecting-to-targets-without-a-plc-runtime)
|
|
50
|
+
- [Differences when using with TwinCAT 2](#differences-when-using-with-twincat-2)
|
|
51
|
+
- [Getting started](#getting-started)
|
|
52
|
+
- [Documentation](#documentation)
|
|
53
|
+
- [Available methods](#available-methods)
|
|
54
|
+
- [Creating a client](#creating-a-client)
|
|
55
|
+
- [Connecting](#connecting)
|
|
56
|
+
- [Reading values](#reading-values)
|
|
57
|
+
- [Reading any value](#reading-any-value)
|
|
58
|
+
- [Reading raw data](#reading-raw-data)
|
|
59
|
+
- [Reading reference/pointer](#reading-referencepointer)
|
|
60
|
+
- [Writing values](#writing-values)
|
|
61
|
+
- [Writing any value](#writing-any-value)
|
|
62
|
+
- [Writing raw data](#writing-raw-data)
|
|
63
|
+
- [Writing reference/pointer](#writing-referencepointer)
|
|
64
|
+
- [Subscribing to value changes](#subscribing-to-value-changes)
|
|
65
|
+
- [Any value](#any-value)
|
|
66
|
+
- [Raw data](#raw-data)
|
|
67
|
+
- [Unsubscribing](#unsubscribing)
|
|
68
|
+
- [Using variable handles](#using-variable-handles)
|
|
69
|
+
- [Calling function block RPC methods](#calling-function-block-rpc-methods)
|
|
70
|
+
- [Converting from/to raw data](#converting-fromto-raw-data)
|
|
71
|
+
- [Other features](#other-features)
|
|
72
|
+
- [Disconnecting](#disconnecting)
|
|
73
|
+
- [FAQ](#faq)
|
|
74
|
+
- [Lot's of connection issues and timeouts](#lots-of-connection-issues-and-timeouts)
|
|
75
|
+
- [Getting `TypeError: Do not know how to serialize a BigInt`](#getting-typeerror-do-not-know-how-to-serialize-a-bigint)
|
|
76
|
+
- [Can I connect from Raspberry Pi to TwinCAT?](#can-i-connect-from-raspberry-pi-to-twincat)
|
|
77
|
+
- [Receiving ADS error 1808 `Symbol not found` even when it should be found](#receiving-ads-error-1808-symbol-not-found-even-when-it-should-be-found)
|
|
78
|
+
- [Having timeouts or 'mailbox is full' errors](#having-timeouts-or-mailbox-is-full-errors)
|
|
79
|
+
- [Having problems to connect from OSX or Raspberry Pi to target PLC](#having-problems-to-connect-from-osx-or-raspberry-pi-to-target-plc)
|
|
80
|
+
- [A data type is not found even when it should be](#a-data-type-is-not-found-even-when-it-should-be)
|
|
81
|
+
- [ClientException: Connection failed: Device system manager state read failed](#clientexception-connection-failed-device-system-manager-state-read-failed)
|
|
82
|
+
- [Connection failed (error EADDRNOTAVAIL)](#connection-failed-error-eaddrnotavail)
|
|
83
|
+
- [Problems running ads-client with docker](#problems-running-ads-client-with-docker)
|
|
84
|
+
- [How to connect to PLC that is in CONFIG mode?](#how-to-connect-to-plc-that-is-in-config-mode)
|
|
85
|
+
- [Issues with TwinCAT 2 low-end devices (BK9050, BC9050 etc.)](#issues-with-twincat-2-low-end-devices-bk9050-bc9050-etc)
|
|
86
|
+
- [External links](#external-links)
|
|
87
|
+
- [Library testing](#library-testing)
|
|
88
|
+
- [TwinCAT 3 tests](#twincat-3-tests)
|
|
89
|
+
- [TwinCAT 2 tests](#twincat-2-tests)
|
|
90
|
+
- [License](#license)
|
|
16
91
|
|
|
17
|
-
All breaking changes are (hopefully) done.
|
|
18
92
|
|
|
19
|
-
|
|
20
|
-
* See [MIGRATION.md](https://github.com/jisotalo/ads-client/blob/v2-dev/MIGRATION.md) for guide of migrating v1->v2
|
|
21
|
-
* Clone repository and open `docs/index.html` in browser for documentation
|
|
93
|
+
# Support
|
|
22
94
|
|
|
23
|
-
|
|
95
|
+
* Bugs and feature requests:
|
|
96
|
+
* [Github Issues](https://github.com/jisotalo/ads-client/issues)
|
|
97
|
+
* Help, support and discussion:
|
|
98
|
+
* [Github Discussions](https://github.com/jisotalo/ads-client/discussions)
|
|
99
|
+
|
|
100
|
+
If you want to support my work, you can buy me a coffee! Contact for more options.
|
|
24
101
|
|
|
25
|
-
|
|
102
|
+
<a href="https://www.buymeacoffee.com/jisotalo" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-blue.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
|
|
26
103
|
|
|
27
|
-
|
|
28
|
-
|
|
104
|
+
[](https://www.paypal.com/donate/?business=KUWBXXCVGZZME&no_recurring=0¤cy_code=EUR)
|
|
105
|
+
|
|
106
|
+
If you need help with integrating the ads-client, I'm available for coding work with invoicing. Contact for further details.
|
|
107
|
+
|
|
108
|
+
# Installing
|
|
109
|
+
|
|
110
|
+
Install the [npm package](https://www.npmjs.com/package/ads-client):
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
npm install ads-client@beta
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Include the module in your code:
|
|
29
117
|
|
|
30
|
-
### Import the client
|
|
31
118
|
```js
|
|
32
119
|
//Javascript:
|
|
33
120
|
const { Client } = require('ads-client');
|
|
@@ -36,332 +123,1478 @@ const { Client } = require('ads-client');
|
|
|
36
123
|
import { Client } from 'ads-client';
|
|
37
124
|
```
|
|
38
125
|
|
|
39
|
-
|
|
126
|
+
You can also clone the repository and run `npm run build`. After that, the library is available at `./dist/`
|
|
127
|
+
|
|
128
|
+
# Connection setup
|
|
129
|
+
|
|
130
|
+
The ads-client can be used with multiple system configurations.
|
|
131
|
+
|
|
132
|
+
[](https://user-images.githubusercontent.com/13457157/82724547-8dde0800-9cdf-11ea-8dd1-0a1f06f8559f.PNG)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
## Setup 1 - Connect from Windows
|
|
136
|
+
|
|
137
|
+
This is the most common scenario. The client is running on a Windows PC that has TwinCAT Router installed (such as development laptop, Beckhoff IPC/PC, Beckhoff PLC).
|
|
138
|
+
|
|
139
|
+
**Requirements:**
|
|
140
|
+
- Client has one of the following installed
|
|
141
|
+
- TwinCAT XAE (dev. environment)
|
|
142
|
+
- TwinCAT XAR (runtime)
|
|
143
|
+
- [TwinCAT ADS](https://www.beckhoff.com/en-en/products/automation/twincat/tc1xxx-twincat-3-base/tc1000.html)
|
|
144
|
+
- An ADS route is created between the client and the PLC using TwinCAT router
|
|
145
|
+
|
|
146
|
+
**Client settings:**
|
|
147
|
+
|
|
148
|
+
```js
|
|
149
|
+
const client = new Client({
|
|
150
|
+
targetAmsNetId: '192.168.1.120.1.1', //AmsNetId of the target PLC
|
|
151
|
+
targetAdsPort: 851,
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Setup 2 - Connect from Linux/Windows
|
|
156
|
+
|
|
157
|
+
In this scenario, the client is running on Linux or Windows without TwinCAT Router. The .NET based router can be run separately on the same machine.
|
|
158
|
+
|
|
159
|
+
**Requirements:**
|
|
160
|
+
- Client has .NET runtime installed
|
|
161
|
+
- Client has [AdsRouterConsoleApp](https://github.com/Beckhoff/TF6000_ADS_DOTNET_V5_Samples/tree/main/Sources/RouterSamples/AdsRouterConsoleApp) or similar running
|
|
162
|
+
- An ADS route is created between the client and the PLC (see [AdsRouterConsoleApp](https://github.com/Beckhoff/TF6000_ADS_DOTNET_V5_Samples/tree/main/Sources/RouterSamples/AdsRouterConsoleApp) docs)
|
|
163
|
+
|
|
164
|
+
**Client settings:**
|
|
165
|
+
|
|
166
|
+
```js
|
|
167
|
+
const client = new Client({
|
|
168
|
+
targetAmsNetId: '192.168.1.120.1.1', //AmsNetId of the target PLC
|
|
169
|
+
targetAdsPort: 851,
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Setup 3 - Connect from any Node.js system
|
|
174
|
+
|
|
175
|
+
In this scenario, the client is running on a machine that has no router running (no TwinCAT router and no 3rd party router). For example, Raspberry Pi without any additional installations.
|
|
176
|
+
|
|
177
|
+
In this setup, the client directly connects to the PLC and uses its TwinCAT router for communication. Only one simultaneous connection from the client is possible.
|
|
178
|
+
|
|
179
|
+
**Requirements:**
|
|
180
|
+
- Target system (PLC) firewall has TCP port 48898 open
|
|
181
|
+
- Windows Firewall might block, make sure Ethernet connection is handled as "private"
|
|
182
|
+
- Local AmsNetId and ADS port are set manually
|
|
183
|
+
- Used `localAmsNetId` is not already in use
|
|
184
|
+
- Used `localAdsPort` is not already in use
|
|
185
|
+
- An ADS route is configured to the PLC (see blow)
|
|
186
|
+
|
|
187
|
+
**Setting up the route:**
|
|
188
|
+
|
|
189
|
+
1. At the PLC, open `C:\TwinCAT\3.1\Target\StaticRoutes.xml`
|
|
190
|
+
2. Copy paste the following under `<RemoteConnections>`
|
|
191
|
+
|
|
192
|
+
```xml
|
|
193
|
+
<Route>
|
|
194
|
+
<Name>UI</Name>
|
|
195
|
+
<Address>192.168.1.10</Address>
|
|
196
|
+
<NetId>192.168.1.10.1.1</NetId>
|
|
197
|
+
<Type>TCP_IP</Type>
|
|
198
|
+
<Flags>64</Flags>
|
|
199
|
+
</Route>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
3. Edit `Address` to IP address of the client (which runs the Node.js app), such as `192.168.1.10`
|
|
203
|
+
4. Edit `NetId` to any unused AmsNetId address, such as `192.168.1.10.1.1`
|
|
204
|
+
5. Restart PLC
|
|
205
|
+
|
|
206
|
+
**Client settings:**
|
|
207
|
+
|
|
208
|
+
```js
|
|
209
|
+
const client = new Client({
|
|
210
|
+
localAmsNetId: '192.168.1.10.1.1', //Same as Address in PLC's StaticRoutes.xml (see above)
|
|
211
|
+
localAdsPort: 32750, //Can be anything that is not used
|
|
212
|
+
targetAmsNetId: '192.168.1.120.1.1',//AmsNetId of the target PLC
|
|
213
|
+
targetAdsPort: 851,
|
|
214
|
+
routerAddress: '192.168.1.120', //PLC IP address
|
|
215
|
+
routerTcpPort: 48898
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**See also:**
|
|
220
|
+
* [Issue #51](https://github.com/jisotalo/ads-client/issues/51#issuecomment-758016428)
|
|
221
|
+
|
|
222
|
+
## Setup 4 - Connect from local system
|
|
223
|
+
|
|
224
|
+
In this scenario, the PLC is running Node.js app locally. For example, the development PC or Beckhoff PLC with a screen for HMI.
|
|
225
|
+
|
|
226
|
+
**Requirements:**
|
|
227
|
+
- AMS router TCP loopback enabled (see [Enabling localhost support](#enabling-localhost-support))
|
|
228
|
+
- Should be already enabled in TwinCAT versions >= 4024.5.
|
|
229
|
+
|
|
230
|
+
**Client settings:**
|
|
231
|
+
|
|
232
|
+
```js
|
|
233
|
+
const client = new Client({
|
|
234
|
+
targetAmsNetId: '127.0.0.1.1.1', //or 'localhost'
|
|
235
|
+
targetAdsPort: 851,
|
|
236
|
+
});
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Setup 5 - Docker container
|
|
240
|
+
|
|
241
|
+
It's also possible to run the client in a docker containers, also with a separate router (Linux systems).
|
|
242
|
+
|
|
243
|
+
I'm available for coding work if you need help with this. See [Supporting](#supporting)
|
|
244
|
+
|
|
245
|
+
# Important
|
|
246
|
+
|
|
247
|
+
## Enabling localhost support on TwinCAT 3
|
|
248
|
+
|
|
249
|
+
If connecting to the local TwinCAT runtime (Node.js and PLC on the same machine), the ADS router TCP loopback feature has to be enabled.
|
|
250
|
+
|
|
251
|
+
TwinCAT 4024.5 and newer already have this enabled as default.
|
|
252
|
+
|
|
253
|
+
1. Open registery edition (`regedit`)
|
|
254
|
+
2. Navigate to
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
32 bit operating system:
|
|
258
|
+
HKEY_LOCAL_MACHINE\SOFTWARE\Beckhoff\TwinCAT3\System\
|
|
259
|
+
|
|
260
|
+
64 bit it operating system:
|
|
261
|
+
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Beckhoff\TwinCAT3\System\
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
3. Create new DWORD registery named `EnableAmsTcpLoopback` with value of `1`
|
|
265
|
+
4. Restart
|
|
266
|
+
|
|
267
|
+

|
|
268
|
+
|
|
269
|
+
Now you can connect to the localhost using `targetAmsNetId` address of `127.0.0.1.1.1` or `localhost`.
|
|
270
|
+
|
|
271
|
+
## Structured variables
|
|
272
|
+
|
|
273
|
+
When writing structured variables, the object properties are handled case-insensitively. This is because the TwinCAT is case-insensitive.
|
|
274
|
+
|
|
275
|
+
In practice, it means that the following objects are equal when passed to `writevalue()` or `convertToRaw()`:
|
|
276
|
+
|
|
277
|
+
```js
|
|
278
|
+
{
|
|
279
|
+
sometext: 'hello',
|
|
280
|
+
somereal: 3.14
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
```js
|
|
285
|
+
{
|
|
286
|
+
SOmeTEXT: 'hello',
|
|
287
|
+
SOMEreal: 3.14
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
If there are multiple properties with the same name (in case-insensitive manner), the client selects probably the first one (the selection is done by `Object.find()`):
|
|
292
|
+
|
|
293
|
+
```js
|
|
294
|
+
//In this case, probably the first one (sometext) is selected and the SOMEtext is skipped.
|
|
295
|
+
{
|
|
296
|
+
sometext: 'hello',
|
|
297
|
+
SOMEtext: 'good day'
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Connecting to targets without a PLC runtime
|
|
302
|
+
|
|
303
|
+
Usually the `ads-client` is used to connect to a PLC runtime. Howevever, it's also possible to connect to any device supporting ADS protocol, such as
|
|
304
|
+
|
|
305
|
+
* TwinCAT system service
|
|
306
|
+
* I/O devices (EtherCAT, K-bus etc.)
|
|
307
|
+
* [ads-server](https://github.com/jisotalo/ads-server) instances
|
|
308
|
+
|
|
309
|
+
As default, the client assumes that there should be a PLC runtime. This causes errors with non-PLC systems, as different PLC related things are initialized.
|
|
310
|
+
```
|
|
311
|
+
Connection failed - failed to set PLC connection. If target is not PLC runtime, use setting "rawClient". If system is in config mode or there is no PLC software yet, you might want to use setting "allowHalfOpen".
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
By using the `rawClient` setting, the client allows raw connections to any ADS supported system.
|
|
315
|
+
|
|
316
|
+
```js
|
|
317
|
+
const client = new Client({
|
|
318
|
+
targetAmsNetId: '192.168.5.131.3.1',
|
|
319
|
+
targetAdsPort: 1002,
|
|
320
|
+
rawClient: true // <-- NOTE
|
|
321
|
+
})
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Note that when the `rawclient` setting is set, the client only connects to the target. All other background features, such as monitoring system state, PLC symbol version or connection issues, are not available.
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
## Differences when using with TwinCAT 2
|
|
328
|
+
|
|
329
|
+
* ADS port for first the PLC runtime is 801 instead of 851
|
|
330
|
+
|
|
331
|
+
```js
|
|
332
|
+
const client = new ads.Client({
|
|
333
|
+
targetAmsNetId: '...',
|
|
334
|
+
targetAdsPort: 801 //<-- NOTE
|
|
335
|
+
});
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
* All variable and data type names are in UPPERCASE
|
|
339
|
+
|
|
340
|
+
This might cause problems if your app is used with both TC2 & TC3 systems.
|
|
341
|
+
|
|
342
|
+

|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
* Global variables are accessed with dot (`.`) prefix (without the GVL name)
|
|
346
|
+
|
|
347
|
+
```js
|
|
348
|
+
await client.readSymbol('GVL_Test.ExampleSTRUCT') //TwinCAT 3
|
|
349
|
+
await client.readSymbol('.ExampleSTRUCT') //TwinCAT 2
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
* ENUMs are always numeric values only
|
|
353
|
+
|
|
354
|
+
* Empty structs and function blocks (without members) can't be read
|
|
355
|
+
|
|
356
|
+
# Getting started
|
|
357
|
+
|
|
358
|
+
## Documentation
|
|
359
|
+
|
|
360
|
+
The documentation is available at [https://jisotalo.fi/ads-client](TODO-DOC-URL-HERE/classes/Client.html) and `./docs` folder.
|
|
361
|
+
|
|
362
|
+
Examples in the getting started are based on a PLC project from [https://github.com/jisotalo/ads-client-test-plc-project](https://github.com/jisotalo/ads-client-test-plc-project).
|
|
363
|
+
|
|
364
|
+
You can use the test PLC project as reference together with the [ads-client.test.js](https://github.com/jisotalo/ads-client/blob/master/test/TC3/ads-client.test.js) to see and try out all available features.
|
|
365
|
+
|
|
366
|
+
## Available methods
|
|
367
|
+
|
|
368
|
+
Click a method to open it's documentation.
|
|
369
|
+
|
|
370
|
+
| Method | Description |
|
|
371
|
+
| ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
372
|
+
| [`cacheDataTypes()`](TODO-DOC-URL-HERE/classes/Client.html#cacheDataTypes) | Caches all data types from the target PLC runtime. |
|
|
373
|
+
| [`cacheSymbols()`](TODO-DOC-URL-HERE/classes/Client.html#cacheSymbols) | Caches all symbols from the target PLC runtime. |
|
|
374
|
+
| [`connect()`](TODO-DOC-URL-HERE/classes/Client.html#connect) | Connects to the target. |
|
|
375
|
+
| [`convertFromRaw()`](TODO-DOC-URL-HERE/classes/Client.html#convertFromRaw) | Converts raw data to a Javascript object by using the provided data type. |
|
|
376
|
+
| [`convertToRaw()`](TODO-DOC-URL-HERE/classes/Client.html#convertToRaw) | Converts a Javascript object to raw data by using the provided data type. |
|
|
377
|
+
| [`createVariableHandle()`](TODO-DOC-URL-HERE/classes/Client.html#createVariableHandle) | Creates a handle to a variable at the target system by variable path (such as `GVL_Test.ExampleStruct`). |
|
|
378
|
+
| [`createVariableHandleMulti()`](TODO-DOC-URL-HERE/classes/Client.html#createVariableHandleMulti) | Sends multiple `createVariableHandle()` commands in one ADS packet (ADS sum command). |
|
|
379
|
+
| [`deleteVariableHandle()`](TODO-DOC-URL-HERE/classes/Client.html#deleteVariableHandle) | Deletes a variable handle that was previously created using `createVariableHandle()`. |
|
|
380
|
+
| [`deleteVariableHandleMulti()`](TODO-DOC-URL-HERE/classes/Client.html#deleteVariableHandleMulti) | Sends multiple `deleteVariableHandle()` commands in one ADS packet (ADS sum command). |
|
|
381
|
+
| [`disconnect()`](TODO-DOC-URL-HERE/classes/Client.html#disconnect) | Disconnects from the target and closes active connection. |
|
|
382
|
+
| [`getDataType()`](TODO-DOC-URL-HERE/classes/Client.html#getDataType) | Returns full data type declaration for requested data type (such as `ST_Struct`). |
|
|
383
|
+
| [`getDataTypes()`](TODO-DOC-URL-HERE/classes/Client.html#getDataTypes) | Returns all target PLC runtime data types. |
|
|
384
|
+
| [`getDefaultPlcObject()`](TODO-DOC-URL-HERE/classes/Client.html#getDefaultPlcObject) | Returns a default (empty) Javascript object representing provided PLC data type. |
|
|
385
|
+
| [`getSymbol()`](TODO-DOC-URL-HERE/classes/Client.html#getSymbol) | Returns a symbol object for given variable path (such as `GVL_Test.ExampleStruct`). |
|
|
386
|
+
| [`getSymbols()`](TODO-DOC-URL-HERE/classes/Client.html#getSymbols) | Returns all symbols from the target PLC runtime. |
|
|
387
|
+
| [`invokeRpcMethod()`](TODO-DOC-URL-HERE/classes/Client.html#invokeRpcMethod) | Invokes a function block RPC method on the target system. |
|
|
388
|
+
| [`readDeviceInfo()`](TODO-DOC-URL-HERE/classes/Client.html#readDeviceInfo) | Reads target device information. |
|
|
389
|
+
| [`readPlcRuntimeState()`](TODO-DOC-URL-HERE/classes/Client.html#readPlcRuntimeState) | Reads target PLC runtime state (`Run`, `Stop` etc.) |
|
|
390
|
+
| [`readPlcSymbolVersion()`](TODO-DOC-URL-HERE/classes/Client.html#readPlcSymbolVersion) | Reads target PLC runtime symbol version. |
|
|
391
|
+
| [`readPlcUploadInfo()`](TODO-DOC-URL-HERE/classes/Client.html#readPlcUploadInfo) | Reads target PLC runtime upload information. |
|
|
392
|
+
| [`readRaw()`](TODO-DOC-URL-HERE/classes/Client.html#readRaw) | Reads raw data from the target system by a raw ADS address (index group, index offset and data length). |
|
|
393
|
+
| [`readRawByHandle()`](TODO-DOC-URL-HERE/classes/Client.html#readRawByHandle) | Reads raw data from the target system by a previously created variable handle (acquired using `createVariableHandle()`). |
|
|
394
|
+
| [`readRawByPath()`](TODO-DOC-URL-HERE/classes/Client.html#readRawByPath) | Reads raw data from the target system by variable path (such as `GVL_Test.ExampleStruct`). |
|
|
395
|
+
| [`readRawBySymbol()`](TODO-DOC-URL-HERE/classes/Client.html#readRawBySymbol) | Reads raw data from the target system by a symbol object (acquired using `getSymbol()`). |
|
|
396
|
+
| [`readRawMulti()`](TODO-DOC-URL-HERE/classes/Client.html#readRawMulti) | Sends multiple `readRaw()` commands in one ADS packet (ADS sum command). |
|
|
397
|
+
| [`readState()`](TODO-DOC-URL-HERE/classes/Client.html#readState) | Reads target ADS state. |
|
|
398
|
+
| [`readTcSystemState()`](TODO-DOC-URL-HERE/classes/Client.html#readTcSystemState) | Reads target TwinCAT system state from ADS port 10000 (usually `Run` or `Config`). |
|
|
399
|
+
| [`readValue()`](TODO-DOC-URL-HERE/classes/Client.html#readValue) | Reads variable's value from the target system by a variable path (such as `GVL_Test.ExampleStruct`) and returns the value as a Javascript object. |
|
|
400
|
+
| [`readValueBySymbol()`](TODO-DOC-URL-HERE/classes/Client.html#readValueBySymbol) | Reads variable's value from the target system by a symbol object (acquired using `getSymbol()`) and returns the value as a Javascript object. |
|
|
401
|
+
| [`readWriteRaw()`](TODO-DOC-URL-HERE/classes/Client.html#readWriteRaw) | Writes raw data to the target system by a raw ADS address (index group, index offset) and reads the result as raw data. |
|
|
402
|
+
| [`readWriteRawMulti()`](TODO-DOC-URL-HERE/classes/Client.html#readWriteRawMulti) | Sends multiple `readWriteRaw()` commands in one ADS packet (ADS sum command). |
|
|
403
|
+
| [`reconnect()`](TODO-DOC-URL-HERE/classes/Client.html#reconnect) | Reconnects to the target (disconnects and then connects again). |
|
|
404
|
+
| [`resetPlc()`](TODO-DOC-URL-HERE/classes/Client.html#resetPlc) | Resets the target PLC runtime. Same as reset cold in TwinCAT XAE. |
|
|
405
|
+
| [`restartPlc()`](TODO-DOC-URL-HERE/classes/Client.html#restartPlc) | Restarts the PLC runtime. Same as calling `resetPlc()` and then `startPlc()`. |
|
|
406
|
+
| [`restartTcSystem()`](TODO-DOC-URL-HERE/classes/Client.html#restartTcSystem) | Restarts the target TwinCAT system. |
|
|
407
|
+
| [`sendAdsCommand()`](TODO-DOC-URL-HERE/classes/Client.html#sendAdsCommand) | Sends a raw ADS command to the target. |
|
|
408
|
+
| [`setDebugLevel()`](TODO-DOC-URL-HERE/classes/Client.html#setDebugLevel) | Sets active debug level. |
|
|
409
|
+
| [`setTcSystemToConfig()`](TODO-DOC-URL-HERE/classes/Client.html#setTcSystemToConfig) | Sets the target TwinCAT system to config mode. Same as `Restart TwinCAT (Config mode)` in TwinCAT XAE. |
|
|
410
|
+
| [`setTcSystemToRun()`](TODO-DOC-URL-HERE/classes/Client.html#setTcSystemToRun) | Sets the target TwinCAT system to run mode. Same as `Restart TwinCAT system` in TwinCAT XAE. |
|
|
411
|
+
| [`startPlc()`](TODO-DOC-URL-HERE/classes/Client.html#startPlc) | Starts the target PLC runtime. Same as pressing the green play button in TwinCAT XAE. |
|
|
412
|
+
| [`stopPlc()`](TODO-DOC-URL-HERE/classes/Client.html#stopPlc) | Stops the target PLC runtime. Same as pressing the red stop button in TwinCAT XAE. |
|
|
413
|
+
| [`subscribe()`](TODO-DOC-URL-HERE/classes/Client.html#subscribe) | Subscribes to value change notifications (ADS notifications) by variable path (such as `GVL_Test.ExampleStruct`) or raw ADS address (index group, index offset and data length). |
|
|
414
|
+
| [`subscribeRaw()`](TODO-DOC-URL-HERE/classes/Client.html#subscribeRaw) | Subscribes to raw value change notifications (ADS notifications) by a raw ADS address (index group, index offset and data length). |
|
|
415
|
+
| [`subscribeValue()`](TODO-DOC-URL-HERE/classes/Client.html#subscribeValue) | Subscribes to value change notifications (ADS notifications) by a variable path, such as `GVL_Test.ExampleStruct`. |
|
|
416
|
+
| [`unsubscribe()`](TODO-DOC-URL-HERE/classes/Client.html#unsubscribe) | Unsubscribes a subscription (deletes ADS notification). |
|
|
417
|
+
| [`unsubscribeAll()`](TODO-DOC-URL-HERE/classes/Client.html#unsubscribeAll) | Unsubscribes all active subscription (deletes all ADS notifications). |
|
|
418
|
+
| [`writeControl()`](TODO-DOC-URL-HERE/classes/Client.html#writeControl) | Sends an ADS `WriteControl` command to the target. |
|
|
419
|
+
| [`writeRaw()`](TODO-DOC-URL-HERE/classes/Client.html#writeRaw) | Writes raw data to the target system by a raw ADS address (index group, index offset and data length). |
|
|
420
|
+
| [`writeRawByHandle()`](TODO-DOC-URL-HERE/classes/Client.html#writeRawByHandle) | Writes raw data to the target system by a previously created variable handle (acquired using `createVariableHandle()`). |
|
|
421
|
+
| [`writeRawByPath()`](TODO-DOC-URL-HERE/classes/Client.html#writeRawByPath) | Writes raw data to the target system by variable path (such as `GVL_Test.ExampleStruct`). |
|
|
422
|
+
| [`writeRawBySymbol()`](TODO-DOC-URL-HERE/classes/Client.html#writeRawBySymbol) | Writes raw data to the target system by a symbol object (acquired using `getSymbol()`). |
|
|
423
|
+
| [`writeRawMulti()`](TODO-DOC-URL-HERE/classes/Client.html#writeRawMulti) | Sends multiple `writeRaw()` commands in one ADS packet (ADS sum command). |
|
|
424
|
+
| [`writeValue()`](TODO-DOC-URL-HERE/classes/Client.html#writeValue) | Writes variable's value to the target system by a variable path (such as `GVL_Test.ExampleStruct`). Converts the value from a Javascript object to a raw value. |
|
|
425
|
+
| [`writeValueBySymbol()`](TODO-DOC-URL-HERE/classes/Client.html#writeValueBySymbol) | Writes variable's value to the target system by a symbol object (acquired using `getSymbol()`). Converts the value from a Javascript object to a raw value. |
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
## Creating a client
|
|
433
|
+
|
|
434
|
+
Settings are passed via the [`Client`](https://jisotalo.fi/ads-client/classes/Client.html) constructor. The following settings are mandatory:
|
|
435
|
+
- [`targetAmsNetId`](https://jisotalo.fi/ads-client/interfaces/AdsClientSettings.html#targetAmsNetId) - Target runtime AmsNetId
|
|
436
|
+
- [`targetAdsPort`](https://jisotalo.fi/ads-client/interfaces/AdsClientSettings.html#targetAdsPort) - Target runtime ADS port
|
|
437
|
+
|
|
438
|
+
See other settings from the [`AdsClientSettings` documentation](https://jisotalo.fi/ads-client/interfaces/AdsClientSettings.html).
|
|
40
439
|
|
|
41
440
|
```js
|
|
42
441
|
const client = new Client({
|
|
43
442
|
targetAmsNetId: "localhost",
|
|
44
443
|
targetAdsPort: 851
|
|
45
444
|
});
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
## Connecting
|
|
448
|
+
|
|
449
|
+
It's a good practice to start a connection at startup and keep it open until the app is closed.
|
|
450
|
+
If there are connection issues or the PLC software is updated, the client will handle everything automatically.
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
```js
|
|
454
|
+
const { Client } = require('ads-client');
|
|
455
|
+
|
|
456
|
+
const client = new Client({
|
|
457
|
+
targetAmsNetId: 'localhost',
|
|
458
|
+
targetAdsPort: 851
|
|
459
|
+
})
|
|
460
|
+
|
|
461
|
+
client.connect()
|
|
462
|
+
.then(res => {
|
|
463
|
+
console.log(`Connected to the ${res.targetAmsNetId}`)
|
|
464
|
+
console.log(`Router assigned us AmsNetId ${res.localAmsNetId} and port ${res.localAdsPort}`)
|
|
46
465
|
|
|
47
|
-
|
|
466
|
+
}).catch(err => {
|
|
467
|
+
console.log('Error:', err)
|
|
468
|
+
});
|
|
48
469
|
```
|
|
49
470
|
|
|
50
|
-
|
|
471
|
+
## Reading values
|
|
472
|
+
|
|
473
|
+
### Reading any value
|
|
474
|
+
|
|
475
|
+
Use [`readValue()`](TODO-DOC-URL-HERE/classes/Client.html#readValue) to read any PLC value as a Javascript object. If using Typescript, the type of the PLC variable can be passed as well (`readValue<T>()`).
|
|
476
|
+
|
|
477
|
+
The only exception is the dereferenced value of a reference/pointer, see [Reading reference/pointer](#reading-referencepointer).
|
|
51
478
|
|
|
52
479
|
```js
|
|
53
|
-
|
|
54
|
-
|
|
480
|
+
try {
|
|
481
|
+
let res;
|
|
55
482
|
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
console.log(res.value);
|
|
483
|
+
//Example: INT
|
|
484
|
+
res = await client.readValue('GVL_Read.StandardTypes.INT_');
|
|
485
|
+
console.log(res.value);
|
|
486
|
+
// 32767
|
|
487
|
+
|
|
488
|
+
//Example: STRING
|
|
489
|
+
res = await client.readValue('GVL_Read.StandardTypes.STRING_');
|
|
490
|
+
console.log(res.value);
|
|
491
|
+
// A test string ääöö!!@@
|
|
492
|
+
|
|
493
|
+
//Example: DT
|
|
494
|
+
res = await client.readValue('GVL_Read.StandardTypes.DT_');
|
|
495
|
+
console.log(res.value);
|
|
496
|
+
// 2106-02-06T06:28:15.000Z (Date object)
|
|
497
|
+
|
|
498
|
+
//Example: STRUCT
|
|
499
|
+
res = await client.readValue('GVL_Read.ComplexTypes.STRUCT_');
|
|
500
|
+
console.log(res.value);
|
|
501
|
+
/*
|
|
502
|
+
{
|
|
503
|
+
BOOL_: true,
|
|
504
|
+
BOOL_2: false,
|
|
505
|
+
BYTE_: 255,
|
|
506
|
+
WORD_: 65535,
|
|
507
|
+
//...and so on
|
|
508
|
+
}
|
|
509
|
+
*/
|
|
510
|
+
|
|
511
|
+
//Example: FUNCTION_BLOCK
|
|
512
|
+
res = await client.readValue('GVL_Read.ComplexTypes.BLOCK_2'); //TON
|
|
513
|
+
console.log(res.value);
|
|
514
|
+
// { IN: false, PT: 2500, Q: false, ET: 0, M: false, StartTime: 0 }
|
|
515
|
+
|
|
516
|
+
//Example: ARRAY
|
|
517
|
+
res = await client.readValue('GVL_Read.StandardArrays.REAL_3');
|
|
518
|
+
console.log(res.value);
|
|
519
|
+
// [ 75483.546875, 0, -75483.546875 ]
|
|
59
520
|
|
|
521
|
+
//Example: ENUM
|
|
522
|
+
res = await client.readValue('GVL_Read.ComplexTypes.ENUM_');
|
|
523
|
+
console.log(res.value);
|
|
524
|
+
// { name: 'Running', value: 100 }
|
|
525
|
+
|
|
526
|
+
} catch (err) {
|
|
527
|
+
console.log("Error:", err);
|
|
528
|
+
}
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
**Typescript example**:
|
|
532
|
+
|
|
533
|
+
```ts
|
|
534
|
+
let res;
|
|
535
|
+
|
|
536
|
+
//Example: INT
|
|
537
|
+
res = await client.readValue<number>('GVL_Read.StandardTypes.INT_');
|
|
538
|
+
console.log(res.value); //res.value is typed as number
|
|
539
|
+
// 32767
|
|
540
|
+
|
|
541
|
+
//Example: STRUCT
|
|
542
|
+
interface ST_ComplexTypes {
|
|
543
|
+
BOOL_: boolean,
|
|
544
|
+
BOOL_2: boolean,
|
|
545
|
+
BYTE_: number,
|
|
546
|
+
WORD_: number,
|
|
547
|
+
//..and so on
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
res = await client.readValue<ST_ComplexTypes>('GVL_Read.ComplexTypes.STRUCT_');
|
|
551
|
+
console.log(res.value); //res.value is typed as ST_ComplexTypes
|
|
552
|
+
/*
|
|
553
|
+
{
|
|
554
|
+
BOOL_: true,
|
|
555
|
+
BOOL_2: false,
|
|
556
|
+
BYTE_: 255,
|
|
557
|
+
WORD_: 65535,
|
|
558
|
+
//..and so on
|
|
559
|
+
}
|
|
560
|
+
*/
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### Reading raw data
|
|
564
|
+
|
|
565
|
+
Use [`readRaw()`](TODO-DOC-URL-HERE/classes/Client.html#readRaw) to read any PLC value as raw data. Raw data is a `Buffer` object, containing the data as bytes.
|
|
566
|
+
|
|
567
|
+
The only exception is the dereferenced value of a reference/pointer, see [Reading reference/pointer](#reading-referencepointer).
|
|
568
|
+
|
|
569
|
+
The `indexGroup` and `indexOffset` can be acquired for example by using [`getSymbol()`](TODO-DOC-URL-HERE/classes/Client.html#getSymbol).
|
|
570
|
+
|
|
571
|
+
```js
|
|
572
|
+
try {
|
|
573
|
+
//Example: Reading raw data by index offset and index group
|
|
574
|
+
//and converting it to a Javascript object manually
|
|
575
|
+
const data = await client.readRaw(16448, 414816, 2);
|
|
576
|
+
console.log(data); //<Buffer ff 7f>
|
|
577
|
+
|
|
578
|
+
const converted = await client.convertFromRaw(data, 'INT');
|
|
579
|
+
console.log(converted); //32767
|
|
580
|
+
|
|
581
|
+
} catch (err) {
|
|
582
|
+
console.log("Error:", err);
|
|
583
|
+
}
|
|
60
584
|
```
|
|
61
585
|
|
|
62
|
-
|
|
586
|
+
[`readRawByPath()`](TODO-DOC-URL-HERE/classes/Client.html#readRawByPath) can be used to read raw data by a variable path.
|
|
63
587
|
|
|
64
588
|
```js
|
|
65
|
-
|
|
589
|
+
try {
|
|
590
|
+
//Example: Reading raw data by variable path
|
|
591
|
+
//and converting it to a Javascript object manually
|
|
592
|
+
const data = await client.readRawByPath('GVL_Read.StandardTypes.INT_');
|
|
593
|
+
console.log(data); //<Buffer ff 7f>
|
|
594
|
+
|
|
595
|
+
const converted = await client.convertFromRaw(data, 'INT');
|
|
596
|
+
console.log(converted); //32767
|
|
597
|
+
|
|
598
|
+
} catch (err) {
|
|
599
|
+
console.log("Error:", err);
|
|
600
|
+
}
|
|
66
601
|
```
|
|
67
|
-
|
|
602
|
+
|
|
603
|
+
### Reading reference/pointer
|
|
604
|
+
|
|
605
|
+
The dereferenced value of a reference (`REFERENCE TO`) or a pointer (`POINTER TO`) can be read
|
|
606
|
+
using [`readRawByPath()`](TODO-DOC-URL-HERE/classes/Client.html#readRawByPath).
|
|
68
607
|
|
|
69
608
|
```js
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
callback: (data, subscription) => console.log(`${data.timestamp}: Value changed to ${data.value}`)
|
|
74
|
-
});
|
|
609
|
+
try {
|
|
610
|
+
//Reading a raw POINTER value (Note the dereference operator ^)
|
|
611
|
+
const rawPtrValue = await client.readRawByPath('GVL_Read.ComplexTypes.POINTER_^');
|
|
75
612
|
|
|
76
|
-
//
|
|
77
|
-
await client.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
613
|
+
//Converting to a Javascript object
|
|
614
|
+
const ptrValue = await client.convertFromRaw(rawPtrValue, 'ST_StandardTypes');
|
|
615
|
+
console.log(ptrValue);
|
|
616
|
+
|
|
617
|
+
//Reading a raw REFERENCE value
|
|
618
|
+
const rawRefValue = await client.readRawByPath('GVL_Read.ComplexTypes.REFERENCE_');
|
|
619
|
+
|
|
620
|
+
//Converting to a Javascript object
|
|
621
|
+
const refValue = await client.convertFromRaw(rawRefValue, 'ST_StandardTypes');
|
|
622
|
+
console.log(refValue);
|
|
623
|
+
|
|
624
|
+
//Short version:
|
|
625
|
+
const refValue2 = await client.convertFromRaw(
|
|
626
|
+
await client.readRawByPath('GVL_Read.ComplexTypes.REFERENCE_'),
|
|
627
|
+
'ST_StandardTypes'
|
|
628
|
+
);
|
|
629
|
+
|
|
630
|
+
} catch (err) {
|
|
631
|
+
console.log("Error:", err);
|
|
632
|
+
}
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
Another way is to use [variable handles](TODO-DOC-URL-HERE/classes/Client.html#createVariableHandle).
|
|
636
|
+
|
|
637
|
+
```js
|
|
638
|
+
try {
|
|
639
|
+
//Reading a POINTER value (Note the dereference operator ^)
|
|
640
|
+
const ptrHandle = await client.createVariableHandle('GVL_Read.ComplexTypes.POINTER_^');
|
|
641
|
+
const rawPtrValue = await client.readRawByHandle(ptrHandle);
|
|
642
|
+
await client.deleteVariableHandle(ptrHandle);
|
|
643
|
+
|
|
644
|
+
//Converting to a Javascript object
|
|
645
|
+
const ptrValue = await client.convertFromRaw(rawPtrValue, 'ST_StandardTypes');
|
|
646
|
+
console.log(ptrValue);
|
|
647
|
+
|
|
648
|
+
//Reading a REFERENCE value
|
|
649
|
+
const refHandle = await client.createVariableHandle('GVL_Read.ComplexTypes.REFERENCE_');
|
|
650
|
+
const rawRefValue = await client.readRawByHandle(refHandle);
|
|
651
|
+
await client.deleteVariableHandle(refHandle);
|
|
652
|
+
|
|
653
|
+
//Converting to a Javascript object
|
|
654
|
+
const refValue = await client.convertFromRaw(rawRefValue, 'ST_StandardTypes');
|
|
655
|
+
console.log(refValue);
|
|
656
|
+
|
|
657
|
+
} catch (err) {
|
|
658
|
+
console.log("Error:", err);
|
|
659
|
+
}
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
## Writing values
|
|
663
|
+
|
|
664
|
+
### Writing any value
|
|
665
|
+
|
|
666
|
+
Use [`writeValue()`](TODO-DOC-URL-HERE/classes/Client.html#writeValue) to write any PLC value.
|
|
667
|
+
|
|
668
|
+
The only exception is the dereferenced value of a reference/pointer, see [Writing reference/pointer](#writing-referencepointer).
|
|
669
|
+
|
|
670
|
+
```js
|
|
671
|
+
try {
|
|
672
|
+
//Example: INT
|
|
673
|
+
await client.writeValue('GVL_Write.StandardTypes.INT_', 32767);
|
|
674
|
+
|
|
675
|
+
//Example: STRING
|
|
676
|
+
await client.writeValue('GVL_Write.StandardTypes.STRING_', 'This is a test');
|
|
677
|
+
|
|
678
|
+
//Example: DT
|
|
679
|
+
await client.writeValue('GVL_Write.StandardTypes.DT_', new Date());
|
|
680
|
+
|
|
681
|
+
//Example: STRUCT (all properties)
|
|
682
|
+
await client.writeValue('GVL_Write.ComplexTypes.STRUCT_', {
|
|
683
|
+
BOOL_: true,
|
|
684
|
+
BOOL_2: false,
|
|
685
|
+
BYTE_: 255,
|
|
686
|
+
WORD_: 65535,
|
|
687
|
+
//...and so on
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
//Example: STRUCT (only some properties)
|
|
691
|
+
await client.writeValue('GVL_Write.ComplexTypes.STRUCT_', {
|
|
692
|
+
WORD_: 65535
|
|
693
|
+
}, true); //<-- NOTE: autoFill set
|
|
694
|
+
|
|
695
|
+
//Example: FUNCTION_BLOCK (all properties)
|
|
696
|
+
await client.writeValue('GVL_Write.ComplexTypes.BLOCK_2', {
|
|
697
|
+
IN: false,
|
|
698
|
+
PT: 2500,
|
|
699
|
+
Q: false,
|
|
700
|
+
ET: 0,
|
|
701
|
+
M: false,
|
|
702
|
+
StartTime: 0
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
//Example: FUNCTION_BLOCK (only some properties)
|
|
706
|
+
await client.writeValue('GVL_Write.ComplexTypes.BLOCK_2', {
|
|
707
|
+
IN: true
|
|
708
|
+
}, true); //<-- NOTE: autoFill set
|
|
709
|
+
|
|
710
|
+
//Example: ARRAY
|
|
711
|
+
await client.writeValue('GVL_Write.StandardArrays.REAL_3', [
|
|
712
|
+
75483.546875,
|
|
713
|
+
0,
|
|
714
|
+
-75483.546875
|
|
715
|
+
]);
|
|
716
|
+
|
|
717
|
+
//Example: ENUM
|
|
718
|
+
res = await client.writeValue('GVL_Write.ComplexTypes.ENUM_', 'Running');
|
|
719
|
+
//...or...
|
|
720
|
+
res = await client.writeValue('GVL_Write.ComplexTypes.ENUM_', 100);
|
|
721
|
+
|
|
722
|
+
} catch (err) {
|
|
723
|
+
console.log("Error:", err);
|
|
724
|
+
}
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
### Writing raw data
|
|
728
|
+
|
|
729
|
+
Use [`writeRaw()`](TODO-DOC-URL-HERE/classes/Client.html#writeRaw) to write any PLC value using raw data. Raw data is a `Buffer` object, containing the data as bytes.
|
|
730
|
+
|
|
731
|
+
The only exception is the dereferenced value of a reference/pointer, see [Reading reference/pointer](#reading-referencepointer).
|
|
732
|
+
|
|
733
|
+
The `indexGroup` and `indexOffset` can be acquired for example by using [`getSymbol()`](TODO-DOC-URL-HERE/classes/Client.html#getSymbol).
|
|
734
|
+
|
|
735
|
+
```js
|
|
736
|
+
try {
|
|
737
|
+
//Example: Writing a INT variable by index offset and index group using raw data
|
|
738
|
+
const data = await client.convertToRaw(32767, 'INT');
|
|
739
|
+
console.log(data); //<Buffer ff 7f>
|
|
740
|
+
|
|
741
|
+
await client.writeRaw(16448, 414816, data);
|
|
742
|
+
|
|
743
|
+
} catch (err) {
|
|
744
|
+
console.log("Error:", err);
|
|
745
|
+
}
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
[`writeRawByPath()`](TODO-DOC-URL-HERE/classes/Client.html#writeRawByPath) can be used to write raw data by a variable path.
|
|
749
|
+
|
|
750
|
+
```js
|
|
751
|
+
try {
|
|
752
|
+
//Example: Writing a INT variable by its variable path using raw data
|
|
753
|
+
const data = await client.convertToRaw(32767, 'INT');
|
|
754
|
+
console.log(data); //<Buffer ff 7f>
|
|
755
|
+
|
|
756
|
+
await client.writeRawByPath('GVL_Write.StandardTypes.INT_', data);
|
|
757
|
+
|
|
758
|
+
} catch (err) {
|
|
759
|
+
console.log("Error:", err);
|
|
760
|
+
}
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
### Writing reference/pointer
|
|
764
|
+
|
|
765
|
+
The dereferenced value of a reference (`REFERENCE TO`) or a pointer (`POINTER TO`) can be written
|
|
766
|
+
using [`writeRawByPath()`](TODO-DOC-URL-HERE/classes/Client.html#writeRawByPath).
|
|
767
|
+
|
|
768
|
+
```js
|
|
769
|
+
try {
|
|
770
|
+
//Writing a POINTER value
|
|
771
|
+
const ptrValue = {...} //some value
|
|
772
|
+
const rawPtrValue = await client.convertToRaw(ptrValue, 'ST_StandardTypes');
|
|
773
|
+
|
|
774
|
+
await client.writeRawByPath('GVL_Write.ComplexTypes.POINTER_^', rawPtrValue);
|
|
775
|
+
|
|
776
|
+
//Writing a REFERENCE value
|
|
777
|
+
const refValue = {...} //some value
|
|
778
|
+
const rawRefValue = await client.convertToRaw(refValue, 'ST_StandardTypes');
|
|
779
|
+
await client.writeRawByPath('GVL_Write.ComplexTypes.REFERENCE_', rawRefValue);
|
|
780
|
+
|
|
781
|
+
} catch (err) {
|
|
782
|
+
console.log("Error:", err);
|
|
783
|
+
}
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
Another way is to use [variable handles](TODO-DOC-URL-HERE/classes/Client.html#createVariableHandle).
|
|
787
|
+
|
|
788
|
+
```js
|
|
789
|
+
try {
|
|
790
|
+
//Writing a POINTER value (Note the dereference operator ^)
|
|
791
|
+
const ptrValue = {...} //some value
|
|
792
|
+
const rawPtrValue = await client.convertToRaw(ptrValue, 'ST_StandardTypes');
|
|
793
|
+
|
|
794
|
+
const ptrHandle = await client.createVariableHandle('GVL_Write.ComplexTypes.POINTER_^');
|
|
795
|
+
await client.writeRawByHandle(ptrHandle, rawPtrValue);
|
|
796
|
+
await client.deleteVariableHandle(ptrHandle);
|
|
797
|
+
|
|
798
|
+
//Writing a REFERENCE value
|
|
799
|
+
const refValue = {...} //some value
|
|
800
|
+
const rawRefValue = await client.convertToRaw(refValue, 'ST_StandardTypes');
|
|
801
|
+
|
|
802
|
+
const refHandle = await client.createVariableHandle('GVL_Write.ComplexTypes.REFERENCE_');
|
|
803
|
+
await client.writeRawByHandle(refHandle, rawRefValue);
|
|
804
|
+
await client.deleteVariableHandle(refHandle);
|
|
805
|
+
|
|
806
|
+
} catch (err) {
|
|
807
|
+
console.log("Error:", err);
|
|
808
|
+
}
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
## Subscribing to value changes
|
|
812
|
+
|
|
813
|
+
In ads-client, subscriptions are used to handle ADS notifications.
|
|
814
|
+
|
|
815
|
+
By subscribing to a variable value changes, the target system (PLC) will send ADS notifications when the value changes (or every x milliseconds). The client then receives these notifications and calls the callback function with the latest value.
|
|
816
|
+
|
|
817
|
+
More information about ADS notifications at [Beckhoff Infosys: Use of ADS Notifications](https://infosys.beckhoff.com/content/1033/tc3_ads.net/9407523595.html?id=431879546285476216).
|
|
818
|
+
|
|
819
|
+
### Any value
|
|
820
|
+
|
|
821
|
+
Use [`subscribeValue()`](TODO-DOC-URL-HERE/classes/Client.html#subscribeValue) or [`subscribe()`](TODO-DOC-URL-HERE/classes/Client.html#subscribe) to subscribe to PLC variable value changes.
|
|
822
|
+
|
|
823
|
+
Example: Subscribe to changes of `GVL_Subscription.NumericValue_10ms`. The callback is called when the PLC value changes (at maximum every 100 milliseconds).
|
|
824
|
+
```js
|
|
825
|
+
try {
|
|
826
|
+
const sub = await client.subscribeValue(
|
|
827
|
+
'GVL_Subscription.NumericValue_10ms',
|
|
828
|
+
(data, subscription) => {
|
|
829
|
+
console.log(`Value of ${subscription.symbol.name} has changed: ${data.value}`);
|
|
830
|
+
},
|
|
831
|
+
100
|
|
832
|
+
);
|
|
833
|
+
|
|
834
|
+
} catch (err) {
|
|
835
|
+
console.log("Error:", err);
|
|
836
|
+
}
|
|
837
|
+
```
|
|
838
|
+
|
|
839
|
+
Example: Subscribe to value of `GVL_Subscription.NumericValue_1000ms`. The callback is called with the latest value every 100 milliseconds (changed or not).
|
|
840
|
+
|
|
841
|
+
```js
|
|
842
|
+
try {
|
|
843
|
+
const valueChanged = (data, subscription) => {
|
|
844
|
+
console.log(`Value of ${subscription.symbol.name}: ${data.value}`);
|
|
83
845
|
}
|
|
84
|
-
|
|
846
|
+
|
|
847
|
+
const sub = await client.subscribeValue(
|
|
848
|
+
'GVL_Subscription.NumericValue_1000ms',
|
|
849
|
+
valueChanged,
|
|
850
|
+
100,
|
|
851
|
+
false
|
|
852
|
+
);
|
|
853
|
+
|
|
854
|
+
} catch (err) {
|
|
855
|
+
console.log("Error:", err);
|
|
856
|
+
}
|
|
85
857
|
```
|
|
86
858
|
|
|
87
|
-
|
|
859
|
+
Example: Same as previous example but using [`subscribe()`](TODO-DOC-URL-HERE/classes/Client.html#subscribe) and Typescript type for the variable value. A type can be provided for `subscribeValue<T>()` as well.
|
|
88
860
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
861
|
+
```js
|
|
862
|
+
//NOTE: Typescript
|
|
863
|
+
try {
|
|
864
|
+
const valueChanged = (data, subscription) => {
|
|
865
|
+
//data.value is typed as "number" instead of "any" (see subscribe() call below)
|
|
866
|
+
console.log(`Value of ${subscription.symbol.name}: ${data.value}`);
|
|
867
|
+
}
|
|
96
868
|
|
|
97
|
-
|
|
869
|
+
const sub = await client.subscribe<number>({
|
|
870
|
+
target: 'GVL_Subscription.NumericValue_1000ms',
|
|
871
|
+
callback: valueChanged,
|
|
872
|
+
cycleTime: 100,
|
|
873
|
+
sendOnChange: false
|
|
874
|
+
});
|
|
98
875
|
|
|
99
|
-
|
|
876
|
+
} catch (err) {
|
|
877
|
+
console.log("Error:", err);
|
|
878
|
+
}
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
### Raw data
|
|
882
|
+
|
|
883
|
+
Use [`subscribeRaw()`](TODO-DOC-URL-HERE/classes/Client.html#subscribeRaw) or [`subscribe()`](TODO-DOC-URL-HERE/classes/Client.html#subscribe) to subscribe to raw value changes.
|
|
884
|
+
|
|
885
|
+
The `indexGroup` and `indexOffset` can be acquired for example by using [`getSymbol()`](TODO-DOC-URL-HERE/classes/Client.html#getSymbol).
|
|
886
|
+
|
|
887
|
+
Example: Subscribe to raw address of `indexGroup` = 16448, `indexOffset` = 414816 and `size` = 2 bytes.
|
|
888
|
+
|
|
889
|
+
```js
|
|
890
|
+
try {
|
|
891
|
+
await client.subscribeRaw(16448, 414816, 2, (data, subscription) => {
|
|
892
|
+
console.log(`Value has changed: ${data.value.toString('hex')}`);
|
|
893
|
+
}, 100);
|
|
894
|
+
|
|
895
|
+
} catch (err) {
|
|
896
|
+
console.log("Error:", err);
|
|
897
|
+
}
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
Same using [`subscribe()`](TODO-DOC-URL-HERE/classes/Client.html#subscribe):
|
|
901
|
+
```js
|
|
902
|
+
try {
|
|
903
|
+
await client.subscribe({
|
|
904
|
+
target: {
|
|
905
|
+
indexGroup: 16448,
|
|
906
|
+
indexOffset: 414816,
|
|
907
|
+
size: 2
|
|
908
|
+
},
|
|
909
|
+
callback: (data, subscription) => {
|
|
910
|
+
console.log(`Value has changed: ${data.value.toString('hex')}`);
|
|
911
|
+
},
|
|
912
|
+
cycleTime: 100
|
|
913
|
+
});
|
|
914
|
+
|
|
915
|
+
} catch (err) {
|
|
916
|
+
console.log("Error:", err);
|
|
917
|
+
}
|
|
918
|
+
```
|
|
919
|
+
|
|
920
|
+
### Unsubscribing
|
|
921
|
+
|
|
922
|
+
Subscriptions should always be cancelled when no longer needed (to save PLC resources). Use [`unsubscribe()`](TODO-DOC-URL-HERE/classes/Client.html#unsubscribe) or subscription object's [`ActiveSubscription.unsubscribe()`](TODO-DOC-URL-HERE/interfaces/ActiveSubscription.html#unsubscribe) to unsubscribe.
|
|
923
|
+
|
|
924
|
+
```js
|
|
925
|
+
const sub = await client.subscribeValue(...);
|
|
926
|
+
|
|
927
|
+
//Later when no longer needed
|
|
928
|
+
await sub.unsubscribe();
|
|
929
|
+
//or await client.unsubscribe(sub);
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
## Using variable handles
|
|
933
|
+
|
|
934
|
+
Variable handles are another alternative to read and write raw data.
|
|
935
|
+
|
|
936
|
+
A handle is created to a specific PLC variable and after that, read and write operations are available.
|
|
937
|
+
There is no need to use `indexGroup` or `indexOffset`.
|
|
938
|
+
|
|
939
|
+
Handles should always be deleted after no longer needed, as the PLC has limited number of handles.
|
|
940
|
+
However, it's a perfectly valid practice to keep the handles open as long as needed.
|
|
941
|
+
|
|
942
|
+
Handles can also be used to read/write reference and pointer values, see [Reading reference/pointer](#reading-referencepointer).
|
|
943
|
+
|
|
944
|
+
### Reading a value using a variable handle
|
|
945
|
+
|
|
946
|
+
```js
|
|
947
|
+
//Creating a handle
|
|
948
|
+
const handle = await client.createVariableHandle('GVL_Read.StandardTypes.INT_');
|
|
949
|
+
|
|
950
|
+
//Reading a value
|
|
951
|
+
const data = await client.readRawByHandle(handle);
|
|
952
|
+
|
|
953
|
+
//Deleting the handle
|
|
954
|
+
await client.deleteVariableHandle(handle);
|
|
955
|
+
|
|
956
|
+
//Converting to a Javascript value
|
|
957
|
+
const converted = await client.convertFromRaw(data, 'INT');
|
|
958
|
+
console.log(data); //<Buffer ff 7f>
|
|
959
|
+
console.log(converted); //32767
|
|
960
|
+
```
|
|
961
|
+
|
|
962
|
+
### Writing a value using a variable handle
|
|
963
|
+
|
|
964
|
+
```js
|
|
965
|
+
//Creating a raw value
|
|
966
|
+
const data = await client.convertToRaw(32767, 'INT');
|
|
967
|
+
console.log(data); //<Buffer ff 7f>
|
|
968
|
+
|
|
969
|
+
//Creating a handle
|
|
970
|
+
const handle = await client.createVariableHandle('GVL_Write.StandardTypes.INT_');
|
|
971
|
+
|
|
972
|
+
//Writing the value
|
|
973
|
+
await client.writeRawByHandle(handle, data);
|
|
974
|
+
|
|
975
|
+
//Deleting the handle
|
|
976
|
+
await client.deleteVariableHandle(handle);
|
|
977
|
+
```
|
|
978
|
+
|
|
979
|
+
## Calling function block RPC methods
|
|
980
|
+
|
|
981
|
+
If a function block method has pragma `{attribute 'TcRpcEnable'}`, the method can be called from ads-client.
|
|
982
|
+
|
|
983
|
+
Read more at [Beckhoff Infosys: Attribute 'TcRpcEnable'](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/7145472907.html).
|
|
984
|
+
|
|
985
|
+
### Things to note when using RPC Methods
|
|
986
|
+
|
|
987
|
+
- Do not use online change if you change RPC method parameters or return data types
|
|
988
|
+
- Make sure that parameters and return value have no pack-mode pragmas defined, otherwise data might be corrupted
|
|
989
|
+
- Do not use `ARRAY` values directly in parameters or return value, encapsulate arrays inside struct and use the struct instead
|
|
990
|
+
- The feature isn't well documented by Bechkhoff, so there might be some things that aren't taken into account
|
|
991
|
+
|
|
992
|
+
### Simple RPC method example
|
|
993
|
+
|
|
994
|
+
### RPC methods with structs
|
|
995
|
+
|
|
996
|
+
## Converting from/to raw data
|
|
997
|
+
|
|
998
|
+
README in progress.
|
|
999
|
+
|
|
1000
|
+
## Other features
|
|
1001
|
+
|
|
1002
|
+
README in progress.
|
|
1003
|
+
|
|
1004
|
+
## Disconnecting
|
|
1005
|
+
|
|
1006
|
+
After the client is no more used, always use [`disconnect()`](TODO-DOC-URL-HERE/classes/Client.html#disconnect) to release all subscription handles and other resources.
|
|
1007
|
+
|
|
1008
|
+
```js
|
|
1009
|
+
await client.disconnect();
|
|
1010
|
+
```
|
|
1011
|
+
|
|
1012
|
+
## FAQ
|
|
1013
|
+
|
|
1014
|
+
### Lot's of connection issues and timeouts
|
|
1015
|
+
Things to try:
|
|
1016
|
+
- Remove all TwinCAT routes and create them again (yes, really)
|
|
1017
|
+
- Increase value of [`timeoutDelay`](TODO-DOC-URL-HERE/interfaces/AdsClientSettings.html#timeoutDelay) setting
|
|
1018
|
+
- Cache all data types and symbols straight after connecting using [`cacheDataTypes`](TODO-DOC-URL-HERE/classes/Client.html#cacheDataTypes) and [`cacheSymbols`](TODO-DOC-URL-HERE/classes/Client.html#cacheSymbols)
|
|
1019
|
+
|
|
1020
|
+
### Getting `TypeError: Do not know how to serialize a BigInt`
|
|
1021
|
+
- `JSON.stringify` doesn't understand BigInt values (such as `LINT` or similar 64 bit PLC values)
|
|
1022
|
+
- Check [this Github issue](https://github.com/GoogleChromeLabs/jsbi/issues/30#issuecomment-953187833) for a patch
|
|
1023
|
+
- `BigInt.prototype.toJSON = function() { return this.toString() }`
|
|
1024
|
+
|
|
1025
|
+
### Can I connect from Raspberry Pi to TwinCAT?
|
|
1026
|
+
|
|
1027
|
+
Yes, for example using [Setup 3 - Connect from any Node.js system](#setup-3---connect-from-any-nodejs-system).
|
|
1028
|
+
1. Open a TCP port 48898 from your PLC
|
|
1029
|
+
2. Edit `StaticRoutes.xml` file from your PLC
|
|
1030
|
+
3. Connect from the Raspberry Pi using the PLC IP address as `routerAddress` and the AmsNetID written to `StaticRoutes.xml` as `localAmsNetId`
|
|
1031
|
+
|
|
1032
|
+
### Receiving ADS error 1808 `Symbol not found` even when it should be found
|
|
1033
|
+
|
|
1034
|
+
- Make sure you have updated the latest PLC software using *download*. Sometimes online change causes this.
|
|
1035
|
+
- If you are using TwinCAT 2, see chapter [Differences when using with TwinCAT 2](#differences-when-using-with-twincat-2)
|
|
1036
|
+
|
|
1037
|
+
### Having timeouts or 'mailbox is full' errors
|
|
1038
|
+
|
|
1039
|
+
- The AMS router is capable of handling only limited number of requests in a certain time.
|
|
1040
|
+
- Other possible reason is that operating system TCP window is full because of large number of requests.
|
|
1041
|
+
- Solution:
|
|
1042
|
+
- Use structs or arrays to send data in larger packets
|
|
1043
|
+
- Try raw/multi commands to decrease data usage
|
|
1044
|
+
|
|
1045
|
+
### Having problems to connect from OSX or Raspberry Pi to target PLC
|
|
1046
|
+
|
|
1047
|
+
- You need to connect to the PLC AMS router instead
|
|
1048
|
+
- See [this issue comment](https://github.com/jisotalo/ads-client/issues/51#issuecomment-758016428)
|
|
1049
|
+
|
|
1050
|
+
### A data type is not found even when it should be
|
|
1051
|
+
|
|
1052
|
+
If you use methods like `convertFromRaw()` and `getDataType()` but receive an error similar to `ClientException: Finding data type *data type* failed`, make sure you have really written the data type correctly.
|
|
1053
|
+
|
|
1054
|
+
For example, when copying a variable name from TwinCAT online view using CTRL+C, it might not work:
|
|
1055
|
+
- Displayed name: `ARRAY [0..1, 0..1] OF ST_Example`
|
|
1056
|
+
- The value copied to clipboard `ARRAY [0..1, 0..1] OF ST_Example`
|
|
1057
|
+
- --> **This causes error!**
|
|
1058
|
+
- The real data type name that needs to be used is `ARRAY [0..1,0..1] OF ST_Example` (note no whitespace between array dimensions)
|
|
1059
|
+
|
|
1060
|
+
If you have problems, try to read the symbol object using `getSymbol()`. The final solution is to read all data types using `getDataTypes()` and manually finding the correct type.
|
|
1061
|
+
|
|
1062
|
+
### ClientException: Connection failed: Device system manager state read failed
|
|
1063
|
+
|
|
1064
|
+
- The `targetAmsNetId` didn't contain a system manager service (port `10000`)
|
|
1065
|
+
- The target is not a PLC and `rawClient` setting is not set
|
|
1066
|
+
- Solution:
|
|
1067
|
+
- Double-check connection settings
|
|
1068
|
+
- [Use `rawClient` setting ](#connecting-to-targets-without-a-plc-runtime)
|
|
1069
|
+
|
|
1070
|
+
### Connection failed (error EADDRNOTAVAIL)
|
|
1071
|
+
This could happen if you have manually provided `localAddress` or `localTcpPort` that don't exist.
|
|
1072
|
+
For example, setting `localAddress` to `192.168.10.1` when the computer has only ethernet interface with IP `192.168.1.1`.
|
|
1073
|
+
|
|
1074
|
+
See also https://github.com/jisotalo/ads-client/issues/82
|
|
1075
|
+
|
|
1076
|
+
### Problems running ads-client with docker
|
|
1077
|
+
|
|
1078
|
+
- EADDRNOTAVAIL: See above and https://github.com/jisotalo/ads-client/issues/82
|
|
1079
|
+
|
|
1080
|
+
### How to connect to PLC that is in CONFIG mode?
|
|
1081
|
+
As default, the ads-client checks if the target has PLC runtime at given port. However, when target system manager is at config mode, there is none. The client will throw an error during connecting:
|
|
1082
|
+
|
|
1083
|
+
`Connection failed - failed to set PLC connection. If target is not PLC runtime, use setting "rawClient". If system is in config mode or there is no PLC software yet, you might want to use setting "allowHalfOpen"`
|
|
1084
|
+
|
|
1085
|
+
You can disable the check by providing setting `allowHalfOpen: true`. After that, it's possible to start the PLC by `setTcSystemToRun()`.
|
|
1086
|
+
|
|
1087
|
+
Another option is to use setting `rawClient: true` - see [Connecting to targets without a PLC runtime](#connecting-to-targets-without-a-plc-runtime).
|
|
1088
|
+
|
|
1089
|
+
### Issues with TwinCAT 2 low-end devices (BK9050, BC9050 etc.)
|
|
1090
|
+
* You can only use raw commands (such as `readRaw()`, `writeRaw()`, `subscribeRaw()`) as these devices provide no symbols
|
|
1091
|
+
* See [issue 114](https://github.com/jisotalo/ads-client/issues/114) and [issue 116](https://github.com/jisotalo/ads-client/issues/116)
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
|
|
1095
|
+
## External links
|
|
1096
|
+
|
|
1097
|
+
README in progress.
|
|
100
1098
|
|
|
101
1099
|
## Library testing
|
|
102
1100
|
|
|
103
|
-
|
|
1101
|
+
All features of this library are tested using quite large test suite - see `./test/` directory.
|
|
1102
|
+
This prevents regression, thus updating the ads-client should be always safe.
|
|
1103
|
+
|
|
1104
|
+
There are separate tests for TwinCAT 2 and TwinCAT 3.
|
|
1105
|
+
|
|
1106
|
+
PLC projects for running test suites are located in the following repository:
|
|
1107
|
+
[https://github.com/jisotalo/ads-client-test-plc-project/tree/v2-dev](https://github.com/jisotalo/ads-client-test-plc-project/tree/v2-dev).
|
|
1108
|
+
|
|
1109
|
+
### TwinCAT 3 tests
|
|
1110
|
+
|
|
1111
|
+
Tests are run with command `npm run test-tc3`. TwinCAT 3 test PLC projects needs to be running in the target system.
|
|
1112
|
+
|
|
1113
|
+
**Results 28.09.2024:**
|
|
1114
|
+
|
|
1115
|
+
<details>
|
|
1116
|
+
<summary>Click to show test results</summary>
|
|
1117
|
+
<pre>
|
|
1118
|
+
PASS test/TC3/ads-client.test.js (24.801 s)
|
|
1119
|
+
√ IMPORTANT NOTE: This test requires running a specific TwinCAT 3 PLC project (https://github.com/jisotalo/ads-client-test-plc-project) (1 ms)
|
|
1120
|
+
connection
|
|
1121
|
+
√ client is not connected at beginning (1 ms)
|
|
1122
|
+
√ checking ads client settings (1 ms)
|
|
1123
|
+
√ connecting to the target (41 ms)
|
|
1124
|
+
√ checking that test PLC project is active (13 ms)
|
|
1125
|
+
√ checking that test PLC project version is correct (10 ms)
|
|
1126
|
+
√ checking 32/64 bitness (4 ms)
|
|
1127
|
+
√ caching of symbols and data types
|
|
1128
|
+
√ reconnecting (34 ms)
|
|
1129
|
+
resetting PLC to original state
|
|
1130
|
+
√ resetting PLC (514 ms)
|
|
1131
|
+
√ checking that reset was successful (8 ms)
|
|
1132
|
+
√ checking that PLC is not running (11 ms)
|
|
1133
|
+
√ setting IsReset to false (6 ms)
|
|
1134
|
+
√ starting PLC (7 ms)
|
|
1135
|
+
√ checking that test PLC project is running (505 ms)
|
|
1136
|
+
testing PLC runtime stop, start, restart
|
|
1137
|
+
√ stopping PLC (15 ms)
|
|
1138
|
+
√ starting PLC (14 ms)
|
|
1139
|
+
√ restarting PLC (528 ms)
|
|
1140
|
+
system state, PLC runtime states and device information
|
|
1141
|
+
√ reading TwinCAT system state (5 ms)
|
|
1142
|
+
√ reading PLC runtime (port 851) state (3 ms)
|
|
1143
|
+
√ reading PLC runtime (port 852) state (3 ms)
|
|
1144
|
+
√ reading PLC runtime device info (3 ms)
|
|
1145
|
+
√ reading TwinCAT system device info (5 ms)
|
|
1146
|
+
√ reading PLC runtime symbol version (4 ms)
|
|
1147
|
+
symbols and data types
|
|
1148
|
+
√ reading upload info (4 ms)
|
|
1149
|
+
√ reading all symbols (16 ms)
|
|
1150
|
+
√ reading single symbol information (1 ms)
|
|
1151
|
+
√ reading all data type information (19 ms)
|
|
1152
|
+
√ reading single data type information (2 ms)
|
|
1153
|
+
data conversion
|
|
1154
|
+
√ converting a raw PLC value to a Javascript variable (4 ms)
|
|
1155
|
+
√ converting a Javascript value to a raw PLC value (40 ms)
|
|
1156
|
+
reading values
|
|
1157
|
+
reading standard values
|
|
1158
|
+
√ reading BOOL (16 ms)
|
|
1159
|
+
√ reading BYTE (8 ms)
|
|
1160
|
+
√ reading WORD (9 ms)
|
|
1161
|
+
√ reading DWORD (7 ms)
|
|
1162
|
+
√ reading SINT (15 ms)
|
|
1163
|
+
√ reading USINT (7 ms)
|
|
1164
|
+
√ reading INT (14 ms)
|
|
1165
|
+
√ reading UINT (7 ms)
|
|
1166
|
+
√ reading DINT (13 ms)
|
|
1167
|
+
√ reading UDINT (7 ms)
|
|
1168
|
+
√ reading REAL (31 ms)
|
|
1169
|
+
√ reading STRING (16 ms)
|
|
1170
|
+
√ reading DATE (7 ms)
|
|
1171
|
+
√ reading DT (14 ms)
|
|
1172
|
+
√ reading TOD (16 ms)
|
|
1173
|
+
√ reading TIME (8 ms)
|
|
1174
|
+
√ reading LWORD (8 ms)
|
|
1175
|
+
√ reading LINT (13 ms)
|
|
1176
|
+
√ reading ULINT (7 ms)
|
|
1177
|
+
√ reading LREAL (31 ms)
|
|
1178
|
+
√ reading WSTRING (16 ms)
|
|
1179
|
+
√ reading LDATE (---- TODO: Needs TC 3.1.4026 ----)
|
|
1180
|
+
√ reading LDT (---- TODO: Needs TC 3.1.4026 ----) (1 ms)
|
|
1181
|
+
√ reading LTOD (---- TODO: Needs TC 3.1.4026 ----)
|
|
1182
|
+
√ reading LTIME (9 ms)
|
|
1183
|
+
reading standard array values
|
|
1184
|
+
√ reading ARRAY OF BOOL (14 ms)
|
|
1185
|
+
√ reading ARRAY OF BYTE (8 ms)
|
|
1186
|
+
√ reading ARRAY OF WORD (9 ms)
|
|
1187
|
+
√ reading ARRAY OF DWORD (8 ms)
|
|
1188
|
+
√ reading ARRAY OF SINT (13 ms)
|
|
1189
|
+
√ reading ARRAY OF USINT (7 ms)
|
|
1190
|
+
√ reading ARRAY OF INT (13 ms)
|
|
1191
|
+
√ reading ARRAY OF UINT (8 ms)
|
|
1192
|
+
√ reading ARRAY OF DINT (16 ms)
|
|
1193
|
+
√ reading ARRAY OF UDINT (7 ms)
|
|
1194
|
+
√ reading ARRAY OF REAL (31 ms)
|
|
1195
|
+
√ reading ARRAY OF STRING (15 ms)
|
|
1196
|
+
√ reading ARRAY OF DATE (8 ms)
|
|
1197
|
+
√ reading ARRAY OF DT (15 ms)
|
|
1198
|
+
√ reading ARRAY OF TOD (14 ms)
|
|
1199
|
+
√ reading ARRAY OF TIME (5 ms)
|
|
1200
|
+
√ reading ARRAY OF LWORD (8 ms)
|
|
1201
|
+
√ reading ARRAY OF LINT (16 ms)
|
|
1202
|
+
√ reading ARRAY OF ULINT (7 ms)
|
|
1203
|
+
√ reading ARRAY OF LREAL (26 ms)
|
|
1204
|
+
√ reading ARRAY OF WSTRING (13 ms)
|
|
1205
|
+
√ reading ARRAY OF LDATE (---- TODO: Needs TC 3.1.4026 ----)
|
|
1206
|
+
√ reading ARRAY OF LDT (---- TODO: Needs TC 3.1.4026 ----)
|
|
1207
|
+
√ reading ARRAY OF LTOD (---- TODO: Needs TC 3.1.4026 ----)
|
|
1208
|
+
√ reading ARRAY OF LTIME (6 ms)
|
|
1209
|
+
reading complex values
|
|
1210
|
+
√ reading STRUCT (14 ms)
|
|
1211
|
+
√ reading ALIAS (7 ms)
|
|
1212
|
+
√ reading ENUM (44 ms)
|
|
1213
|
+
√ reading POINTER (address) (8 ms)
|
|
1214
|
+
√ reading SUBRANGE (8 ms)
|
|
1215
|
+
√ reading UNION (22 ms)
|
|
1216
|
+
√ reading FUNCTION_BLOCK (28 ms)
|
|
1217
|
+
√ reading INTERFACE (8 ms)
|
|
1218
|
+
reading complex array values
|
|
1219
|
+
√ reading ARRAY OF STRUCT (19 ms)
|
|
1220
|
+
√ reading ARRAY OF ALIAS (9 ms)
|
|
1221
|
+
√ reading ARRAY OF ENUM (38 ms)
|
|
1222
|
+
√ reading ARRAY OF POINTER (address) (6 ms)
|
|
1223
|
+
√ reading ARRAY OF SUBRANGE (6 ms)
|
|
1224
|
+
√ reading ARRAY OF UNION (8 ms)
|
|
1225
|
+
√ reading ARRAY OF FUNCTION_BLOCK (27 ms)
|
|
1226
|
+
√ reading ARRAY OF INTERFACE (6 ms)
|
|
1227
|
+
reading special types / cases
|
|
1228
|
+
√ reading ARRAY with negative index (9 ms)
|
|
1229
|
+
√ reading multi-dimensional ARRAY (9 ms)
|
|
1230
|
+
√ reading ARRAY OF ARRAY (7 ms)
|
|
1231
|
+
√ reading STRUCT with pragma: {attribute 'pack_mode' := '1'} (8 ms)
|
|
1232
|
+
√ reading STRUCT with pragma: {attribute 'pack_mode' := '8'} (8 ms)
|
|
1233
|
+
√ reading an empty FUNCTION_BLOCK (7 ms)
|
|
1234
|
+
√ reading an empty STRUCT (8 ms)
|
|
1235
|
+
√ reading an empty ARRAY (9 ms)
|
|
1236
|
+
√ reading a single BIT (15 ms)
|
|
1237
|
+
√ reading a struct with BIT types (8 ms)
|
|
1238
|
+
reading dereferenced POINTER and REFERENCE values
|
|
1239
|
+
√ reading POINTER (value) (8 ms)
|
|
1240
|
+
√ reading REFERENCE (value) (5 ms)
|
|
1241
|
+
reading raw data
|
|
1242
|
+
√ reading a raw value (4 ms)
|
|
1243
|
+
√ reading a raw value using symbol (3 ms)
|
|
1244
|
+
√ reading a raw value using path (4 ms)
|
|
1245
|
+
√ reading multiple raw values (multi/sum command) (5 ms)
|
|
1246
|
+
reading (misc)
|
|
1247
|
+
√ reading a value using symbol (3 ms)
|
|
1248
|
+
writing values
|
|
1249
|
+
writing standard values
|
|
1250
|
+
√ writing BOOL (22 ms)
|
|
1251
|
+
√ writing BYTE (12 ms)
|
|
1252
|
+
√ writing WORD (12 ms)
|
|
1253
|
+
√ writing DWORD (12 ms)
|
|
1254
|
+
√ writing SINT (24 ms)
|
|
1255
|
+
√ writing USINT (10 ms)
|
|
1256
|
+
√ writing INT (23 ms)
|
|
1257
|
+
√ writing UINT (11 ms)
|
|
1258
|
+
√ writing DINT (25 ms)
|
|
1259
|
+
√ writing UDINT (11 ms)
|
|
1260
|
+
√ writing REAL (46 ms)
|
|
1261
|
+
√ writing STRING (21 ms)
|
|
1262
|
+
√ writing DATE (11 ms)
|
|
1263
|
+
√ writing DT (22 ms)
|
|
1264
|
+
√ writing TOD (22 ms)
|
|
1265
|
+
√ writing TIME (11 ms)
|
|
1266
|
+
√ writing LWORD (7 ms)
|
|
1267
|
+
√ writing LINT (24 ms)
|
|
1268
|
+
√ writing ULINT (9 ms)
|
|
1269
|
+
√ writing LREAL (44 ms)
|
|
1270
|
+
√ writing WSTRING (22 ms)
|
|
1271
|
+
√ writing LDATE (---- TODO: Needs TC 3.1.4026 ----)
|
|
1272
|
+
√ writing LDT (---- TODO: Needs TC 3.1.4026 ----) (1 ms)
|
|
1273
|
+
√ writing LTOD (---- TODO: Needs TC 3.1.4026 ----)
|
|
1274
|
+
√ writing LTIME (7 ms)
|
|
1275
|
+
writing standard array values
|
|
1276
|
+
√ writing ARRAY OF BOOL (24 ms)
|
|
1277
|
+
√ writing ARRAY OF BYTE (13 ms)
|
|
1278
|
+
√ writing ARRAY OF WORD (12 ms)
|
|
1279
|
+
√ writing ARRAY OF DWORD (13 ms)
|
|
1280
|
+
√ writing ARRAY OF SINT (22 ms)
|
|
1281
|
+
√ writing ARRAY OF USINT (12 ms)
|
|
1282
|
+
√ writing ARRAY OF INT (19 ms)
|
|
1283
|
+
√ writing ARRAY OF UINT (11 ms)
|
|
1284
|
+
√ writing ARRAY OF DINT (22 ms)
|
|
1285
|
+
√ writing ARRAY OF UDINT (12 ms)
|
|
1286
|
+
√ writing ARRAY OF REAL (44 ms)
|
|
1287
|
+
√ writing ARRAY OF STRING (23 ms)
|
|
1288
|
+
√ writing ARRAY OF DATE (11 ms)
|
|
1289
|
+
√ writing ARRAY OF DT (20 ms)
|
|
1290
|
+
√ writing ARRAY OF TOD (22 ms)
|
|
1291
|
+
√ writing ARRAY OF TIME (11 ms)
|
|
1292
|
+
√ writing ARRAY OF LWORD (12 ms)
|
|
1293
|
+
√ writing ARRAY OF LINT (21 ms)
|
|
1294
|
+
√ writing ARRAY OF ULINT (12 ms)
|
|
1295
|
+
√ writing ARRAY OF LREAL (46 ms)
|
|
1296
|
+
√ writing ARRAY OF WSTRING (22 ms)
|
|
1297
|
+
√ writing ARRAY OF LDATE (---- TODO: Needs TC 3.1.4026 ----) (1 ms)
|
|
1298
|
+
√ writing ARRAY OF LDT (---- TODO: Needs TC 3.1.4026 ----) (1 ms)
|
|
1299
|
+
√ writing ARRAY OF LTOD (---- TODO: Needs TC 3.1.4026 ----)
|
|
1300
|
+
√ writing ARRAY OF LTIME (10 ms)
|
|
1301
|
+
writing complex values
|
|
1302
|
+
√ writing STRUCT (26 ms)
|
|
1303
|
+
√ writing ALIAS (11 ms)
|
|
1304
|
+
√ writing ENUM (49 ms)
|
|
1305
|
+
√ writing POINTER (address) (14 ms)
|
|
1306
|
+
√ writing SUBRANGE (17 ms)
|
|
1307
|
+
√ writing UNION (46 ms)
|
|
1308
|
+
√ writing FUNCTION_BLOCK (46 ms)
|
|
1309
|
+
√ writing INTERFACE (13 ms)
|
|
1310
|
+
writing complex array values
|
|
1311
|
+
√ writing ARRAY OF STRUCT (24 ms)
|
|
1312
|
+
√ writing ARRAY OF ALIAS (10 ms)
|
|
1313
|
+
√ writing ARRAY OF ENUM (57 ms)
|
|
1314
|
+
√ writing ARRAY OF POINTER (address) (12 ms)
|
|
1315
|
+
√ writing ARRAY OF SUBRANGE (11 ms)
|
|
1316
|
+
√ writing ARRAY OF UNION (16 ms)
|
|
1317
|
+
√ writing ARRAY OF FUNCTION_BLOCK (48 ms)
|
|
1318
|
+
√ writing ARRAY OF INTERFACE (16 ms)
|
|
1319
|
+
writing special types / cases
|
|
1320
|
+
√ writing ARRAY with negative index (14 ms)
|
|
1321
|
+
√ writing multi-dimensional ARRAY (15 ms)
|
|
1322
|
+
√ writing ARRAY OF ARRAY (16 ms)
|
|
1323
|
+
√ writing STRUCT with pragma: {attribute 'pack_mode' := '1'} (14 ms)
|
|
1324
|
+
√ writing STRUCT with pragma: {attribute 'pack_mode' := '8'} (16 ms)
|
|
1325
|
+
√ writing an empty FUNCTION_BLOCK (8 ms)
|
|
1326
|
+
√ writing an empty STRUCT (7 ms)
|
|
1327
|
+
√ writing an empty ARRAY (7 ms)
|
|
1328
|
+
√ writing a single BIT (36 ms)
|
|
1329
|
+
√ writing a struct with BIT types (11 ms)
|
|
1330
|
+
writing dereferenced POINTER and REFERENCE values
|
|
1331
|
+
√ writing POINTER (value) (22 ms)
|
|
1332
|
+
√ writing REFERENCE (value) (23 ms)
|
|
1333
|
+
writing raw data
|
|
1334
|
+
√ writing a raw value (6 ms)
|
|
1335
|
+
√ writing a raw value using symbol (7 ms)
|
|
1336
|
+
√ writing a raw value using path (11 ms)
|
|
1337
|
+
√ writing multiple raw values (multi/sum command) (20 ms)
|
|
1338
|
+
writing (misc)
|
|
1339
|
+
√ writing a value using symbol (8 ms)
|
|
1340
|
+
variable handles
|
|
1341
|
+
√ creating and deleting a varible handle (14 ms)
|
|
1342
|
+
√ reading value using a variable handle (12 ms)
|
|
1343
|
+
√ writing value using a variable handle (30 ms)
|
|
1344
|
+
√ creating and deleting multiple varible handles (multi/sum command) (9 ms)
|
|
1345
|
+
subscriptions (ADS notifications)
|
|
1346
|
+
√ subscribing and unsubscribing successfully (2033 ms)
|
|
1347
|
+
√ subscribing to a changing value (10 ms) with default cycle time (3023 ms)
|
|
1348
|
+
√ subscribing to a changing value (10 ms) with 10 ms cycle time (28 ms)
|
|
1349
|
+
√ subscribing to a constant value with maximum delay of 2000 ms (2016 ms)
|
|
1350
|
+
√ subscribing to a raw ADS address (222 ms)
|
|
1351
|
+
√ subscribing using subscribeValue() (2030 ms)
|
|
1352
|
+
√ subscribing to a raw ADS address using subscribeRaw() (219 ms)
|
|
1353
|
+
remote procedure calls (RPC methods)
|
|
1354
|
+
√ calling a RPC method (13 ms)
|
|
1355
|
+
√ calling a RPC method with struct parameters (9 ms)
|
|
1356
|
+
√ calling a RPC method without return value and without parameters (11 ms)
|
|
1357
|
+
miscellaneous
|
|
1358
|
+
√ sending read write ADS command (6 ms)
|
|
1359
|
+
√ sending multiple read write ADS commands (multi/sum command) (12 ms)
|
|
1360
|
+
issue specific tests
|
|
1361
|
+
issue 103 (https://github.com/jisotalo/ads-client/issues/103)
|
|
1362
|
+
√ calling unsubscribeAll() multiple times (should not crash to unhandled exception) (50 ms)
|
|
1363
|
+
disconnecting
|
|
1364
|
+
√ disconnecting client (9 ms)
|
|
1365
|
+
controlling TwinCAT system service
|
|
1366
|
+
√ connecting (1 ms)
|
|
1367
|
+
√ setting TwinCAT system to config (4024 ms)
|
|
1368
|
+
√ setting TwinCAT system to run (4018 ms)
|
|
1369
|
+
√ disconnecting (2 ms)
|
|
1370
|
+
handling unknown/stale ADS notifications
|
|
1371
|
+
√ connecting (27 ms)
|
|
1372
|
+
√ creating an unknown notification handle by forced disconnecting (1040 ms)
|
|
1373
|
+
√ deleting an unknown notification handle automatically (1034 ms)
|
|
1374
|
+
√ disconnecting (1 ms)
|
|
1375
|
+
|
|
1376
|
+
Test Suites: 1 passed, 1 total
|
|
1377
|
+
Tests: 223 passed, 223 total
|
|
1378
|
+
Snapshots: 0 total
|
|
1379
|
+
Time: 24.889 s, estimated 25 s
|
|
1380
|
+
Ran all test suites matching /TC3\\ads-client.test.js/i.
|
|
1381
|
+
</pre>
|
|
1382
|
+
</details>
|
|
1383
|
+
|
|
1384
|
+
### TwinCAT 2 tests
|
|
1385
|
+
|
|
1386
|
+
Tests are run with command `npm run test-tc2`. TwinCAT 2 test PLC projects needs to be running in the target system.
|
|
1387
|
+
|
|
1388
|
+
TwinCAT 2 tests only have features that are supported by TC2.
|
|
104
1389
|
|
|
105
|
-
|
|
1390
|
+
**Results 28.09.2024:**
|
|
106
1391
|
|
|
107
1392
|
<details>
|
|
108
1393
|
<summary>Click to show test results</summary>
|
|
109
1394
|
<pre>
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
data
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
reading
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
writing
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
√
|
|
278
|
-
√
|
|
279
|
-
√ writing
|
|
280
|
-
√
|
|
281
|
-
|
|
282
|
-
√
|
|
283
|
-
√
|
|
284
|
-
√
|
|
285
|
-
√
|
|
286
|
-
√
|
|
287
|
-
√
|
|
288
|
-
√
|
|
289
|
-
|
|
290
|
-
√
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
√
|
|
297
|
-
|
|
298
|
-
√
|
|
299
|
-
√
|
|
300
|
-
|
|
301
|
-
√
|
|
302
|
-
|
|
303
|
-
√
|
|
304
|
-
√
|
|
305
|
-
√
|
|
306
|
-
√
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
√ writing STRUCT with pragma: {attribute 'pack_mode' := '1'} (17 ms)
|
|
314
|
-
√ writing STRUCT with pragma: {attribute 'pack_mode' := '8'} (12 ms)
|
|
315
|
-
√ writing an empty FUNCTION_BLOCK (8 ms)
|
|
316
|
-
√ writing an empty STRUCT (8 ms)
|
|
317
|
-
√ writing an empty ARRAY (6 ms)
|
|
318
|
-
√ writing a single BIT (40 ms)
|
|
319
|
-
√ writing a struct with BIT types (15 ms)
|
|
320
|
-
writing dereferenced POINTER and REFERENCE values
|
|
321
|
-
√ writing POINTER (value) (23 ms)
|
|
322
|
-
√ writing REFERENCE (value) (21 ms)
|
|
323
|
-
writing raw data
|
|
324
|
-
√ writing a raw value (7 ms)
|
|
325
|
-
√ writing a raw value using symbol (4 ms)
|
|
326
|
-
√ writing a raw value using path (12 ms)
|
|
327
|
-
√ writing multiple raw values (multi/sum command) (20 ms)
|
|
328
|
-
writing (misc)
|
|
329
|
-
√ writing a value using symbol (4 ms)
|
|
330
|
-
variable handles
|
|
331
|
-
√ creating and deleting a varible handle (16 ms)
|
|
332
|
-
√ reading value using a variable handle (12 ms)
|
|
333
|
-
√ writing value using a variable handle (32 ms)
|
|
334
|
-
√ creating and deleting multiple varible handles (multi/sum command) (8 ms)
|
|
335
|
-
subscriptions (ADS notifications)
|
|
336
|
-
√ subscribing and unsubscribing successfully (2052 ms)
|
|
337
|
-
√ subscribing to a changing value (10 ms) with default cycle time (3031 ms)
|
|
338
|
-
√ subscribing to a changing value (10 ms) with 10 ms cycle time (36 ms)
|
|
339
|
-
√ subscribing to a constant value with maximum delay of 2000 ms (2018 ms)
|
|
340
|
-
√ subscribing to a raw ADS address (227 ms)
|
|
341
|
-
√ subscribing using subscribeValue() (2023 ms)
|
|
342
|
-
√ subscribing to a raw ADS address using subscribeRaw() (217 ms)
|
|
343
|
-
remote procedure calls (RPC methods)
|
|
344
|
-
√ calling a RPC method (13 ms)
|
|
345
|
-
√ calling a RPC method with struct parameters (11 ms)
|
|
346
|
-
√ calling a RPC method without return value and without parameters (9 ms)
|
|
347
|
-
miscellaneous
|
|
348
|
-
√ sending read write ADS command (9 ms)
|
|
349
|
-
√ sending multiple read write ADS commands (multi/sum command) (9 ms)
|
|
350
|
-
issue specific tests
|
|
351
|
-
issue 103 (https://github.com/jisotalo/ads-client/issues/103)
|
|
352
|
-
√ calling unsubscribeAll() multiple times (should not crash to unhandled exception) (51 ms)
|
|
353
|
-
disconnecting
|
|
354
|
-
√ disconnecting client (8 ms)
|
|
355
|
-
controlling TwinCAT system service
|
|
356
|
-
√ connecting (2 ms)
|
|
357
|
-
√ setting TwinCAT system to config (2021 ms)
|
|
358
|
-
√ setting TwinCAT system to run (2022 ms)
|
|
359
|
-
√ disconnecting (1 ms)
|
|
360
|
-
handling unknown/stale ADS notifications
|
|
361
|
-
√ connecting (29 ms)
|
|
362
|
-
√ creating an unknown notification handle by forced disconnecting (1037 ms)
|
|
363
|
-
√ deleting an unknown notification handle automatically (1028 ms)
|
|
364
|
-
√ disconnecting (1 ms)
|
|
1395
|
+
PASS test/TC2/ads-client.test.js (26.971 s)
|
|
1396
|
+
√ IMPORTANT NOTE: This test requires running a specific TwinCAT 2 PLC project (https://github.com/jisotalo/ads-client-test-plc-project)
|
|
1397
|
+
connection
|
|
1398
|
+
√ client is not connected at beginning
|
|
1399
|
+
√ checking ads client settings
|
|
1400
|
+
√ connecting to the target (35 ms)
|
|
1401
|
+
√ checking that test PLC project is active (58 ms)
|
|
1402
|
+
√ checking that test PLC project version is correct (9 ms)
|
|
1403
|
+
√ checking 32/64 bitness (2 ms)
|
|
1404
|
+
√ caching of symbols and data types (1 ms)
|
|
1405
|
+
√ reconnecting (22 ms)
|
|
1406
|
+
resetting PLC to original state
|
|
1407
|
+
√ resetting PLC (507 ms)
|
|
1408
|
+
√ checking that reset was successful (5 ms)
|
|
1409
|
+
√ checking that PLC is not running (8 ms)
|
|
1410
|
+
√ setting IsReset to false (3 ms)
|
|
1411
|
+
√ starting PLC (5 ms)
|
|
1412
|
+
√ checking that test PLC project is running (506 ms)
|
|
1413
|
+
testing PLC runtime stop, start, restart
|
|
1414
|
+
√ stopping PLC (13 ms)
|
|
1415
|
+
√ starting PLC (10 ms)
|
|
1416
|
+
√ restarting PLC (523 ms)
|
|
1417
|
+
system state, PLC runtime states and device information
|
|
1418
|
+
√ reading TwinCAT system state (2 ms)
|
|
1419
|
+
√ reading PLC runtime (port 801) state (2 ms)
|
|
1420
|
+
√ reading PLC runtime device info (2 ms)
|
|
1421
|
+
√ reading TwinCAT system device info (2 ms)
|
|
1422
|
+
√ reading PLC runtime symbol version (2 ms)
|
|
1423
|
+
symbols and data types
|
|
1424
|
+
√ reading upload info (2 ms)
|
|
1425
|
+
√ reading all symbols (15 ms)
|
|
1426
|
+
√ reading single symbol information
|
|
1427
|
+
√ reading all data type information (33 ms)
|
|
1428
|
+
√ reading single data type information (9 ms)
|
|
1429
|
+
data conversion
|
|
1430
|
+
√ converting a raw PLC value to a Javascript variable (7 ms)
|
|
1431
|
+
√ converting a Javascript value to a raw PLC value (41 ms)
|
|
1432
|
+
reading values
|
|
1433
|
+
reading standard values
|
|
1434
|
+
√ reading BOOL (12 ms)
|
|
1435
|
+
√ reading BYTE (6 ms)
|
|
1436
|
+
√ reading WORD (6 ms)
|
|
1437
|
+
√ reading DWORD (7 ms)
|
|
1438
|
+
√ reading SINT (11 ms)
|
|
1439
|
+
√ reading USINT (7 ms)
|
|
1440
|
+
√ reading INT (11 ms)
|
|
1441
|
+
√ reading UINT (7 ms)
|
|
1442
|
+
√ reading DINT (13 ms)
|
|
1443
|
+
√ reading UDINT (6 ms)
|
|
1444
|
+
√ reading REAL (19 ms)
|
|
1445
|
+
√ reading STRING (10 ms)
|
|
1446
|
+
√ reading DATE (4 ms)
|
|
1447
|
+
√ reading DT (9 ms)
|
|
1448
|
+
√ reading TOD (8 ms)
|
|
1449
|
+
√ reading TIME (6 ms)
|
|
1450
|
+
√ reading LREAL (26 ms)
|
|
1451
|
+
reading standard array values
|
|
1452
|
+
√ reading ARRAY OF BOOL (9 ms)
|
|
1453
|
+
√ reading ARRAY OF BYTE (5 ms)
|
|
1454
|
+
√ reading ARRAY OF WORD (5 ms)
|
|
1455
|
+
√ reading ARRAY OF DWORD (4 ms)
|
|
1456
|
+
√ reading ARRAY OF SINT (10 ms)
|
|
1457
|
+
√ reading ARRAY OF USINT (6 ms)
|
|
1458
|
+
√ reading ARRAY OF INT (9 ms)
|
|
1459
|
+
√ reading ARRAY OF UINT (6 ms)
|
|
1460
|
+
√ reading ARRAY OF DINT (10 ms)
|
|
1461
|
+
√ reading ARRAY OF UDINT (3 ms)
|
|
1462
|
+
√ reading ARRAY OF REAL (18 ms)
|
|
1463
|
+
√ reading ARRAY OF STRING (9 ms)
|
|
1464
|
+
√ reading ARRAY OF DATE (7 ms)
|
|
1465
|
+
√ reading ARRAY OF DT (11 ms)
|
|
1466
|
+
√ reading ARRAY OF TOD (9 ms)
|
|
1467
|
+
√ reading ARRAY OF TIME (4 ms)
|
|
1468
|
+
√ reading ARRAY OF LREAL (24 ms)
|
|
1469
|
+
reading complex values
|
|
1470
|
+
√ reading STRUCT (15 ms)
|
|
1471
|
+
√ reading ALIAS (8 ms)
|
|
1472
|
+
√ reading ENUM (16 ms)
|
|
1473
|
+
√ reading POINTER (address) (6 ms)
|
|
1474
|
+
√ reading SUBRANGE (6 ms)
|
|
1475
|
+
√ reading FUNCTION_BLOCK (23 ms)
|
|
1476
|
+
reading complex array values
|
|
1477
|
+
√ reading ARRAY OF STRUCT (12 ms)
|
|
1478
|
+
√ reading ARRAY OF ALIAS (6 ms)
|
|
1479
|
+
√ reading ARRAY OF ENUM (20 ms)
|
|
1480
|
+
√ reading ARRAY OF POINTER (address) (5 ms)
|
|
1481
|
+
√ reading ARRAY OF SUBRANGE (3 ms)
|
|
1482
|
+
√ reading ARRAY OF FUNCTION_BLOCK (22 ms)
|
|
1483
|
+
reading special types / cases
|
|
1484
|
+
√ reading ARRAY with negative index (8 ms)
|
|
1485
|
+
√ reading multi-dimensional ARRAY (5 ms)
|
|
1486
|
+
√ reading ARRAY OF ARRAY (5 ms)
|
|
1487
|
+
reading dereferenced POINTER values
|
|
1488
|
+
√ reading POINTER (value) (5 ms)
|
|
1489
|
+
reading raw data
|
|
1490
|
+
√ reading a raw value (3 ms)
|
|
1491
|
+
√ reading a raw value using symbol (2 ms)
|
|
1492
|
+
√ reading a raw value using path (2 ms)
|
|
1493
|
+
√ reading multiple raw values (multi/sum command) (4 ms)
|
|
1494
|
+
reading (misc)
|
|
1495
|
+
√ reading a value using symbol (3 ms)
|
|
1496
|
+
writing values
|
|
1497
|
+
writing standard values
|
|
1498
|
+
√ writing BOOL (22 ms)
|
|
1499
|
+
√ writing BYTE (10 ms)
|
|
1500
|
+
√ writing WORD (8 ms)
|
|
1501
|
+
√ writing DWORD (10 ms)
|
|
1502
|
+
√ writing SINT (18 ms)
|
|
1503
|
+
√ writing USINT (6 ms)
|
|
1504
|
+
√ writing INT (16 ms)
|
|
1505
|
+
√ writing UINT (8 ms)
|
|
1506
|
+
√ writing DINT (13 ms)
|
|
1507
|
+
√ writing UDINT (7 ms)
|
|
1508
|
+
√ writing REAL (30 ms)
|
|
1509
|
+
√ writing STRING (14 ms)
|
|
1510
|
+
√ writing DATE (6 ms)
|
|
1511
|
+
√ writing DT (15 ms)
|
|
1512
|
+
√ writing TOD (15 ms)
|
|
1513
|
+
√ writing TIME (8 ms)
|
|
1514
|
+
√ writing LREAL (28 ms)
|
|
1515
|
+
writing standard array values
|
|
1516
|
+
√ writing ARRAY OF BOOL (14 ms)
|
|
1517
|
+
√ writing ARRAY OF BYTE (7 ms)
|
|
1518
|
+
√ writing ARRAY OF WORD (7 ms)
|
|
1519
|
+
√ writing ARRAY OF DWORD (9 ms)
|
|
1520
|
+
√ writing ARRAY OF SINT (13 ms)
|
|
1521
|
+
√ writing ARRAY OF USINT (7 ms)
|
|
1522
|
+
√ writing ARRAY OF INT (15 ms)
|
|
1523
|
+
√ writing ARRAY OF UINT (7 ms)
|
|
1524
|
+
√ writing ARRAY OF DINT (13 ms)
|
|
1525
|
+
√ writing ARRAY OF UDINT (9 ms)
|
|
1526
|
+
√ writing ARRAY OF REAL (25 ms)
|
|
1527
|
+
√ writing ARRAY OF STRING (17 ms)
|
|
1528
|
+
√ writing ARRAY OF DATE (7 ms)
|
|
1529
|
+
√ writing ARRAY OF DT (18 ms)
|
|
1530
|
+
√ writing ARRAY OF TOD (18 ms)
|
|
1531
|
+
√ writing ARRAY OF TIME (10 ms)
|
|
1532
|
+
√ writing ARRAY OF LREAL (35 ms)
|
|
1533
|
+
writing complex values
|
|
1534
|
+
√ writing STRUCT (19 ms)
|
|
1535
|
+
√ writing ALIAS (10 ms)
|
|
1536
|
+
√ writing ENUM (25 ms)
|
|
1537
|
+
√ writing POINTER (address) (13 ms)
|
|
1538
|
+
√ writing SUBRANGE (12 ms)
|
|
1539
|
+
√ writing FUNCTION_BLOCK (39 ms)
|
|
1540
|
+
writing complex array values
|
|
1541
|
+
√ writing ARRAY OF STRUCT (24 ms)
|
|
1542
|
+
√ writing ARRAY OF ALIAS (11 ms)
|
|
1543
|
+
√ writing ARRAY OF ENUM (28 ms)
|
|
1544
|
+
√ writing ARRAY OF POINTER (address) (14 ms)
|
|
1545
|
+
√ writing ARRAY OF SUBRANGE (11 ms)
|
|
1546
|
+
√ writing ARRAY OF FUNCTION_BLOCK (48 ms)
|
|
1547
|
+
writing special types / cases
|
|
1548
|
+
√ writing ARRAY with negative index (18 ms)
|
|
1549
|
+
√ writing multi-dimensional ARRAY (13 ms)
|
|
1550
|
+
√ writing ARRAY OF ARRAY (12 ms)
|
|
1551
|
+
√ writing an empty FUNCTION_BLOCK (7 ms)
|
|
1552
|
+
writing dereferenced POINTER and REFERENCE values
|
|
1553
|
+
√ writing POINTER (value) (15 ms)
|
|
1554
|
+
writing raw data
|
|
1555
|
+
√ writing a raw value (7 ms)
|
|
1556
|
+
√ writing a raw value using symbol (4 ms)
|
|
1557
|
+
√ writing a raw value using path (10 ms)
|
|
1558
|
+
√ writing multiple raw values (multi/sum command) (11 ms)
|
|
1559
|
+
writing (misc)
|
|
1560
|
+
√ writing a value using symbol (5 ms)
|
|
1561
|
+
variable handles
|
|
1562
|
+
√ creating and deleting a varible handle (12 ms)
|
|
1563
|
+
√ reading value using a variable handle (6 ms)
|
|
1564
|
+
√ writing value using a variable handle (19 ms)
|
|
1565
|
+
√ creating and deleting multiple varible handles (multi/sum command) (5 ms)
|
|
1566
|
+
subscriptions (ADS notifications)
|
|
1567
|
+
√ subscribing and unsubscribing successfully (2010 ms)
|
|
1568
|
+
√ subscribing to a changing value (10 ms) with default cycle time (3018 ms)
|
|
1569
|
+
√ subscribing to a changing value (10 ms) with 10 ms cycle time (30 ms)
|
|
1570
|
+
√ subscribing to a constant value with maximum delay of 2000 ms (2010 ms)
|
|
1571
|
+
√ subscribing to a raw ADS address (220 ms)
|
|
1572
|
+
√ subscribing using subscribeValue() (2010 ms)
|
|
1573
|
+
√ subscribing to a raw ADS address using subscribeRaw() (218 ms)
|
|
1574
|
+
miscellaneous
|
|
1575
|
+
√ sending read write ADS command (7 ms)
|
|
1576
|
+
√ sending multiple read write ADS commands (multi/sum command) (10 ms)
|
|
1577
|
+
issue specific tests
|
|
1578
|
+
issue 103 (https://github.com/jisotalo/ads-client/issues/103)
|
|
1579
|
+
√ calling unsubscribeAll() multiple times (should not crash to unhandled exception) (28 ms)
|
|
1580
|
+
disconnecting
|
|
1581
|
+
√ disconnecting client (6 ms)
|
|
1582
|
+
controlling TwinCAT system service
|
|
1583
|
+
√ connecting (2 ms)
|
|
1584
|
+
√ setting TwinCAT system to config (5248 ms)
|
|
1585
|
+
√ setting TwinCAT system to run (6303 ms)
|
|
1586
|
+
√ disconnecting (1 ms)
|
|
1587
|
+
handling unknown/stale ADS notifications
|
|
1588
|
+
√ connecting (23 ms)
|
|
1589
|
+
√ creating an unknown notification handle by forced disconnecting (1033 ms)
|
|
1590
|
+
√ deleting an unknown notification handle automatically (1021 ms)
|
|
1591
|
+
√ disconnecting (1 ms)
|
|
1592
|
+
|
|
1593
|
+
Test Suites: 1 passed, 1 total
|
|
1594
|
+
Tests: 164 passed, 164 total
|
|
1595
|
+
Snapshots: 0 total
|
|
1596
|
+
Time: 27.056 s
|
|
1597
|
+
Ran all test suites matching /TC2\\ads-client.test.js/i.
|
|
365
1598
|
</pre>
|
|
366
1599
|
</details>
|
|
367
1600
|
|