ads-client 2.0.0-beta.5 → 2.0.0-beta.6
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 +56 -2
- package/README.md +775 -896
- package/dist/ads-client.d.ts +18 -4
- package/dist/ads-client.js +63 -34
- package/dist/ads-client.js.map +1 -1
- package/dist/ads-commons.d.ts +7 -6
- package/dist/ads-commons.js +3 -2
- package/dist/ads-commons.js.map +1 -1
- package/dist/types/ads-protocol-types.d.ts +2 -2
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
# ads-client
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
[](https://www.npmjs.org/package/ads-client)
|
|
5
4
|
[](https://github.com/jisotalo/ads-client)
|
|
6
5
|
[](https://choosealicense.com/licenses/mit/)
|
|
7
6
|
|
|
8
|
-
|
|
9
7
|
Beckhoff TwinCAT ADS client library for Node.js (unofficial).
|
|
10
8
|
|
|
11
9
|
Connect to a Beckhoff TwinCAT automation system using the ADS protocol from a Node.js app.
|
|
@@ -14,17 +12,18 @@ If you are using Node-RED, check out the [node-red-contrib-ads-client](https://w
|
|
|
14
12
|
|
|
15
13
|
# Project status
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
Version 2 is almost ready!
|
|
15
|
+
14.12.2024 - version 2 released!
|
|
20
16
|
|
|
21
17
|
- Rewritten in Typescript
|
|
22
18
|
- 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](
|
|
19
|
+
- See [MIGRATION.md](https://github.com/jisotalo/ads-client/blob/v2-dev/MIGRATION.md) for guide of migrating v1 -> v2 (**breaking changes!**)
|
|
20
|
+
- See the new [documentation](https://jisotalo.fi/ads-client/classes/Client.html)
|
|
21
|
+
|
|
22
|
+
Documentation for legacy version 1.4.4 is archived at [https://archive.ph/Sswco](https://archive.ph/Sswco).
|
|
25
23
|
|
|
26
24
|
# Features
|
|
27
25
|
- Supports TwinCAT 2 and 3
|
|
26
|
+
- Supports connecting to the local TwinCAT 3 runtime
|
|
28
27
|
- Supports any kind of target systems with ADS protocol (local runtime, PLC, EtherCAT I/O...)
|
|
29
28
|
- Supports multiple connections from the same host
|
|
30
29
|
- Reading and writing any kind of variables
|
|
@@ -35,8 +34,13 @@ Version 2 is almost ready!
|
|
|
35
34
|
- Automatic byte alignment support (all pack-modes automatically supported)
|
|
36
35
|
|
|
37
36
|
# Table of contents
|
|
37
|
+
- [ads-client](#ads-client)
|
|
38
|
+
- [Project status](#project-status)
|
|
39
|
+
- [Features](#features)
|
|
40
|
+
- [Table of contents](#table-of-contents)
|
|
38
41
|
- [Support](#support)
|
|
39
42
|
- [Installing](#installing)
|
|
43
|
+
- [Minimal example (TLDR)](#minimal-example-tldr)
|
|
40
44
|
- [Connection setup](#connection-setup)
|
|
41
45
|
- [Setup 1 - Connect from Windows](#setup-1---connect-from-windows)
|
|
42
46
|
- [Setup 2 - Connect from Linux/Windows](#setup-2---connect-from-linuxwindows)
|
|
@@ -66,22 +70,35 @@ Version 2 is almost ready!
|
|
|
66
70
|
- [Raw data](#raw-data)
|
|
67
71
|
- [Unsubscribing](#unsubscribing)
|
|
68
72
|
- [Using variable handles](#using-variable-handles)
|
|
73
|
+
- [Reading a value using a variable handle](#reading-a-value-using-a-variable-handle)
|
|
74
|
+
- [Writing a value using a variable handle](#writing-a-value-using-a-variable-handle)
|
|
69
75
|
- [Calling function block RPC methods](#calling-function-block-rpc-methods)
|
|
70
|
-
|
|
76
|
+
- [Things to note when using RPC Methods](#things-to-note-when-using-rpc-methods)
|
|
77
|
+
- [About examples](#about-examples)
|
|
78
|
+
- [RPC method with standard data types](#rpc-method-with-standard-data-types)
|
|
79
|
+
- [RPC method with struct](#rpc-method-with-struct)
|
|
80
|
+
- [Converting data between raw data and Javascript objects](#converting-data-between-raw-data-and-javascript-objects)
|
|
81
|
+
- [Converting a raw value to a Javascript object](#converting-a-raw-value-to-a-javascript-object)
|
|
82
|
+
- [Converting a Javascript object to a raw value](#converting-a-javascript-object-to-a-raw-value)
|
|
71
83
|
- [Other features](#other-features)
|
|
84
|
+
- [ADS sum commands](#ads-sum-commands)
|
|
85
|
+
- [Starting and stopping a PLC](#starting-and-stopping-a-plc)
|
|
86
|
+
- [Starting and stopping TwinCAT system](#starting-and-stopping-twincat-system)
|
|
87
|
+
- [Client events](#client-events)
|
|
88
|
+
- [Debugging](#debugging)
|
|
72
89
|
- [Disconnecting](#disconnecting)
|
|
73
|
-
- [
|
|
74
|
-
- [
|
|
90
|
+
- [Common issues and questions](#common-issues-and-questions)
|
|
91
|
+
- [There are lot's of connection issues and timeouts](#there-are-lots-of-connection-issues-and-timeouts)
|
|
75
92
|
- [Getting `TypeError: Do not know how to serialize a BigInt`](#getting-typeerror-do-not-know-how-to-serialize-a-bigint)
|
|
76
93
|
- [Can I connect from Raspberry Pi to TwinCAT?](#can-i-connect-from-raspberry-pi-to-twincat)
|
|
77
94
|
- [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
95
|
- [Having timeouts or 'mailbox is full' errors](#having-timeouts-or-mailbox-is-full-errors)
|
|
79
96
|
- [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
97
|
- [A data type is not found even when it should be](#a-data-type-is-not-found-even-when-it-should-be)
|
|
81
|
-
- [
|
|
98
|
+
- [Connection failed - failed to set PLC connection](#connection-failed---failed-to-set-plc-connection)
|
|
82
99
|
- [Connection failed (error EADDRNOTAVAIL)](#connection-failed-error-eaddrnotavail)
|
|
83
100
|
- [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)
|
|
101
|
+
- [How to connect to a PLC that is in CONFIG mode?](#how-to-connect-to-a-plc-that-is-in-config-mode)
|
|
85
102
|
- [Issues with TwinCAT 2 low-end devices (BK9050, BC9050 etc.)](#issues-with-twincat-2-low-end-devices-bk9050-bc9050-etc)
|
|
86
103
|
- [External links](#external-links)
|
|
87
104
|
- [Library testing](#library-testing)
|
|
@@ -110,7 +127,7 @@ If you need help with integrating the ads-client, I'm available for coding work
|
|
|
110
127
|
Install the [npm package](https://www.npmjs.com/package/ads-client):
|
|
111
128
|
|
|
112
129
|
```
|
|
113
|
-
npm install ads-client
|
|
130
|
+
npm install ads-client
|
|
114
131
|
```
|
|
115
132
|
|
|
116
133
|
Include the module in your code:
|
|
@@ -125,11 +142,53 @@ import { Client } from 'ads-client';
|
|
|
125
142
|
|
|
126
143
|
You can also clone the repository and run `npm run build`. After that, the library is available at `./dist/`
|
|
127
144
|
|
|
145
|
+
# Minimal example (TLDR)
|
|
146
|
+
|
|
147
|
+
This connects to a local PLC runtime, reads a value, writes a value, reads it again and then disconnects. The value is a string and it's located in `GVL_Global.StringValue`.
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
const { Client } = require('ads-client');
|
|
151
|
+
|
|
152
|
+
const client = new Client({
|
|
153
|
+
targetAmsNetId: 'localhost',
|
|
154
|
+
targetAdsPort: 851
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
client.connect()
|
|
158
|
+
.then(async (res) => {
|
|
159
|
+
console.log(`Connected to the ${res.targetAmsNetId}`);
|
|
160
|
+
console.log(`Router assigned us AmsNetId ${res.localAmsNetId} and port ${res.localAdsPort}`);
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
//Reading a value
|
|
164
|
+
const read = await client.readValue('GVL_Global.StringValue');
|
|
165
|
+
console.log('Value read (before):', read.value);
|
|
166
|
+
|
|
167
|
+
//Writing a value
|
|
168
|
+
await client.writeValue('GVL_Global.StringValue', 'This is a new value');
|
|
169
|
+
|
|
170
|
+
//Reading a value
|
|
171
|
+
const read2 = await client.readValue('GVL_Global.StringValue');
|
|
172
|
+
console.log('Value read (after):', read2.value);
|
|
173
|
+
|
|
174
|
+
} catch (err) {
|
|
175
|
+
console.log('Something failed:', err);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
//Disconnecting
|
|
179
|
+
await client.disconnect();
|
|
180
|
+
console.log('Disconnected');
|
|
181
|
+
|
|
182
|
+
}).catch(err => {
|
|
183
|
+
console.log('Error:', err);
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
128
187
|
# Connection setup
|
|
129
188
|
|
|
130
189
|
The ads-client can be used with multiple system configurations.
|
|
131
190
|
|
|
132
|
-
|
|
191
|
+

|
|
133
192
|
|
|
134
193
|
|
|
135
194
|
## Setup 1 - Connect from Windows
|
|
@@ -357,7 +416,7 @@ await client.readSymbol('.ExampleSTRUCT') //TwinCAT 2
|
|
|
357
416
|
|
|
358
417
|
## Documentation
|
|
359
418
|
|
|
360
|
-
The documentation is available at [https://jisotalo.fi/ads-client](
|
|
419
|
+
The documentation is available at [https://jisotalo.fi/ads-client](https://jisotalo.fi/ads-client/classes/Client.html) and `./docs` folder.
|
|
361
420
|
|
|
362
421
|
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
422
|
|
|
@@ -367,67 +426,63 @@ You can use the test PLC project as reference together with the [ads-client.test
|
|
|
367
426
|
|
|
368
427
|
Click a method to open it's documentation.
|
|
369
428
|
|
|
370
|
-
| Method
|
|
371
|
-
|
|
|
372
|
-
| [`cacheDataTypes()`](
|
|
373
|
-
| [`cacheSymbols()`](
|
|
374
|
-
| [`connect()`](
|
|
375
|
-
| [`convertFromRaw()`](
|
|
376
|
-
| [`convertToRaw()`](
|
|
377
|
-
| [`createVariableHandle()`](
|
|
378
|
-
| [`createVariableHandleMulti()`](
|
|
379
|
-
| [`deleteVariableHandle()`](
|
|
380
|
-
| [`deleteVariableHandleMulti()`](
|
|
381
|
-
| [`disconnect()`](
|
|
382
|
-
| [`getDataType()`](
|
|
383
|
-
| [`getDataTypes()`](
|
|
384
|
-
| [`getDefaultPlcObject()`](
|
|
385
|
-
| [`getSymbol()`](
|
|
386
|
-
| [`getSymbols()`](
|
|
387
|
-
| [`invokeRpcMethod()`](
|
|
388
|
-
| [`readDeviceInfo()`](
|
|
389
|
-
| [`readPlcRuntimeState()`](
|
|
390
|
-
| [`readPlcSymbolVersion()`](
|
|
391
|
-
| [`readPlcUploadInfo()`](
|
|
392
|
-
| [`readRaw()`](
|
|
393
|
-
| [`readRawByHandle()`](
|
|
394
|
-
| [`readRawByPath()`](
|
|
395
|
-
| [`readRawBySymbol()`](
|
|
396
|
-
| [`readRawMulti()`](
|
|
397
|
-
| [`readState()`](
|
|
398
|
-
| [`readTcSystemState()`](
|
|
399
|
-
| [`readValue()`](
|
|
400
|
-
| [`readValueBySymbol()`](
|
|
401
|
-
| [`readWriteRaw()`](
|
|
402
|
-
| [`readWriteRawMulti()`](
|
|
403
|
-
| [`reconnect()`](
|
|
404
|
-
| [`resetPlc()`](
|
|
405
|
-
| [`restartPlc()`](
|
|
406
|
-
| [`restartTcSystem()`](
|
|
407
|
-
| [`sendAdsCommand()`](
|
|
408
|
-
| [`
|
|
409
|
-
| [`
|
|
410
|
-
| [`
|
|
411
|
-
| [`
|
|
412
|
-
| [`
|
|
413
|
-
| [`
|
|
414
|
-
| [`
|
|
415
|
-
| [`
|
|
416
|
-
| [`
|
|
417
|
-
| [`
|
|
418
|
-
| [`
|
|
419
|
-
| [`
|
|
420
|
-
| [`
|
|
421
|
-
| [`
|
|
422
|
-
| [`
|
|
423
|
-
| [`
|
|
424
|
-
| [`
|
|
425
|
-
| [`
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
429
|
+
| Method | Description |
|
|
430
|
+
| --------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
431
|
+
| [`cacheDataTypes()`](https://jisotalo.fi/ads-client/classes/Client.html#cacheDataTypes) | Caches all data types from the target PLC runtime. |
|
|
432
|
+
| [`cacheSymbols()`](https://jisotalo.fi/ads-client/classes/Client.html#cacheSymbols) | Caches all symbols from the target PLC runtime. |
|
|
433
|
+
| [`connect()`](https://jisotalo.fi/ads-client/classes/Client.html#connect) | Connects to the target. |
|
|
434
|
+
| [`convertFromRaw()`](https://jisotalo.fi/ads-client/classes/Client.html#convertFromRaw) | Converts raw data to a Javascript object by using the provided data type. |
|
|
435
|
+
| [`convertToRaw()`](https://jisotalo.fi/ads-client/classes/Client.html#convertToRaw) | Converts a Javascript object to raw data by using the provided data type. |
|
|
436
|
+
| [`createVariableHandle()`](https://jisotalo.fi/ads-client/classes/Client.html#createVariableHandle) | Creates a handle to a variable at the target system by variable path (such as `GVL_Test.ExampleStruct`). |
|
|
437
|
+
| [`createVariableHandleMulti()`](https://jisotalo.fi/ads-client/classes/Client.html#createVariableHandleMulti) | Sends multiple `createVariableHandle()` commands in one ADS packet (ADS sum command). |
|
|
438
|
+
| [`deleteVariableHandle()`](https://jisotalo.fi/ads-client/classes/Client.html#deleteVariableHandle) | Deletes a variable handle that was previously created using `createVariableHandle()`. |
|
|
439
|
+
| [`deleteVariableHandleMulti()`](https://jisotalo.fi/ads-client/classes/Client.html#deleteVariableHandleMulti) | Sends multiple `deleteVariableHandle()` commands in one ADS packet (ADS sum command). |
|
|
440
|
+
| [`disconnect()`](https://jisotalo.fi/ads-client/classes/Client.html#disconnect) | Disconnects from the target and closes active connection. |
|
|
441
|
+
| [`getDataType()`](https://jisotalo.fi/ads-client/classes/Client.html#getDataType) | Returns full data type declaration for requested data type (such as `ST_Struct`). |
|
|
442
|
+
| [`getDataTypes()`](https://jisotalo.fi/ads-client/classes/Client.html#getDataTypes) | Returns all target PLC runtime data types. |
|
|
443
|
+
| [`getDefaultPlcObject()`](https://jisotalo.fi/ads-client/classes/Client.html#getDefaultPlcObject) | Returns a default (empty) Javascript object representing provided PLC data type. |
|
|
444
|
+
| [`getSymbol()`](https://jisotalo.fi/ads-client/classes/Client.html#getSymbol) | Returns a symbol object for given variable path (such as `GVL_Test.ExampleStruct`). |
|
|
445
|
+
| [`getSymbols()`](https://jisotalo.fi/ads-client/classes/Client.html#getSymbols) | Returns all symbols from the target PLC runtime. |
|
|
446
|
+
| [`invokeRpcMethod()`](https://jisotalo.fi/ads-client/classes/Client.html#invokeRpcMethod) | Invokes a function block RPC method on the target system. |
|
|
447
|
+
| [`readDeviceInfo()`](https://jisotalo.fi/ads-client/classes/Client.html#readDeviceInfo) | Reads target device information. |
|
|
448
|
+
| [`readPlcRuntimeState()`](https://jisotalo.fi/ads-client/classes/Client.html#readPlcRuntimeState) | Reads target PLC runtime state (`Run`, `Stop` etc.) |
|
|
449
|
+
| [`readPlcSymbolVersion()`](https://jisotalo.fi/ads-client/classes/Client.html#readPlcSymbolVersion) | Reads target PLC runtime symbol version. |
|
|
450
|
+
| [`readPlcUploadInfo()`](https://jisotalo.fi/ads-client/classes/Client.html#readPlcUploadInfo) | Reads target PLC runtime upload information. |
|
|
451
|
+
| [`readRaw()`](https://jisotalo.fi/ads-client/classes/Client.html#readRaw) | Reads raw data from the target system by a raw ADS address (index group, index offset and data length). |
|
|
452
|
+
| [`readRawByHandle()`](https://jisotalo.fi/ads-client/classes/Client.html#readRawByHandle) | Reads raw data from the target system by a previously created variable handle (acquired using `createVariableHandle()`). |
|
|
453
|
+
| [`readRawByPath()`](https://jisotalo.fi/ads-client/classes/Client.html#readRawByPath) | Reads raw data from the target system by variable path (such as `GVL_Test.ExampleStruct`). |
|
|
454
|
+
| [`readRawBySymbol()`](https://jisotalo.fi/ads-client/classes/Client.html#readRawBySymbol) | Reads raw data from the target system by a symbol object (acquired using `getSymbol()`). |
|
|
455
|
+
| [`readRawMulti()`](https://jisotalo.fi/ads-client/classes/Client.html#readRawMulti) | Sends multiple `readRaw()` commands in one ADS packet (ADS sum command). |
|
|
456
|
+
| [`readState()`](https://jisotalo.fi/ads-client/classes/Client.html#readState) | Reads target ADS state. |
|
|
457
|
+
| [`readTcSystemState()`](https://jisotalo.fi/ads-client/classes/Client.html#readTcSystemState) | Reads target TwinCAT system state from ADS port 10000 (usually `Run` or `Config`). |
|
|
458
|
+
| [`readValue()`](https://jisotalo.fi/ads-client/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. |
|
|
459
|
+
| [`readValueBySymbol()`](https://jisotalo.fi/ads-client/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. |
|
|
460
|
+
| [`readWriteRaw()`](https://jisotalo.fi/ads-client/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. |
|
|
461
|
+
| [`readWriteRawMulti()`](https://jisotalo.fi/ads-client/classes/Client.html#readWriteRawMulti) | Sends multiple `readWriteRaw()` commands in one ADS packet (ADS sum command). |
|
|
462
|
+
| [`reconnect()`](https://jisotalo.fi/ads-client/classes/Client.html#reconnect) | Reconnects to the target (disconnects and then connects again). |
|
|
463
|
+
| [`resetPlc()`](https://jisotalo.fi/ads-client/classes/Client.html#resetPlc) | Resets the target PLC runtime. Same as reset cold in TwinCAT XAE. |
|
|
464
|
+
| [`restartPlc()`](https://jisotalo.fi/ads-client/classes/Client.html#restartPlc) | Restarts the PLC runtime. Same as calling `resetPlc()` and then `startPlc()`. |
|
|
465
|
+
| [`restartTcSystem()`](https://jisotalo.fi/ads-client/classes/Client.html#restartTcSystem) | Restarts the target TwinCAT system. |
|
|
466
|
+
| [`sendAdsCommand()`](https://jisotalo.fi/ads-client/classes/Client.html#sendAdsCommand) | Sends a raw ADS command to the target. |
|
|
467
|
+
| [`sendAdsCommandWithFallback()`](https://jisotalo.fi/ads-client/classes/Client.html#sendAdsCommandWithFallback) | Sends a raw ADS command to the target. If it fails to specific ADS error codes, sends the fallback ADS command. |
|
|
468
|
+
| [`setDebugLevel()`](https://jisotalo.fi/ads-client/classes/Client.html#setDebugLevel) | Sets active debug level. |
|
|
469
|
+
| [`setTcSystemToConfig()`](https://jisotalo.fi/ads-client/classes/Client.html#setTcSystemToConfig) | Sets the target TwinCAT system to config mode. Same as `Restart TwinCAT (Config mode)` in TwinCAT XAE. |
|
|
470
|
+
| [`setTcSystemToRun()`](https://jisotalo.fi/ads-client/classes/Client.html#setTcSystemToRun) | Sets the target TwinCAT system to run mode. Same as `Restart TwinCAT system` in TwinCAT XAE. |
|
|
471
|
+
| [`startPlc()`](https://jisotalo.fi/ads-client/classes/Client.html#startPlc) | Starts the target PLC runtime. Same as pressing the green play button in TwinCAT XAE. |
|
|
472
|
+
| [`stopPlc()`](https://jisotalo.fi/ads-client/classes/Client.html#stopPlc) | Stops the target PLC runtime. Same as pressing the red stop button in TwinCAT XAE. |
|
|
473
|
+
| [`subscribe()`](https://jisotalo.fi/ads-client/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). |
|
|
474
|
+
| [`subscribeRaw()`](https://jisotalo.fi/ads-client/classes/Client.html#subscribeRaw) | Subscribes to raw value change notifications (ADS notifications) by a raw ADS address (index group, index offset and data length). |
|
|
475
|
+
| [`subscribeValue()`](https://jisotalo.fi/ads-client/classes/Client.html#subscribeValue) | Subscribes to value change notifications (ADS notifications) by a variable path, such as `GVL_Test.ExampleStruct`. |
|
|
476
|
+
| [`unsubscribe()`](https://jisotalo.fi/ads-client/classes/Client.html#unsubscribe) | Unsubscribes a subscription (deletes ADS notification). |
|
|
477
|
+
| [`unsubscribeAll()`](https://jisotalo.fi/ads-client/classes/Client.html#unsubscribeAll) | Unsubscribes all active subscription (deletes all ADS notifications). |
|
|
478
|
+
| [`writeControl()`](https://jisotalo.fi/ads-client/classes/Client.html#writeControl) | Sends an ADS `WriteControl` command to the target. |
|
|
479
|
+
| [`writeRaw()`](https://jisotalo.fi/ads-client/classes/Client.html#writeRaw) | Writes raw data to the target system by a raw ADS address (index group, index offset and data length). |
|
|
480
|
+
| [`writeRawByHandle()`](https://jisotalo.fi/ads-client/classes/Client.html#writeRawByHandle) | Writes raw data to the target system by a previously created variable handle (acquired using `createVariableHandle()`). |
|
|
481
|
+
| [`writeRawByPath()`](https://jisotalo.fi/ads-client/classes/Client.html#writeRawByPath) | Writes raw data to the target system by variable path (such as `GVL_Test.ExampleStruct`). |
|
|
482
|
+
| [`writeRawBySymbol()`](https://jisotalo.fi/ads-client/classes/Client.html#writeRawBySymbol) | Writes raw data to the target system by a symbol object (acquired using `getSymbol()`). |
|
|
483
|
+
| [`writeRawMulti()`](https://jisotalo.fi/ads-client/classes/Client.html#writeRawMulti) | Sends multiple `writeRaw()` commands in one ADS packet (ADS sum command). |
|
|
484
|
+
| [`writeValue()`](https://jisotalo.fi/ads-client/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. |
|
|
485
|
+
| [`writeValueBySymbol()`](https://jisotalo.fi/ads-client/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. |
|
|
431
486
|
|
|
432
487
|
## Creating a client
|
|
433
488
|
|
|
@@ -456,15 +511,21 @@ const { Client } = require('ads-client');
|
|
|
456
511
|
const client = new Client({
|
|
457
512
|
targetAmsNetId: 'localhost',
|
|
458
513
|
targetAdsPort: 851
|
|
459
|
-
})
|
|
514
|
+
});
|
|
460
515
|
|
|
461
516
|
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}`)
|
|
517
|
+
.then(async (res) => {
|
|
518
|
+
console.log(`Connected to the ${res.targetAmsNetId}`);
|
|
519
|
+
console.log(`Router assigned us AmsNetId ${res.localAmsNetId} and port ${res.localAdsPort}`);
|
|
520
|
+
//Connected
|
|
521
|
+
|
|
522
|
+
//...
|
|
523
|
+
|
|
524
|
+
//Disconnecting
|
|
525
|
+
await client.disconnect();
|
|
465
526
|
|
|
466
527
|
}).catch(err => {
|
|
467
|
-
console.log('Error:', err)
|
|
528
|
+
console.log('Error:', err);
|
|
468
529
|
});
|
|
469
530
|
```
|
|
470
531
|
|
|
@@ -472,73 +533,85 @@ client.connect()
|
|
|
472
533
|
|
|
473
534
|
### Reading any value
|
|
474
535
|
|
|
475
|
-
Use [`readValue()`](
|
|
536
|
+
Use [`readValue()`](https://jisotalo.fi/ads-client/classes/Client.html#readValue) to read any PLC value.
|
|
476
537
|
|
|
477
538
|
The only exception is the dereferenced value of a reference/pointer, see [Reading reference/pointer](#reading-referencepointer).
|
|
478
539
|
|
|
540
|
+
**Reading INT**
|
|
541
|
+
|
|
479
542
|
```js
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
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 ]
|
|
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);
|
|
543
|
+
const res = await client.readValue('GVL_Read.StandardTypes.INT_');
|
|
544
|
+
console.log(res.value);
|
|
545
|
+
// 32767
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
**Reading STRING**
|
|
549
|
+
|
|
550
|
+
```js
|
|
551
|
+
const res = await client.readValue('GVL_Read.StandardTypes.STRING_');
|
|
552
|
+
console.log(res.value);
|
|
553
|
+
// A test string ääöö!!@@
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
**Reading DT**
|
|
557
|
+
|
|
558
|
+
```js
|
|
559
|
+
const res = await client.readValue('GVL_Read.StandardTypes.DT_');
|
|
560
|
+
console.log(res.value);
|
|
561
|
+
// 2106-02-06T06:28:15.000Z (Date object)
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
**Reading STRUCT**
|
|
565
|
+
|
|
566
|
+
```js
|
|
567
|
+
const res = await client.readValue('GVL_Read.ComplexTypes.STRUCT_');
|
|
568
|
+
console.log(res.value);
|
|
569
|
+
/*
|
|
570
|
+
{
|
|
571
|
+
BOOL_: true,
|
|
572
|
+
BOOL_2: false,
|
|
573
|
+
BYTE_: 255,
|
|
574
|
+
WORD_: 65535,
|
|
575
|
+
//...and so on
|
|
528
576
|
}
|
|
577
|
+
*/
|
|
529
578
|
```
|
|
530
579
|
|
|
531
|
-
**
|
|
580
|
+
**Reading FUNCTION_BLOCK**
|
|
532
581
|
|
|
533
|
-
```
|
|
534
|
-
|
|
582
|
+
```js
|
|
583
|
+
const res = await client.readValue('GVL_Read.ComplexTypes.BLOCK_2'); //TON
|
|
584
|
+
console.log(res.value);
|
|
585
|
+
// { IN: false, PT: 2500, Q: false, ET: 0, M: false, StartTime: 0 }
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
**Reading ARRAY**
|
|
589
|
+
|
|
590
|
+
```js
|
|
591
|
+
const res = await client.readValue('GVL_Read.StandardArrays.REAL_3');
|
|
592
|
+
console.log(res.value);
|
|
593
|
+
// [ 75483.546875, 0, -75483.546875 ]
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
**Reading ENUM**
|
|
535
597
|
|
|
536
|
-
|
|
537
|
-
res = await client.readValue
|
|
598
|
+
```js
|
|
599
|
+
const res = await client.readValue('GVL_Read.ComplexTypes.ENUM_');
|
|
600
|
+
console.log(res.value);
|
|
601
|
+
// { name: 'Running', value: 100 }
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
**Typescript example: Reading INT**
|
|
605
|
+
|
|
606
|
+
```ts
|
|
607
|
+
const res = await client.readValue<number>('GVL_Read.StandardTypes.INT_');
|
|
538
608
|
console.log(res.value); //res.value is typed as number
|
|
539
609
|
// 32767
|
|
610
|
+
```
|
|
540
611
|
|
|
541
|
-
|
|
612
|
+
**Typescript example: Reading STRUCT**
|
|
613
|
+
|
|
614
|
+
```ts
|
|
542
615
|
interface ST_ComplexTypes {
|
|
543
616
|
BOOL_: boolean,
|
|
544
617
|
BOOL_2: boolean,
|
|
@@ -547,7 +620,7 @@ interface ST_ComplexTypes {
|
|
|
547
620
|
//..and so on
|
|
548
621
|
}
|
|
549
622
|
|
|
550
|
-
res = await client.readValue<ST_ComplexTypes>('GVL_Read.ComplexTypes.STRUCT_');
|
|
623
|
+
const res = await client.readValue<ST_ComplexTypes>('GVL_Read.ComplexTypes.STRUCT_');
|
|
551
624
|
console.log(res.value); //res.value is typed as ST_ComplexTypes
|
|
552
625
|
/*
|
|
553
626
|
{
|
|
@@ -562,382 +635,436 @@ console.log(res.value); //res.value is typed as ST_ComplexTypes
|
|
|
562
635
|
|
|
563
636
|
### Reading raw data
|
|
564
637
|
|
|
565
|
-
Use [`readRaw()`](
|
|
638
|
+
Use [`readRaw()`](https://jisotalo.fi/ads-client/classes/Client.html#readRaw) or [`readRawByPath()`](https://jisotalo.fi/ads-client/classes/Client.html#readRawByPath) to read any PLC value as raw data. The raw data in this context means received bytes (a `Buffer` object).
|
|
566
639
|
|
|
567
640
|
The only exception is the dereferenced value of a reference/pointer, see [Reading reference/pointer](#reading-referencepointer).
|
|
568
641
|
|
|
569
|
-
|
|
642
|
+
For converting the data between raw data and Javascript object, see [Converting data between raw data and Javascript objects](#converting-data-between-raw-data-and-javascript-objects).
|
|
570
643
|
|
|
571
|
-
|
|
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>
|
|
644
|
+
**readRaw()**
|
|
577
645
|
|
|
578
|
-
|
|
579
|
-
console.log(converted); //32767
|
|
646
|
+
The `indexGroup` and `indexOffset` can be acquired by using [`getSymbol()`](https://jisotalo.fi/ads-client/classes/Client.html#getSymbol).
|
|
580
647
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
648
|
+
|
|
649
|
+
```js
|
|
650
|
+
//Read 2 bytes from indexGroup 16448 and indexOffset 414816
|
|
651
|
+
const data = await client.readRaw(16448, 414816, 2);
|
|
652
|
+
console.log(data); //<Buffer ff 7f>
|
|
584
653
|
```
|
|
585
654
|
|
|
586
|
-
|
|
655
|
+
**readRawByPath()**
|
|
587
656
|
|
|
588
657
|
```js
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
658
|
+
const data = await client.readRawByPath('GVL_Read.StandardTypes.INT_');
|
|
659
|
+
console.log(data); //<Buffer ff 7f>
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
### Reading reference/pointer
|
|
594
664
|
|
|
595
|
-
|
|
596
|
-
console.log(converted); //32767
|
|
665
|
+
The dereferenced value of a reference (`REFERENCE TO`) or a pointer (`POINTER TO`) can be read with [`readRawByPath()`](https://jisotalo.fi/ads-client/classes/Client.html#readRawByPath) or by using [variable handles](https://jisotalo.fi/ads-client/classes/Client.html#createVariableHandle).
|
|
597
666
|
|
|
598
|
-
|
|
599
|
-
|
|
667
|
+
**Reading POINTER (readRawByPath())**
|
|
668
|
+
|
|
669
|
+
```js
|
|
670
|
+
//Reading a raw POINTER value (Note the dereference operator ^)
|
|
671
|
+
const value = await client.readRawByPath('GVL_Read.ComplexTypes.POINTER_^');
|
|
672
|
+
|
|
673
|
+
//Converting to a Javascript object
|
|
674
|
+
const value = await client.convertFromRaw(rawValue, 'ST_StandardTypes');
|
|
675
|
+
console.log(value);
|
|
676
|
+
/*
|
|
677
|
+
{
|
|
678
|
+
BOOL_: true,
|
|
679
|
+
BOOL_2: false,
|
|
680
|
+
BYTE_: 255,
|
|
681
|
+
WORD_: 65535,
|
|
682
|
+
//..and so on
|
|
600
683
|
}
|
|
684
|
+
*/
|
|
601
685
|
```
|
|
602
686
|
|
|
603
|
-
|
|
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).
|
|
687
|
+
**Reading REFERENCE (readRawByPath())**
|
|
607
688
|
|
|
608
689
|
```js
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
const rawPtrValue = await client.readRawByPath('GVL_Read.ComplexTypes.POINTER_^');
|
|
690
|
+
//Reading a raw REFERENCE value
|
|
691
|
+
const rawValue = await client.readRawByPath('GVL_Read.ComplexTypes.REFERENCE_');
|
|
612
692
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
693
|
+
//Converting to a Javascript object
|
|
694
|
+
const value = await client.convertFromRaw(rawValue, 'ST_StandardTypes');
|
|
695
|
+
console.log(value);
|
|
696
|
+
/*
|
|
697
|
+
{
|
|
698
|
+
BOOL_: true,
|
|
699
|
+
BOOL_2: false,
|
|
700
|
+
BYTE_: 255,
|
|
701
|
+
WORD_: 65535,
|
|
702
|
+
//..and so on
|
|
703
|
+
}
|
|
704
|
+
*/
|
|
705
|
+
```
|
|
616
706
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
707
|
+
**Reading POINTER (variable handle)**
|
|
708
|
+
|
|
709
|
+
```js
|
|
710
|
+
//Reading a POINTER value (Note the dereference operator ^)
|
|
711
|
+
const handle = await client.createVariableHandle('GVL_Read.ComplexTypes.POINTER_^');
|
|
712
|
+
const rawValue = await client.readRawByHandle(handle);
|
|
713
|
+
await client.deleteVariableHandle(handle);
|
|
714
|
+
|
|
715
|
+
//Converting to a Javascript object
|
|
716
|
+
const value = await client.convertFromRaw(rawValue, 'ST_StandardTypes');
|
|
717
|
+
console.log(value);
|
|
718
|
+
/*
|
|
719
|
+
{
|
|
720
|
+
BOOL_: true,
|
|
721
|
+
BOOL_2: false,
|
|
722
|
+
BYTE_: 255,
|
|
723
|
+
WORD_: 65535,
|
|
724
|
+
//..and so on
|
|
632
725
|
}
|
|
726
|
+
*/
|
|
633
727
|
```
|
|
634
728
|
|
|
635
|
-
|
|
729
|
+
**Reading REFERENCE (variable handle)**
|
|
636
730
|
|
|
637
731
|
```js
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
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);
|
|
732
|
+
//Reading a REFERENCE value
|
|
733
|
+
const handle = await client.createVariableHandle('GVL_Read.ComplexTypes.REFERENCE_');
|
|
734
|
+
const rawValue = await client.readRawByHandle(handle);
|
|
735
|
+
await client.deleteVariableHandle(handle);
|
|
736
|
+
|
|
737
|
+
//Converting to a Javascript object
|
|
738
|
+
const value = await client.convertFromRaw(rawValue, 'ST_StandardTypes');
|
|
739
|
+
console.log(value);
|
|
740
|
+
/*
|
|
741
|
+
{
|
|
742
|
+
BOOL_: true,
|
|
743
|
+
BOOL_2: false,
|
|
744
|
+
BYTE_: 255,
|
|
745
|
+
WORD_: 65535,
|
|
746
|
+
//..and so on
|
|
659
747
|
}
|
|
748
|
+
*/
|
|
660
749
|
```
|
|
661
750
|
|
|
662
751
|
## Writing values
|
|
663
752
|
|
|
664
753
|
### Writing any value
|
|
665
754
|
|
|
666
|
-
Use [`writeValue()`](
|
|
755
|
+
Use [`writeValue()`](https://jisotalo.fi/ads-client/classes/Client.html#writeValue) to write any PLC value.
|
|
667
756
|
|
|
668
757
|
The only exception is the dereferenced value of a reference/pointer, see [Writing reference/pointer](#writing-referencepointer).
|
|
669
758
|
|
|
759
|
+
**Writing INT**
|
|
760
|
+
|
|
670
761
|
```js
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
762
|
+
const res = await client.writeValue('GVL_Write.StandardTypes.INT_', 32767);
|
|
763
|
+
console.log(res.value);
|
|
764
|
+
// 32767
|
|
765
|
+
```
|
|
674
766
|
|
|
675
|
-
|
|
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
|
-
});
|
|
767
|
+
**Writing STRING**
|
|
689
768
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
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
|
-
});
|
|
769
|
+
```js
|
|
770
|
+
await client.writeValue('GVL_Write.StandardTypes.STRING_', 'This is a test');
|
|
771
|
+
```
|
|
704
772
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
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
|
-
}
|
|
773
|
+
**Writing DT**
|
|
774
|
+
|
|
775
|
+
```js
|
|
776
|
+
await client.writeValue('GVL_Write.StandardTypes.DT_', new Date());
|
|
725
777
|
```
|
|
726
778
|
|
|
727
|
-
|
|
779
|
+
**Writing STRUCT (all properties)**
|
|
728
780
|
|
|
729
|
-
|
|
781
|
+
```js
|
|
782
|
+
await client.writeValue('GVL_Write.ComplexTypes.STRUCT_', {
|
|
783
|
+
BOOL_: true,
|
|
784
|
+
BOOL_2: false,
|
|
785
|
+
BYTE_: 255,
|
|
786
|
+
WORD_: 65535,
|
|
787
|
+
//...and so on
|
|
788
|
+
});
|
|
789
|
+
```
|
|
730
790
|
|
|
731
|
-
|
|
791
|
+
**Writing STRUCT (some properties only)**
|
|
732
792
|
|
|
733
|
-
The
|
|
793
|
+
All other properties will keep their values. The client reads the active value first and then makes changes.
|
|
734
794
|
|
|
735
795
|
```js
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
796
|
+
await client.writeValue('GVL_Write.ComplexTypes.STRUCT_', {
|
|
797
|
+
WORD_: 65535
|
|
798
|
+
}, true); //<-- NOTE: autoFill set
|
|
799
|
+
```
|
|
740
800
|
|
|
741
|
-
|
|
801
|
+
**Writing FUNCTION_BLOCK (all properties)**
|
|
742
802
|
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
803
|
+
```js
|
|
804
|
+
const timerBlock = {
|
|
805
|
+
IN: false,
|
|
806
|
+
PT: 2500,
|
|
807
|
+
Q: false,
|
|
808
|
+
ET: 0,
|
|
809
|
+
M: false,
|
|
810
|
+
StartTime: 0
|
|
811
|
+
};
|
|
812
|
+
|
|
813
|
+
await client.writeValue('GVL_Write.ComplexTypes.BLOCK_2', timerBlock);
|
|
746
814
|
```
|
|
747
815
|
|
|
748
|
-
|
|
816
|
+
**Writing FUNCTION_BLOCK (some properties only)**
|
|
817
|
+
|
|
818
|
+
All other properties will keep their values. The client reads the active value first and then makes changes.
|
|
749
819
|
|
|
750
820
|
```js
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
821
|
+
await client.writeValue('GVL_Write.ComplexTypes.BLOCK_2', {
|
|
822
|
+
IN: true
|
|
823
|
+
}, true); //<-- NOTE: autoFill set
|
|
824
|
+
```
|
|
755
825
|
|
|
756
|
-
|
|
826
|
+
**Writing ARRAY**
|
|
757
827
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
828
|
+
```js
|
|
829
|
+
const data = [
|
|
830
|
+
75483.546875,
|
|
831
|
+
0,
|
|
832
|
+
-75483.546875
|
|
833
|
+
];
|
|
834
|
+
|
|
835
|
+
await client.writeValue('GVL_Write.StandardArrays.REAL_3', data);
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
**Writing ENUM**
|
|
839
|
+
|
|
840
|
+
```js
|
|
841
|
+
await client.writeValue('GVL_Write.ComplexTypes.ENUM_', 'Running');
|
|
842
|
+
//...or...
|
|
843
|
+
await client.writeValue('GVL_Write.ComplexTypes.ENUM_', 100);
|
|
844
|
+
```
|
|
845
|
+
|
|
846
|
+
### Writing raw data
|
|
847
|
+
|
|
848
|
+
Use [`writeRaw()`](https://jisotalo.fi/ads-client/classes/Client.html#writeRaw) or [`writeRawByPath()`](https://jisotalo.fi/ads-client/classes/Client.html#writeRawByPath) to write any PLC value using raw data. The raw data in this context means bytes (a `Buffer` object).
|
|
849
|
+
|
|
850
|
+
The only exception is the dereferenced value of a reference/pointer, see [Reading reference/pointer](#reading-referencepointer).
|
|
851
|
+
|
|
852
|
+
For converting the data between raw data and Javascript object, see [Converting data between raw data and Javascript objects](#converting-data-between-raw-data-and-javascript-objects).
|
|
853
|
+
|
|
854
|
+
**writeRaw()**
|
|
855
|
+
```js
|
|
856
|
+
//Creating raw data of an INT
|
|
857
|
+
const data = await client.convertToRaw(32767, 'INT');
|
|
858
|
+
console.log(data); //<Buffer ff 7f>
|
|
859
|
+
|
|
860
|
+
//Writing the value to indexGroup 16448 and indexOffset 414816
|
|
861
|
+
await client.writeRaw(16448, 414816, data);
|
|
862
|
+
```
|
|
863
|
+
|
|
864
|
+
**writeRawByPath()**
|
|
865
|
+
|
|
866
|
+
```js
|
|
867
|
+
//Creating raw data of an INT
|
|
868
|
+
const data = await client.convertToRaw(32767, 'INT');
|
|
869
|
+
console.log(data); //<Buffer ff 7f>
|
|
870
|
+
|
|
871
|
+
await client.writeRawByPath('GVL_Write.StandardTypes.INT_', data);
|
|
761
872
|
```
|
|
762
873
|
|
|
763
874
|
### Writing reference/pointer
|
|
764
875
|
|
|
765
876
|
The dereferenced value of a reference (`REFERENCE TO`) or a pointer (`POINTER TO`) can be written
|
|
766
|
-
|
|
877
|
+
with [`writeRawByPath()`](https://jisotalo.fi/ads-client/classes/Client.html#writeRawByPath) or by using [variable handles](https://jisotalo.fi/ads-client/classes/Client.html#createVariableHandle).
|
|
878
|
+
|
|
879
|
+
**Writing POINTER (writeRawByPath())**
|
|
767
880
|
|
|
768
881
|
```js
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
882
|
+
const value = {
|
|
883
|
+
BOOL_: true,
|
|
884
|
+
BOOL_2: false,
|
|
885
|
+
BYTE_: 255,
|
|
886
|
+
WORD_: 65535,
|
|
887
|
+
//...and so on
|
|
888
|
+
};
|
|
889
|
+
const rawValue = await client.convertToRaw(value, 'ST_StandardTypes');
|
|
773
890
|
|
|
774
|
-
|
|
891
|
+
//Writing a raw POINTER value (Note the dereference operator ^)
|
|
892
|
+
await client.writeRawByPath('GVL_Write.ComplexTypes.POINTER_^', rawValue);
|
|
893
|
+
```
|
|
775
894
|
|
|
776
|
-
|
|
777
|
-
const refValue = {...} //some value
|
|
778
|
-
const rawRefValue = await client.convertToRaw(refValue, 'ST_StandardTypes');
|
|
779
|
-
await client.writeRawByPath('GVL_Write.ComplexTypes.REFERENCE_', rawRefValue);
|
|
895
|
+
**Writing REFERENCE (writeRawByPath())**
|
|
780
896
|
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
897
|
+
```js
|
|
898
|
+
const value = {
|
|
899
|
+
BOOL_: true,
|
|
900
|
+
BOOL_2: false,
|
|
901
|
+
BYTE_: 255,
|
|
902
|
+
WORD_: 65535,
|
|
903
|
+
//...and so on
|
|
904
|
+
};
|
|
905
|
+
const rawValue = await client.convertToRaw(value, 'ST_StandardTypes');
|
|
906
|
+
|
|
907
|
+
//Writing a raw REFERENCE value
|
|
908
|
+
await client.writeRawByPath('GVL_Write.ComplexTypes.REFERENCE_', rawValue);
|
|
784
909
|
```
|
|
785
910
|
|
|
786
|
-
|
|
911
|
+
**Writing POINTER (variable handle)**
|
|
787
912
|
|
|
788
913
|
```js
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
914
|
+
const value = {
|
|
915
|
+
BOOL_: true,
|
|
916
|
+
BOOL_2: false,
|
|
917
|
+
BYTE_: 255,
|
|
918
|
+
WORD_: 65535,
|
|
919
|
+
//...and so on
|
|
920
|
+
};
|
|
921
|
+
const rawValue = await client.convertToRaw(value, 'ST_StandardTypes');
|
|
793
922
|
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
923
|
+
//Writing a raw POINTER value (Note the dereference operator ^)
|
|
924
|
+
const handle = await client.createVariableHandle('GVL_Write.ComplexTypes.POINTER_^');
|
|
925
|
+
await client.writeRawByHandle(handle, rawValue);
|
|
926
|
+
await client.deleteVariableHandle(handle);
|
|
927
|
+
```
|
|
797
928
|
|
|
798
|
-
|
|
799
|
-
const refValue = {...} //some value
|
|
800
|
-
const rawRefValue = await client.convertToRaw(refValue, 'ST_StandardTypes');
|
|
929
|
+
**Writing REFERENCE (variable handle)**
|
|
801
930
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
931
|
+
```js
|
|
932
|
+
const value = {
|
|
933
|
+
BOOL_: true,
|
|
934
|
+
BOOL_2: false,
|
|
935
|
+
BYTE_: 255,
|
|
936
|
+
WORD_: 65535,
|
|
937
|
+
//...and so on
|
|
938
|
+
};
|
|
939
|
+
const rawValue = await client.convertToRaw(value, 'ST_StandardTypes');
|
|
805
940
|
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
941
|
+
//Writing a raw REFERENCE value
|
|
942
|
+
const handle = await client.createVariableHandle('GVL_Write.ComplexTypes.POINTER_');
|
|
943
|
+
await client.writeRawByHandle(handle, rawValue);
|
|
944
|
+
await client.deleteVariableHandle(handle);
|
|
809
945
|
```
|
|
810
946
|
|
|
811
947
|
## Subscribing to value changes
|
|
812
948
|
|
|
813
|
-
In ads-client, subscriptions are used to handle ADS notifications.
|
|
949
|
+
In ads-client, subscriptions are used to handle ADS notifications. ADS notifications are data sent by PLC automatically without request. For example, the latest value of a variable every second.
|
|
814
950
|
|
|
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.
|
|
951
|
+
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 user callback function with the latest value.
|
|
816
952
|
|
|
817
953
|
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
954
|
|
|
819
955
|
### Any value
|
|
820
956
|
|
|
821
|
-
Use [`subscribeValue()`](
|
|
957
|
+
Use [`subscribeValue()`](https://jisotalo.fi/ads-client/classes/Client.html#subscribeValue) or [`subscribe()`](https://jisotalo.fi/ads-client/classes/Client.html#subscribe) to subscribe to PLC variable value changes.
|
|
958
|
+
|
|
959
|
+
**Example**
|
|
960
|
+
|
|
961
|
+
Subscribing to changes of `GVL_Subscription.NumericValue_10ms`. The callback is called when the PLC value changes (at maximum every 100 milliseconds).
|
|
822
962
|
|
|
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
963
|
```js
|
|
825
|
-
|
|
826
|
-
|
|
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);
|
|
964
|
+
const onValueChanged = (data, subscription) => {
|
|
965
|
+
console.log(`Value of ${subscription.symbol.name} has changed: ${data.value}`);
|
|
836
966
|
}
|
|
967
|
+
|
|
968
|
+
const subscription = await client.subscribeValue(
|
|
969
|
+
'GVL_Subscription.NumericValue_10ms',
|
|
970
|
+
onValueChanged,
|
|
971
|
+
100
|
|
972
|
+
);
|
|
837
973
|
```
|
|
838
974
|
|
|
839
|
-
Example
|
|
975
|
+
**Example**
|
|
976
|
+
|
|
977
|
+
Subscribing to value of `GVL_Subscription.NumericValue_1000ms`. The callback is called with the latest value every 100 milliseconds (doesn't matter if the value has changed or not).
|
|
840
978
|
|
|
841
979
|
```js
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
}
|
|
980
|
+
const onValueReceived = (data, subscription) => {
|
|
981
|
+
console.log(`Value of ${subscription.symbol.name} is: ${data.value}`);
|
|
982
|
+
}
|
|
846
983
|
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
984
|
+
const subscription = await client.subscribeValue(
|
|
985
|
+
'GVL_Subscription.NumericValue_1000ms',
|
|
986
|
+
onValueReceived,
|
|
987
|
+
100,
|
|
988
|
+
false
|
|
989
|
+
);
|
|
853
990
|
|
|
854
|
-
} catch (err) {
|
|
855
|
-
console.log("Error:", err);
|
|
856
|
-
}
|
|
857
991
|
```
|
|
858
992
|
|
|
859
|
-
|
|
993
|
+
**Typescript example**
|
|
860
994
|
|
|
861
|
-
|
|
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
|
-
}
|
|
868
|
-
|
|
869
|
-
const sub = await client.subscribe<number>({
|
|
870
|
-
target: 'GVL_Subscription.NumericValue_1000ms',
|
|
871
|
-
callback: valueChanged,
|
|
872
|
-
cycleTime: 100,
|
|
873
|
-
sendOnChange: false
|
|
874
|
-
});
|
|
995
|
+
Same as previous example, but with [`subscribe()`](https://jisotalo.fi/ads-client/classes/Client.html#subscribe) instead. A type can be provided for `subscribeValue<T>()` as well.
|
|
875
996
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
997
|
+
```js
|
|
998
|
+
const subscription = await client.subscribe<number>({
|
|
999
|
+
target: 'GVL_Subscription.NumericValue_1000ms',
|
|
1000
|
+
callback: (data, subscription) => {
|
|
1001
|
+
//data.value is typed as "number" instead of "any"
|
|
1002
|
+
console.log(`Value of ${subscription.symbol.name} is: ${data.value}`);
|
|
1003
|
+
},
|
|
1004
|
+
cycleTime: 100,
|
|
1005
|
+
sendOnChange: false
|
|
1006
|
+
});
|
|
879
1007
|
```
|
|
880
1008
|
|
|
881
1009
|
### Raw data
|
|
882
1010
|
|
|
883
|
-
Use [`subscribeRaw()`](
|
|
1011
|
+
Use [`subscribeRaw()`](https://jisotalo.fi/ads-client/classes/Client.html#subscribeRaw) or [`subscribe()`](https://jisotalo.fi/ads-client/classes/Client.html#subscribe) to subscribe to raw value changes.
|
|
1012
|
+
|
|
1013
|
+
The `indexGroup` and `indexOffset` can be acquired by using [`getSymbol()`](https://jisotalo.fi/ads-client/classes/Client.html#getSymbol).
|
|
884
1014
|
|
|
885
|
-
|
|
1015
|
+
**Example**
|
|
886
1016
|
|
|
887
|
-
|
|
1017
|
+
Subscribing to raw address of `indexGroup` = 16448 and `indexOffset` = 414816 (2 bytes).
|
|
888
1018
|
|
|
889
1019
|
```js
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
}, 100);
|
|
894
|
-
|
|
895
|
-
} catch (err) {
|
|
896
|
-
console.log("Error:", err);
|
|
897
|
-
}
|
|
1020
|
+
await client.subscribeRaw(16448, 414816, 2, (data, subscription) => {
|
|
1021
|
+
console.log(`Value has changed: ${data.value.toString('hex')}`);
|
|
1022
|
+
}, 100);
|
|
898
1023
|
```
|
|
899
1024
|
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
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
|
-
});
|
|
1025
|
+
**Example**
|
|
1026
|
+
|
|
1027
|
+
Same as previous example, but with [`subscribe()`](https://jisotalo.fi/ads-client/classes/Client.html#subscribe) instead.
|
|
914
1028
|
|
|
915
|
-
|
|
916
|
-
|
|
1029
|
+
```js
|
|
1030
|
+
const onValueChanged = (data, subscription) => {
|
|
1031
|
+
console.log(`Value has changed: ${data.value.toString('hex')}`);
|
|
917
1032
|
}
|
|
1033
|
+
|
|
1034
|
+
await client.subscribe({
|
|
1035
|
+
target: {
|
|
1036
|
+
indexGroup: 16448,
|
|
1037
|
+
indexOffset: 414816,
|
|
1038
|
+
size: 2
|
|
1039
|
+
},
|
|
1040
|
+
callback: onValueChanged,
|
|
1041
|
+
cycleTime: 100
|
|
1042
|
+
});
|
|
918
1043
|
```
|
|
919
1044
|
|
|
920
1045
|
### Unsubscribing
|
|
921
1046
|
|
|
922
|
-
Subscriptions should always be cancelled when no longer needed (to save PLC resources).
|
|
1047
|
+
Subscriptions should always be cancelled when no longer needed (to save PLC resources).
|
|
1048
|
+
|
|
1049
|
+
To unsubscribe, use [`unsubscribe()`](https://jisotalo.fi/ads-client/classes/Client.html#unsubscribe) or subscription object's [`ActiveSubscription.unsubscribe()`](https://jisotalo.fi/ads-client/interfaces/ActiveSubscription.html#unsubscribe).
|
|
923
1050
|
|
|
924
1051
|
```js
|
|
925
|
-
const
|
|
1052
|
+
const subscription = await client.subscribeValue(...);
|
|
926
1053
|
|
|
927
1054
|
//Later when no longer needed
|
|
928
|
-
await
|
|
929
|
-
|
|
1055
|
+
await subscription.unsubscribe();
|
|
1056
|
+
|
|
1057
|
+
//Or alternatively
|
|
1058
|
+
await client.unsubscribe(subscription);
|
|
930
1059
|
```
|
|
931
1060
|
|
|
932
1061
|
## Using variable handles
|
|
933
1062
|
|
|
934
|
-
|
|
1063
|
+
Using variable handles is an another way to read and write raw data.
|
|
935
1064
|
|
|
936
|
-
|
|
937
|
-
There is no need to use `indexGroup` or `indexOffset`.
|
|
1065
|
+
First, a handle is created to a specific PLC variable by the variable path. After that, read and write operations are available.
|
|
938
1066
|
|
|
939
1067
|
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
1068
|
|
|
942
1069
|
Handles can also be used to read/write reference and pointer values, see [Reading reference/pointer](#reading-referencepointer).
|
|
943
1070
|
|
|
@@ -978,49 +1105,277 @@ await client.deleteVariableHandle(handle);
|
|
|
978
1105
|
|
|
979
1106
|
## Calling function block RPC methods
|
|
980
1107
|
|
|
981
|
-
If a function block method has pragma `{attribute 'TcRpcEnable'}`,
|
|
1108
|
+
If a function block method has pragma `{attribute 'TcRpcEnable'}`,
|
|
1109
|
+
the method can be called from ads-client.
|
|
982
1110
|
|
|
983
1111
|
Read more at [Beckhoff Infosys: Attribute 'TcRpcEnable'](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_plc_intro/7145472907.html).
|
|
984
1112
|
|
|
985
1113
|
### Things to note when using RPC Methods
|
|
986
1114
|
|
|
1115
|
+
These are my own observations.
|
|
1116
|
+
|
|
987
1117
|
- Do not use online change if you change RPC method parameters or return data types
|
|
988
1118
|
- Make sure that parameters and return value have no pack-mode pragmas defined, otherwise data might be corrupted
|
|
989
1119
|
- 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
|
|
1120
|
+
- The feature isn't well documented by Beckhoff, so there might be some things that aren't taken into account
|
|
1121
|
+
|
|
1122
|
+
### About examples
|
|
1123
|
+
|
|
1124
|
+
These examples use `FB_RPC` from the test PLC project at [https://github.com/jisotalo/ads-client-test-plc-project](https://github.com/jisotalo/ads-client-test-plc-project).
|
|
1125
|
+
|
|
1126
|
+

|
|
1127
|
+
|
|
1128
|
+
There is an instance of the function block at `GVL_RPC.RpcBlock`.
|
|
1129
|
+
|
|
1130
|
+
### RPC method with standard data types
|
|
1131
|
+
|
|
1132
|
+
The `Calculator()` method calculates sum, product and division of the input values. The method returns `true`, if all calculations were successful.
|
|
1133
|
+
```
|
|
1134
|
+
{attribute 'TcRpcEnable'}
|
|
1135
|
+
METHOD Calculator : BOOL
|
|
1136
|
+
VAR_INPUT
|
|
1137
|
+
Value1 : REAL;
|
|
1138
|
+
Value2 : REAL;
|
|
1139
|
+
END_VAR
|
|
1140
|
+
VAR_OUTPUT
|
|
1141
|
+
Sum : REAL;
|
|
1142
|
+
Product : REAL;
|
|
1143
|
+
Division : REAL;
|
|
1144
|
+
END_VAR
|
|
1145
|
+
|
|
1146
|
+
//--- Code starts ---
|
|
1147
|
+
|
|
1148
|
+
//Return TRUE if all success
|
|
1149
|
+
Calculator := TRUE;
|
|
1150
|
+
|
|
1151
|
+
Sum := Value1 + Value2;
|
|
1152
|
+
Product := Value1 * Value2;
|
|
1153
|
+
|
|
1154
|
+
IF Value2 <> 0 THEN
|
|
1155
|
+
Division := Value1 / Value2;
|
|
1156
|
+
ELSE
|
|
1157
|
+
Division := 0;
|
|
1158
|
+
Calculator := FALSE;
|
|
1159
|
+
END_IF
|
|
1160
|
+
```
|
|
1161
|
+
|
|
1162
|
+
Example call:
|
|
1163
|
+
|
|
1164
|
+
```js
|
|
1165
|
+
const res = await client.invokeRpcMethod('GVL_RPC.RpcBlock', 'Calculator', {
|
|
1166
|
+
Value1: 1,
|
|
1167
|
+
Value2: 123
|
|
1168
|
+
});
|
|
1169
|
+
|
|
1170
|
+
console.log(res);
|
|
1171
|
+
/*
|
|
1172
|
+
{
|
|
1173
|
+
returnValue: true,
|
|
1174
|
+
outputs: {
|
|
1175
|
+
Sum: 124,
|
|
1176
|
+
Product: 123,
|
|
1177
|
+
Division: 0.008130080997943878
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
*/
|
|
1181
|
+
```
|
|
1182
|
+
|
|
1183
|
+
### RPC method with struct
|
|
1184
|
+
|
|
1185
|
+
The `Structs()` method takes a struct value as input, changes its values and then returns the result.
|
|
1186
|
+
|
|
1187
|
+
```
|
|
1188
|
+
{attribute 'TcRpcEnable'}
|
|
1189
|
+
METHOD Structs : ST_Struct
|
|
1190
|
+
VAR_INPUT
|
|
1191
|
+
Input : ST_Struct;
|
|
1192
|
+
END_VAR
|
|
991
1193
|
|
|
992
|
-
|
|
1194
|
+
//--- Code starts ---
|
|
993
1195
|
|
|
994
|
-
|
|
1196
|
+
Structs.SomeText := CONCAT('Response: ', Input.SomeText);
|
|
1197
|
+
Structs.SomeReal := Input.SomeReal * 10.0;
|
|
1198
|
+
Structs.SomeDate := Input.SomeDate + T#24H;
|
|
1199
|
+
```
|
|
1200
|
+
|
|
1201
|
+
Example call:
|
|
1202
|
+
|
|
1203
|
+
```js
|
|
1204
|
+
const res = await client.invokeRpcMethod('GVL_RPC.RpcBlock', 'Structs', {
|
|
1205
|
+
Input: {
|
|
1206
|
+
SomeText: 'Hello ads-client',
|
|
1207
|
+
SomeReal: 3.14,
|
|
1208
|
+
SomeDate: new Date('2024-12-24T00:00:00.000Z')
|
|
1209
|
+
}
|
|
1210
|
+
});
|
|
1211
|
+
|
|
1212
|
+
console.log(res);
|
|
1213
|
+
/*
|
|
1214
|
+
{
|
|
1215
|
+
returnValue: {
|
|
1216
|
+
SomeText: 'Response: Hello ads-client',
|
|
1217
|
+
SomeReal: 31.4,
|
|
1218
|
+
SomeDate: 2024-12-24T01:00:00.000Z
|
|
1219
|
+
},
|
|
1220
|
+
outputs: {}
|
|
1221
|
+
}
|
|
1222
|
+
*/
|
|
1223
|
+
```
|
|
1224
|
+
|
|
1225
|
+
## Converting data between raw data and Javascript objects
|
|
1226
|
+
|
|
1227
|
+
The raw data in this context means sent or received bytes (a `Buffer` object).
|
|
1228
|
+
|
|
1229
|
+
When using methods such as [`readValue()`](https://jisotalo.fi/ads-client/classes/Client.html#readValue), [`writeValue()`](https://jisotalo.fi/ads-client/classes/Client.html#writeValue) and [`subscribeValue()`](https://jisotalo.fi/ads-client/classes/Client.html#subscribeValue),
|
|
1230
|
+
the client converts data automatically. The conversion can be done manually as well, by using [`convertFromRaw()`](https://jisotalo.fi/ads-client/classes/Client.html#convertFromRaw) and [`convertToRaw()`](https://jisotalo.fi/ads-client/classes/Client.html#convertToRaw).
|
|
1231
|
+
|
|
1232
|
+
See my other library [iec-61131-3](https://github.com/jisotalo/iec-61131-3) for other possibilities to convert data between Javascript and IEC 61131-3 types.
|
|
1233
|
+
|
|
1234
|
+
### Converting a raw value to a Javascript object
|
|
1235
|
+
|
|
1236
|
+
Use [`convertFromRaw()`](https://jisotalo.fi/ads-client/classes/Client.html#convertFromRaw) to convert raw data to Javascript object.
|
|
1237
|
+
|
|
1238
|
+
**Converting INT**
|
|
1239
|
+
|
|
1240
|
+
```js
|
|
1241
|
+
const data = await client.readRaw(16448, 414816, 2);
|
|
1242
|
+
console.log(data); //<Buffer ff 7f>
|
|
995
1243
|
|
|
996
|
-
|
|
1244
|
+
const converted = await client.convertFromRaw(data, 'INT');
|
|
1245
|
+
console.log(converted); //32767
|
|
1246
|
+
```
|
|
997
1247
|
|
|
998
|
-
|
|
1248
|
+
**Converting STRUCT**
|
|
1249
|
+
|
|
1250
|
+
```js
|
|
1251
|
+
const converted = await client.convertFromRaw(data, 'ST_StandardTypes');
|
|
1252
|
+
console.log(converted);
|
|
1253
|
+
/*
|
|
1254
|
+
{
|
|
1255
|
+
BOOL_: true,
|
|
1256
|
+
BOOL_2: false,
|
|
1257
|
+
BYTE_: 255,
|
|
1258
|
+
WORD_: 65535,
|
|
1259
|
+
//..and so on
|
|
1260
|
+
}
|
|
1261
|
+
*/
|
|
1262
|
+
```
|
|
1263
|
+
|
|
1264
|
+
|
|
1265
|
+
### Converting a Javascript object to a raw value
|
|
1266
|
+
|
|
1267
|
+
Use [`convertToRaw()`](https://jisotalo.fi/ads-client/classes/Client.html#convertToRaw) to convert Javascript object to raw data.
|
|
1268
|
+
|
|
1269
|
+
**Converting INT**
|
|
1270
|
+
|
|
1271
|
+
```js
|
|
1272
|
+
const data = await client.convertToRaw(12345, 'INT');
|
|
1273
|
+
console.log(data); //<Buffer 39 30>
|
|
1274
|
+
```
|
|
1275
|
+
|
|
1276
|
+
**Converting STRUCT**
|
|
1277
|
+
|
|
1278
|
+
```js
|
|
1279
|
+
const value = {
|
|
1280
|
+
BOOL_: true,
|
|
1281
|
+
BOOL_2: false,
|
|
1282
|
+
BYTE_: 255,
|
|
1283
|
+
WORD_: 65535,
|
|
1284
|
+
//...and so on
|
|
1285
|
+
};
|
|
1286
|
+
|
|
1287
|
+
const data = await client.convertToRaw(value, 'ST_StandardTypes');
|
|
1288
|
+
console.log(data); //<Buffer ...>
|
|
1289
|
+
```
|
|
1290
|
+
|
|
1291
|
+
**Converting STRUCT (some properties only)**
|
|
1292
|
+
|
|
1293
|
+
All other (missing) properties are set to default values (zero / empty string).
|
|
1294
|
+
```js
|
|
1295
|
+
const value = {
|
|
1296
|
+
WORD_: 65535
|
|
1297
|
+
};
|
|
1298
|
+
|
|
1299
|
+
const data = await client.convertToRaw(value, 'ST_StandardTypes', true); //<-- NOTE: autoFill set
|
|
1300
|
+
console.log(data); //<Buffer ...>
|
|
1301
|
+
```
|
|
999
1302
|
|
|
1000
1303
|
## Other features
|
|
1001
1304
|
|
|
1002
|
-
|
|
1305
|
+
### ADS sum commands
|
|
1306
|
+
|
|
1307
|
+
ADS sum commands can be used to have multiple ADS commands in one request. This can be useful for efficiency reasons.
|
|
1308
|
+
|
|
1309
|
+
See [Beckhoff Information System](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_adsdll2/9007199379576075.html&id=9180083787138954512) for more info.
|
|
1310
|
+
|
|
1311
|
+
- [`createVariableHandleMulti()`](https://jisotalo.fi/ads-client/classes/Client.html#createVariableHandleMulti)
|
|
1312
|
+
- [`deleteVariableHandleMulti()`](https://jisotalo.fi/ads-client/classes/Client.html#deleteVariableHandleMulti)
|
|
1313
|
+
- [`readRawMulti()`](https://jisotalo.fi/ads-client/classes/Client.html#readRawMulti)
|
|
1314
|
+
- [`writeRawMulti()`](https://jisotalo.fi/ads-client/classes/Client.html#writeRawMulti)
|
|
1315
|
+
- [`readWriteRawMulti()`](https://jisotalo.fi/ads-client/classes/Client.html#readWriteRawMulti)
|
|
1316
|
+
|
|
1317
|
+
### Starting and stopping a PLC
|
|
1318
|
+
|
|
1319
|
+
- [`startPlc()`](https://jisotalo.fi/ads-client/classes/Client.html#startPlc)
|
|
1320
|
+
- [`stopPlc()`](https://jisotalo.fi/ads-client/classes/Client.html#stopPlc)
|
|
1321
|
+
- [`resetPlc()`](https://jisotalo.fi/ads-client/classes/Client.html#resetPlc)
|
|
1322
|
+
- [`restartPlc()`](https://jisotalo.fi/ads-client/classes/Client.html#restartPlc)
|
|
1323
|
+
|
|
1324
|
+
### Starting and stopping TwinCAT system
|
|
1325
|
+
|
|
1326
|
+
- [`setTcSystemToConfig()`](https://jisotalo.fi/ads-client/classes/Client.html#setTcSystemToConfig)
|
|
1327
|
+
- [`setTcSystemToRun()`](https://jisotalo.fi/ads-client/classes/Client.html#setTcSystemToRun)
|
|
1328
|
+
- [`restartTcSystem()`](https://jisotalo.fi/ads-client/classes/Client.html#restartTcSystem)
|
|
1329
|
+
|
|
1330
|
+
### Client events
|
|
1331
|
+
|
|
1332
|
+
See [`AdsClientEvents`](https://jisotalo.fi/ads-client/interfaces/AdsClientEvents.html) for all available events, their descriptions and examples.
|
|
1333
|
+
|
|
1334
|
+
```js
|
|
1335
|
+
client.on('connect', (connection) => {
|
|
1336
|
+
console.log('Connected:', connection);
|
|
1337
|
+
});
|
|
1338
|
+
```
|
|
1339
|
+
|
|
1340
|
+
### Debugging
|
|
1341
|
+
|
|
1342
|
+
Use [`setDebugLevel()`](https://jisotalo.fi/ads-client/classes/Client.html#setDebugLevel) to change debug level.
|
|
1343
|
+
|
|
1344
|
+
```js
|
|
1345
|
+
client.setDebugLevel(1);
|
|
1346
|
+
```
|
|
1347
|
+
|
|
1348
|
+
- 0: no debugging (default)
|
|
1349
|
+
- 1: basic debugging (`$env:DEBUG='ads-client'`)
|
|
1350
|
+
- 2: detailed debugging (`$env:DEBUG='ads-client,ads-client:details'`)
|
|
1351
|
+
- 3: detailed debugging with raw I/O data (`$env:DEBUG='ads-client,ads-client:details,ads-client:raw-data'`)
|
|
1352
|
+
|
|
1353
|
+
Debug data is available in the console (See [Debug](https://www.npmjs.com/package/debug) library for more).
|
|
1354
|
+
|
|
1003
1355
|
|
|
1004
1356
|
## Disconnecting
|
|
1005
1357
|
|
|
1006
|
-
After the client is no more used, always use [`disconnect()`](
|
|
1358
|
+
After the client is no more used, always use [`disconnect()`](https://jisotalo.fi/ads-client/classes/Client.html#disconnect) to release all subscription handles and other resources.
|
|
1007
1359
|
|
|
1008
1360
|
```js
|
|
1009
1361
|
await client.disconnect();
|
|
1010
1362
|
```
|
|
1011
1363
|
|
|
1012
|
-
##
|
|
1364
|
+
## Common issues and questions
|
|
1013
1365
|
|
|
1014
|
-
###
|
|
1366
|
+
### There are lot's of connection issues and timeouts
|
|
1015
1367
|
Things to try:
|
|
1016
1368
|
- Remove all TwinCAT routes and create them again (yes, really)
|
|
1017
|
-
- Increase value of [`timeoutDelay`](
|
|
1018
|
-
- Cache all data types and symbols straight after connecting using [`cacheDataTypes`](
|
|
1369
|
+
- Increase value of [`timeoutDelay`](https://jisotalo.fi/ads-client/interfaces/AdsClientSettings.html#timeoutDelay) setting
|
|
1370
|
+
- Cache all data types and symbols straight after connecting using [`cacheDataTypes`](https://jisotalo.fi/ads-client/classes/Client.html#cacheDataTypes) and [`cacheSymbols`](https://jisotalo.fi/ads-client/classes/Client.html#cacheSymbols)
|
|
1019
1371
|
|
|
1020
1372
|
### Getting `TypeError: Do not know how to serialize a BigInt`
|
|
1021
1373
|
- `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
|
|
1023
|
-
|
|
1374
|
+
- Check [this Github issue](https://github.com/GoogleChromeLabs/jsbi/issues/30#issuecomment-953187833) for the following patch:
|
|
1375
|
+
|
|
1376
|
+
```js
|
|
1377
|
+
BigInt.prototype.toJSON = function() { return this.toString() }
|
|
1378
|
+
```
|
|
1024
1379
|
|
|
1025
1380
|
### Can I connect from Raspberry Pi to TwinCAT?
|
|
1026
1381
|
|
|
@@ -1033,6 +1388,7 @@ Yes, for example using [Setup 3 - Connect from any Node.js system](#setup-3---co
|
|
|
1033
1388
|
|
|
1034
1389
|
- Make sure you have updated the latest PLC software using *download*. Sometimes online change causes this.
|
|
1035
1390
|
- If you are using TwinCAT 2, see chapter [Differences when using with TwinCAT 2](#differences-when-using-with-twincat-2)
|
|
1391
|
+
- Double check variable path for typos
|
|
1036
1392
|
|
|
1037
1393
|
### Having timeouts or 'mailbox is full' errors
|
|
1038
1394
|
|
|
@@ -1040,11 +1396,12 @@ Yes, for example using [Setup 3 - Connect from any Node.js system](#setup-3---co
|
|
|
1040
1396
|
- Other possible reason is that operating system TCP window is full because of large number of requests.
|
|
1041
1397
|
- Solution:
|
|
1042
1398
|
- Use structs or arrays to send data in larger packets
|
|
1043
|
-
- Try raw
|
|
1399
|
+
- Try raw commands or sum commands to decrease data usage
|
|
1044
1400
|
|
|
1045
1401
|
### Having problems to connect from OSX or Raspberry Pi to target PLC
|
|
1046
1402
|
|
|
1047
|
-
-
|
|
1403
|
+
- The local machine has no AMS router
|
|
1404
|
+
- You need to connect to the PLC's AMS router instead
|
|
1048
1405
|
- See [this issue comment](https://github.com/jisotalo/ads-client/issues/51#issuecomment-758016428)
|
|
1049
1406
|
|
|
1050
1407
|
### A data type is not found even when it should be
|
|
@@ -1057,9 +1414,9 @@ For example, when copying a variable name from TwinCAT online view using CTRL+C,
|
|
|
1057
1414
|
- --> **This causes error!**
|
|
1058
1415
|
- 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
1416
|
|
|
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
|
|
1417
|
+
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 locate the correct type.
|
|
1061
1418
|
|
|
1062
|
-
###
|
|
1419
|
+
### Connection failed - failed to set PLC connection
|
|
1063
1420
|
|
|
1064
1421
|
- The `targetAmsNetId` didn't contain a system manager service (port `10000`)
|
|
1065
1422
|
- The target is not a PLC and `rawClient` setting is not set
|
|
@@ -1077,7 +1434,7 @@ See also https://github.com/jisotalo/ads-client/issues/82
|
|
|
1077
1434
|
|
|
1078
1435
|
- EADDRNOTAVAIL: See above and https://github.com/jisotalo/ads-client/issues/82
|
|
1079
1436
|
|
|
1080
|
-
### How to connect to PLC that is in CONFIG mode?
|
|
1437
|
+
### How to connect to a PLC that is in CONFIG mode?
|
|
1081
1438
|
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
1439
|
|
|
1083
1440
|
`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"`
|
|
@@ -1094,7 +1451,13 @@ Another option is to use setting `rawClient: true` - see [Connecting to targets
|
|
|
1094
1451
|
|
|
1095
1452
|
## External links
|
|
1096
1453
|
|
|
1097
|
-
|
|
1454
|
+
| Description | Link |
|
|
1455
|
+
| -------------------------------------------- | ------------------------------------------------------- |
|
|
1456
|
+
| ADS client for Node-RED | https://github.com/jisotalo/node-red-contrib-ads-client |
|
|
1457
|
+
| ADS server for Node.js | https://github.com/jisotalo/ads-server |
|
|
1458
|
+
| IEC 61131-3 PLC data type helper for Node.js | https://github.com/jisotalo/iec-61131-3/ |
|
|
1459
|
+
| Codesys client for Node.js | https://github.com/jisotalo/codesys-client/ |
|
|
1460
|
+
|
|
1098
1461
|
|
|
1099
1462
|
## Library testing
|
|
1100
1463
|
|
|
@@ -1110,494 +1473,10 @@ PLC projects for running test suites are located in the following repository:
|
|
|
1110
1473
|
|
|
1111
1474
|
Tests are run with command `npm run test-tc3`. TwinCAT 3 test PLC projects needs to be running in the target system.
|
|
1112
1475
|
|
|
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
1476
|
### TwinCAT 2 tests
|
|
1385
1477
|
|
|
1386
1478
|
Tests are run with command `npm run test-tc2`. TwinCAT 2 test PLC projects needs to be running in the target system.
|
|
1387
1479
|
|
|
1388
|
-
TwinCAT 2 tests only have features that are supported by TC2.
|
|
1389
|
-
|
|
1390
|
-
**Results 28.09.2024:**
|
|
1391
|
-
|
|
1392
|
-
<details>
|
|
1393
|
-
<summary>Click to show test results</summary>
|
|
1394
|
-
<pre>
|
|
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.
|
|
1598
|
-
</pre>
|
|
1599
|
-
</details>
|
|
1600
|
-
|
|
1601
1480
|
# License
|
|
1602
1481
|
|
|
1603
1482
|
Licensed under [MIT License](http://www.opensource.org/licenses/MIT).
|