ads-client 2.0.0-beta.2 → 2.0.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +505 -254
- package/dist/ads-client.d.ts +627 -123
- package/dist/ads-client.js +747 -172
- package/dist/ads-client.js.map +1 -1
- package/dist/types/ads-client-types.d.ts +200 -17
- package/dist/types/ads-client-types.js.map +1 -1
- package/dist/types/ads-protocol-types.d.ts +75 -1
- package/dist/types/ads-protocol-types.js.map +1 -1
- package/package.json +3 -4
package/dist/ads-client.js
CHANGED
|
@@ -51,29 +51,39 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
51
51
|
};
|
|
52
52
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
53
|
exports.Client = exports.ADS = void 0;
|
|
54
|
-
const events_1 = __importDefault(require("events"));
|
|
55
54
|
const net_1 = require("net");
|
|
55
|
+
const events_1 = __importDefault(require("events"));
|
|
56
56
|
const debug_1 = __importDefault(require("debug"));
|
|
57
57
|
const long_1 = __importDefault(require("long"));
|
|
58
58
|
const ADS = __importStar(require("./ads-commons"));
|
|
59
59
|
const client_error_1 = __importDefault(require("./client-error"));
|
|
60
|
+
/**
|
|
61
|
+
* Common ADS constants and helper functions
|
|
62
|
+
*
|
|
63
|
+
* @category Client
|
|
64
|
+
*/
|
|
60
65
|
exports.ADS = __importStar(require("./ads-commons"));
|
|
61
66
|
/**
|
|
62
67
|
* A class for handling TwinCAT ADS protocol communication.
|
|
63
68
|
*
|
|
64
69
|
* Settings are provided in constructor - see {@link Client.constructor} and {@link AdsClientSettings}.
|
|
65
70
|
*
|
|
66
|
-
* A client instance should be created for each target
|
|
67
|
-
* can also be used to communicate with
|
|
71
|
+
* A client instance should be created for each target.
|
|
72
|
+
* However, a single client instance can also be used to communicate with
|
|
73
|
+
* multiple endpoints using the `targetOpts` parameter available in all methods.
|
|
68
74
|
*
|
|
69
|
-
*
|
|
75
|
+
* Client also emits different events, such as `client.on('connect', ...)`.
|
|
76
|
+
* See {@link AdsClientEvents} for all available events.
|
|
70
77
|
*
|
|
78
|
+
* @example
|
|
71
79
|
* ```js
|
|
72
80
|
* const client = new Client({
|
|
73
81
|
* targetAmsNetId: "192.168.4.1.1.1",
|
|
74
82
|
* targetAdsPort: 851
|
|
75
83
|
* });
|
|
76
84
|
* ```
|
|
85
|
+
*
|
|
86
|
+
* @category Client
|
|
77
87
|
*/
|
|
78
88
|
class Client extends events_1.default {
|
|
79
89
|
/**
|
|
@@ -222,7 +232,8 @@ class Client extends events_1.default {
|
|
|
222
232
|
connectionDownDelay: 5000,
|
|
223
233
|
allowHalfOpen: false,
|
|
224
234
|
rawClient: false,
|
|
225
|
-
disableCaching: false
|
|
235
|
+
disableCaching: false,
|
|
236
|
+
deleteUnknownSubscriptions: true
|
|
226
237
|
};
|
|
227
238
|
/**
|
|
228
239
|
* Active connection information.
|
|
@@ -247,6 +258,17 @@ class Client extends events_1.default {
|
|
|
247
258
|
};
|
|
248
259
|
}
|
|
249
260
|
;
|
|
261
|
+
/**
|
|
262
|
+
* Emits a warning, which results in a `warning` event, and a console message if `hideConsoleWarnings` is not set.
|
|
263
|
+
*
|
|
264
|
+
* @param message Warning message
|
|
265
|
+
*/
|
|
266
|
+
warn(message) {
|
|
267
|
+
if (!this.settings.hideConsoleWarnings) {
|
|
268
|
+
console.log(`WARNING: ${message}`);
|
|
269
|
+
}
|
|
270
|
+
this.emit('warning', message);
|
|
271
|
+
}
|
|
250
272
|
/**
|
|
251
273
|
* Clears given timer if it's available and increases the ID.
|
|
252
274
|
*
|
|
@@ -409,13 +431,13 @@ class Client extends events_1.default {
|
|
|
409
431
|
}
|
|
410
432
|
//allowHalfOpen allows this, but show some warnings..
|
|
411
433
|
if (!this.metaData.tcSystemState) {
|
|
412
|
-
|
|
434
|
+
this.warn(`"allowHalfOpen" setting is active. Target is connected but no connection to TwinCAT system. If target is not PLC runtime, use setting "rawClient" instead of "allowHalfOpen".`);
|
|
413
435
|
}
|
|
414
436
|
else if (this.metaData.tcSystemState.adsState !== ADS.ADS_STATE.Run) {
|
|
415
|
-
|
|
437
|
+
this.warn(`"allowHalfOpen" setting is active. Target is connected but TwinCAT system is in ${this.metaData.tcSystemState.adsStateStr} instead of run mode. No connection to PLC runtime.`);
|
|
416
438
|
}
|
|
417
439
|
else {
|
|
418
|
-
|
|
440
|
+
this.warn(`"allowHalfOpen" setting is active. No connection to PLC runtime. Check "targetAdsPort" setting and PLC status`);
|
|
419
441
|
}
|
|
420
442
|
}
|
|
421
443
|
}
|
|
@@ -488,6 +510,7 @@ class Client extends events_1.default {
|
|
|
488
510
|
connected: false
|
|
489
511
|
};
|
|
490
512
|
this.metaData = { ...this.defaultMetaData };
|
|
513
|
+
this.activeSubscriptions = {};
|
|
491
514
|
this.socket?.removeAllListeners();
|
|
492
515
|
this.socket?.destroy();
|
|
493
516
|
this.socket = undefined;
|
|
@@ -508,6 +531,7 @@ class Client extends events_1.default {
|
|
|
508
531
|
connected: false
|
|
509
532
|
};
|
|
510
533
|
this.metaData = { ...this.defaultMetaData };
|
|
534
|
+
this.activeSubscriptions = {};
|
|
511
535
|
this.socket?.removeAllListeners();
|
|
512
536
|
this.socket?.destroy();
|
|
513
537
|
this.socket = undefined;
|
|
@@ -525,6 +549,7 @@ class Client extends events_1.default {
|
|
|
525
549
|
connected: false
|
|
526
550
|
};
|
|
527
551
|
this.metaData = { ...this.defaultMetaData };
|
|
552
|
+
this.activeSubscriptions = {};
|
|
528
553
|
this.debug(`disconnectFromTarget(): Connection closing failed, connection was forced to close`);
|
|
529
554
|
this.emit("disconnect", isReconnecting);
|
|
530
555
|
return reject(new client_error_1.default(`disconnect(): Disconnected with errors: ${disconnectError.message}`, err));
|
|
@@ -555,11 +580,11 @@ class Client extends events_1.default {
|
|
|
555
580
|
//Trying to subscribe to previous subscriptions again
|
|
556
581
|
const failures = await this.restoreSubscriptions();
|
|
557
582
|
if (!failures.length) {
|
|
558
|
-
isReconnecting &&
|
|
583
|
+
isReconnecting && this.warn(`Reconnected and all subscriptions were restored!`);
|
|
559
584
|
this.debug(`reconnectToTarget(): Reconnected and all subscriptions were restored!`);
|
|
560
585
|
}
|
|
561
586
|
else {
|
|
562
|
-
|
|
587
|
+
this.warn(`Reconnected but failed to restore following subscriptions:\n - ${failures.join('\n - ')}`);
|
|
563
588
|
this.debug(`reconnectToTarget(): Reconnected but failed to restore following subscriptions: ${failures.join(', ')}`);
|
|
564
589
|
}
|
|
565
590
|
this.emit('reconnect', !failures.length, failures);
|
|
@@ -634,7 +659,7 @@ class Client extends events_1.default {
|
|
|
634
659
|
};
|
|
635
660
|
this.socket?.once('error', errorHandler);
|
|
636
661
|
try {
|
|
637
|
-
|
|
662
|
+
this.socketWrite(packet);
|
|
638
663
|
}
|
|
639
664
|
catch (err) {
|
|
640
665
|
this.socket?.off('error', errorHandler);
|
|
@@ -690,15 +715,45 @@ class Client extends events_1.default {
|
|
|
690
715
|
//Sometimes close event is not received, so resolve already here
|
|
691
716
|
this.socket.once('end', () => {
|
|
692
717
|
this.debugD(`unregisterAdsPort(): Socket connection ended, connection closed.`);
|
|
718
|
+
clearTimeout(this.portRegisterTimeoutTimer);
|
|
719
|
+
this.amsTcpCallback = undefined;
|
|
693
720
|
this.socket?.destroy();
|
|
694
721
|
resolve();
|
|
695
722
|
});
|
|
723
|
+
this.amsTcpCallback = (res) => {
|
|
724
|
+
this.amsTcpCallback = undefined;
|
|
725
|
+
clearTimeout(this.portRegisterTimeoutTimer);
|
|
726
|
+
if (res.amsTcp.command === ADS.AMS_HEADER_FLAG.AMS_TCP_PORT_CLOSE) {
|
|
727
|
+
this.debug(`unregisterAdsPort(): ADS port unregistered`);
|
|
728
|
+
this.socket?.destroy();
|
|
729
|
+
resolve();
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
//Timeout (if no answer from router)
|
|
733
|
+
this.portRegisterTimeoutTimer = setTimeout(() => {
|
|
734
|
+
//Callback is no longer needed, delete it
|
|
735
|
+
this.amsTcpCallback = undefined;
|
|
736
|
+
//Create a custom "ads error" so that the info is passed onwards
|
|
737
|
+
const adsError = {
|
|
738
|
+
ads: {
|
|
739
|
+
error: true,
|
|
740
|
+
errorCode: -1,
|
|
741
|
+
errorStr: `Timeout - no response in ${this.settings.timeoutDelay} ms`
|
|
742
|
+
}
|
|
743
|
+
};
|
|
744
|
+
this.debug(`unregisterAdsPort(): Failed to unregister ADS port: Timeout - no response in ${this.settings.timeoutDelay} ms`);
|
|
745
|
+
return reject(new client_error_1.default(`unregisterAdsPort(): Timeout - no response in ${this.settings.timeoutDelay} ms`, adsError));
|
|
746
|
+
}, this.settings.timeoutDelay);
|
|
696
747
|
try {
|
|
697
|
-
|
|
748
|
+
this.socketWrite(buffer);
|
|
698
749
|
}
|
|
699
750
|
catch (err) {
|
|
700
751
|
reject(err);
|
|
701
752
|
}
|
|
753
|
+
finally {
|
|
754
|
+
this.amsTcpCallback = undefined;
|
|
755
|
+
clearTimeout(this.portRegisterTimeoutTimer);
|
|
756
|
+
}
|
|
702
757
|
});
|
|
703
758
|
}
|
|
704
759
|
/**
|
|
@@ -906,7 +961,7 @@ class Client extends events_1.default {
|
|
|
906
961
|
await this.restoreSubscriptions();
|
|
907
962
|
}
|
|
908
963
|
catch (err) {
|
|
909
|
-
|
|
964
|
+
this.warn(`Target PLC symbol version changed and all subscriptions were not restored (data might be lost from now on). Error info: ${JSON.stringify(err)}`);
|
|
910
965
|
this.debug(`onPlcSymbolVersionChanged(): Failed to restore all subscriptions. Error: %o`, err);
|
|
911
966
|
}
|
|
912
967
|
}
|
|
@@ -920,7 +975,7 @@ class Client extends events_1.default {
|
|
|
920
975
|
* See also {@link Client.socketErrorHandler} which is `onSocketError.bind(this)`
|
|
921
976
|
*/
|
|
922
977
|
onSocketError(err) {
|
|
923
|
-
|
|
978
|
+
this.warn(`Socket connection to target closed to an an error, disconnecting: ${JSON.stringify(err)}`);
|
|
924
979
|
this.onConnectionLost(true);
|
|
925
980
|
}
|
|
926
981
|
/**
|
|
@@ -936,12 +991,12 @@ class Client extends events_1.default {
|
|
|
936
991
|
this.connection.connected = false;
|
|
937
992
|
this.emit('connectionLost', socketFailure);
|
|
938
993
|
if (this.settings.autoReconnect !== true) {
|
|
939
|
-
|
|
994
|
+
this.warn("Connection to target was lost and setting autoReconnect was false -> disconnecting");
|
|
940
995
|
await this.disconnectFromTarget(true).catch();
|
|
941
996
|
return;
|
|
942
997
|
}
|
|
943
998
|
this.socketConnectionLostHandler && this.socket?.off('close', this.socketConnectionLostHandler);
|
|
944
|
-
|
|
999
|
+
this.warn("Connection to target was lost. Trying to reconnect automatically...");
|
|
945
1000
|
const tryToReconnect = async (firstRetryAttempt, timerId) => {
|
|
946
1001
|
//If the timer has changed, quit here (to prevent multiple timers)
|
|
947
1002
|
if (this.reconnectionTimer.id !== timerId) {
|
|
@@ -958,7 +1013,7 @@ class Client extends events_1.default {
|
|
|
958
1013
|
//Reconnecting failed
|
|
959
1014
|
if (firstRetryAttempt) {
|
|
960
1015
|
this.debug(`onConnectionLost()/tryToReconnect(): Reconnecting failed, keeping trying in the background (${err.message}`);
|
|
961
|
-
|
|
1016
|
+
this.warn(`Reconnecting failed. Keeping trying in the background every ${this.settings.reconnectInterval} ms...`);
|
|
962
1017
|
}
|
|
963
1018
|
//If this is still a valid timer, start over again
|
|
964
1019
|
if (this.reconnectionTimer.id === timerId) {
|
|
@@ -1354,7 +1409,12 @@ class Client extends events_1.default {
|
|
|
1354
1409
|
//AMS port unregister
|
|
1355
1410
|
case ADS.AMS_HEADER_FLAG.AMS_TCP_PORT_CLOSE:
|
|
1356
1411
|
packet.amsTcp.commandStr = 'Port unregister';
|
|
1357
|
-
|
|
1412
|
+
if (this.amsTcpCallback) {
|
|
1413
|
+
this.amsTcpCallback(packet);
|
|
1414
|
+
}
|
|
1415
|
+
else {
|
|
1416
|
+
this.debug(`onAmsTcpPacketReceived(): Port unregister response received but no callback was assigned (${packet.amsTcp.commandStr})`);
|
|
1417
|
+
}
|
|
1358
1418
|
break;
|
|
1359
1419
|
//AMS port register
|
|
1360
1420
|
case ADS.AMS_HEADER_FLAG.AMS_TCP_PORT_CONNECT:
|
|
@@ -1434,12 +1494,24 @@ class Client extends events_1.default {
|
|
|
1434
1494
|
})
|
|
1435
1495
|
.catch(err => {
|
|
1436
1496
|
this.debug(`onAdsCommandReceived(): Notification received but parsing Javascript object failed: %o`, err);
|
|
1437
|
-
this.emit('client-error', new client_error_1.default(`
|
|
1497
|
+
this.emit('client-error', new client_error_1.default(`An ADS notification received but parsing data to a Javascript object failed. Subscription: ${JSON.stringify(subscription)}`, err));
|
|
1498
|
+
});
|
|
1499
|
+
}
|
|
1500
|
+
else if (this.settings.deleteUnknownSubscriptions) {
|
|
1501
|
+
this.debug(`onAdsCommandReceived(): An ADS notification with an unknown handle ${sample.notificationHandle} was received from ${key}. Trying to delete it to save resources (deleteUnknownSubscriptions is set).`);
|
|
1502
|
+
this.deleteNotificationHandle(sample.notificationHandle, packet.ams.sourceAmsAddress)
|
|
1503
|
+
.then(() => {
|
|
1504
|
+
this.debug(`onAdsCommandReceived(): An ADS notification with an unknown handle ${sample.notificationHandle} was received from ${key} and automatically deleted`);
|
|
1505
|
+
this.warn(`An ADS notification with an unknown handle ${sample.notificationHandle} was received from ${key} and automatically deleted`);
|
|
1506
|
+
})
|
|
1507
|
+
.catch(err => {
|
|
1508
|
+
this.debug(`onAdsCommandReceived(): Failed to delete an unknown ADS notification handle ${sample.notificationHandle} (from ${key}): %o`, err);
|
|
1509
|
+
this.emit('client-error', new client_error_1.default(`Failed to delete an unknown ADS notification handle ${sample.notificationHandle} (from ${key})`, err));
|
|
1438
1510
|
});
|
|
1439
1511
|
}
|
|
1440
1512
|
else {
|
|
1441
|
-
this.
|
|
1442
|
-
this.
|
|
1513
|
+
this.debug(`onAdsCommandReceived(): Notification received with unknown handle ${sample.notificationHandle} (${key})`);
|
|
1514
|
+
this.warn(`An ADS notification with an unknown handle ${sample.notificationHandle} was received from ${key}. Use unsubscribe() or deleteUnknownSubscriptions setting to save resources.`);
|
|
1443
1515
|
}
|
|
1444
1516
|
}
|
|
1445
1517
|
}
|
|
@@ -1454,7 +1526,7 @@ class Client extends events_1.default {
|
|
|
1454
1526
|
}
|
|
1455
1527
|
else {
|
|
1456
1528
|
this.debugD(`onAdsCommandReceived(): Ads command received with unknown invokeId "${packet.ams.invokeId}"`);
|
|
1457
|
-
this.emit('client-error', new client_error_1.default(`
|
|
1529
|
+
this.emit('client-error', new client_error_1.default(`Ads command received with unknown invokeId "${packet.ams.invokeId}"`));
|
|
1458
1530
|
}
|
|
1459
1531
|
break;
|
|
1460
1532
|
}
|
|
@@ -1486,7 +1558,7 @@ class Client extends events_1.default {
|
|
|
1486
1558
|
this.clearTimer(this.tcSystemStatePollerTimer);
|
|
1487
1559
|
this.debug("onRouterStateChanged(): Local loopback connection active, monitoring router state. Reconnecting when router is back running.");
|
|
1488
1560
|
if (this.metaData.routerState.state === ADS.AMS_ROUTER_STATE.START) {
|
|
1489
|
-
|
|
1561
|
+
this.warn(`Local AMS router state has changed to ${this.metaData.routerState.stateStr}. Reconnecting...`);
|
|
1490
1562
|
this.onConnectionLost();
|
|
1491
1563
|
}
|
|
1492
1564
|
else {
|
|
@@ -1859,7 +1931,7 @@ class Client extends events_1.default {
|
|
|
1859
1931
|
}
|
|
1860
1932
|
//If extended flag RefactorInfo is set
|
|
1861
1933
|
if ((symbol.extendedFlags & ADS.ADS_SYMBOL_FLAGS_2.RefactorInfo) === ADS.ADS_SYMBOL_FLAGS_2.RefactorInfo) {
|
|
1862
|
-
|
|
1934
|
+
this.warn(`Symbol ${symbol.name} (${symbol.type}) has extended flag "RefactorInfo" which is not supported. Things might not work now. Please open an issue at Github`);
|
|
1863
1935
|
}
|
|
1864
1936
|
//Reserved, if any
|
|
1865
1937
|
symbol.reserved = data.subarray(pos);
|
|
@@ -2164,7 +2236,7 @@ class Client extends events_1.default {
|
|
|
2164
2236
|
if ((dataType.flags & ADS.ADS_DATA_TYPE_FLAGS.RefactorInfo) === ADS.ADS_DATA_TYPE_FLAGS.RefactorInfo) {
|
|
2165
2237
|
//TODO: this is not working now
|
|
2166
2238
|
//Things probably break now
|
|
2167
|
-
|
|
2239
|
+
this.warn(`Data type ${dataType.name} (${dataType.type}) has flag "RefactorInfo" which is not supported. Things might not work now. Please open an issue at Github`);
|
|
2168
2240
|
}
|
|
2169
2241
|
//If flag ExtendedFlags set
|
|
2170
2242
|
if ((dataType.flags & ADS.ADS_DATA_TYPE_FLAGS.ExtendedFlags) === ADS.ADS_DATA_TYPE_FLAGS.ExtendedFlags) {
|
|
@@ -2181,13 +2253,13 @@ class Client extends events_1.default {
|
|
|
2181
2253
|
if ((dataType.flags & ADS.ADS_DATA_TYPE_FLAGS.ExtendedEnumInfos) === ADS.ADS_DATA_TYPE_FLAGS.ExtendedEnumInfos) {
|
|
2182
2254
|
//TODO: this is not working now
|
|
2183
2255
|
//Things probably break now
|
|
2184
|
-
|
|
2256
|
+
this.warn(`Data type ${dataType.name} (${dataType.type}) has flag "ExtendedEnumInfos" which is not supported. Things might not work now. Please open an issue at Github`);
|
|
2185
2257
|
}
|
|
2186
2258
|
//If flag SoftwareProtectionLevels set
|
|
2187
2259
|
if ((dataType.flags & ADS.ADS_DATA_TYPE_FLAGS.SoftwareProtectionLevels) === ADS.ADS_DATA_TYPE_FLAGS.SoftwareProtectionLevels) {
|
|
2188
2260
|
//TODO: this is not working now
|
|
2189
2261
|
//Things probably break now
|
|
2190
|
-
|
|
2262
|
+
this.warn(`Data type ${dataType.name} (${dataType.type}) has flag "SoftwareProtectionLevels" which is not supported. Things might not work now. Please open an issue at Github`);
|
|
2191
2263
|
}
|
|
2192
2264
|
//Reserved, if any
|
|
2193
2265
|
dataType.reserved = data.subarray(pos);
|
|
@@ -2288,6 +2360,7 @@ class Client extends events_1.default {
|
|
|
2288
2360
|
if (err.adsError && err.adsError?.errorCode === 1808) {
|
|
2289
2361
|
//Type wasn't found.
|
|
2290
2362
|
//Might be TwinCAT 2 system that doesn't provide pseudo or base data types by ADS --> check if we know the type ourselves
|
|
2363
|
+
const originalName = name;
|
|
2291
2364
|
if (ADS.BASE_DATA_TYPES.isPseudoType(name)) {
|
|
2292
2365
|
//This converts e.g. PVOID to a primitive type
|
|
2293
2366
|
if (knownSize === undefined) {
|
|
@@ -2321,6 +2394,11 @@ class Client extends events_1.default {
|
|
|
2321
2394
|
extendedFlags: 0,
|
|
2322
2395
|
reserved: Buffer.alloc(0)
|
|
2323
2396
|
};
|
|
2397
|
+
//Adding to cache, even though it's not read from the target
|
|
2398
|
+
//With TwinCAT 2, this prevents trying to read base types again and again from the target (as there aren't any)
|
|
2399
|
+
if (!this.settings.disableCaching && !targetOpts.adsPort && !targetOpts.amsNetId) {
|
|
2400
|
+
this.metaData.plcDataTypes[originalName.toLowerCase()] = dataType;
|
|
2401
|
+
}
|
|
2324
2402
|
}
|
|
2325
2403
|
else {
|
|
2326
2404
|
//Type is unknown - we can't do anything
|
|
@@ -2364,7 +2442,7 @@ class Client extends events_1.default {
|
|
|
2364
2442
|
}
|
|
2365
2443
|
}
|
|
2366
2444
|
else if ((((dataType.type === '' && !ADS.BASE_DATA_TYPES.isPseudoType(dataType.name)) || ADS.BASE_DATA_TYPES.isKnownType(dataType.name))
|
|
2367
|
-
&& dataType.flagsStr.includes('DataType')
|
|
2445
|
+
&& (dataType.flagsStr.includes('DataType') || ADS.BASE_DATA_TYPES.isKnownType(dataType.name)) //isKnownType() call is for TwinCAT 2 support, as base types (like INT16) do not have DataType flag (but it's the final base type)
|
|
2368
2446
|
&& !dataType.flagsStr.includes('EnumInfos')
|
|
2369
2447
|
&& dataType.arrayDimension === 0)) {
|
|
2370
2448
|
//This is final form (no need to go deeper)
|
|
@@ -2372,8 +2450,10 @@ class Client extends events_1.default {
|
|
|
2372
2450
|
}
|
|
2373
2451
|
else if (dataType.arrayDimension > 0) {
|
|
2374
2452
|
//Data type is an array - get array subtype
|
|
2453
|
+
//TwinCAT 2 support - calculate size if it's array of pointer etc.
|
|
2454
|
+
const size = dataType.size / dataType.arrayInfos.reduce((total, dimension) => total * dimension.length, 1);
|
|
2375
2455
|
builtType = {
|
|
2376
|
-
...(await this.buildDataType(dataType.type, targetOpts, false))
|
|
2456
|
+
...(await this.buildDataType(dataType.type, targetOpts, false, size))
|
|
2377
2457
|
};
|
|
2378
2458
|
builtType.arrayInfos = [...dataType.arrayInfos, ...builtType.arrayInfos];
|
|
2379
2459
|
}
|
|
@@ -2901,14 +2981,44 @@ class Client extends events_1.default {
|
|
|
2901
2981
|
* - {@link Client.unsubscribe}() - `DeleteNotification` command
|
|
2902
2982
|
* - {@link Client.readWriteRaw}() - `ReadWrite` command
|
|
2903
2983
|
*
|
|
2904
|
-
* @template T ADS response type. If omitted, generic {@link AdsResponse} type is used
|
|
2905
|
-
*
|
|
2906
|
-
* @throws Throws an error if sending the command fails or if target responds with an error
|
|
2907
|
-
*
|
|
2908
2984
|
* @example
|
|
2909
2985
|
* ```js
|
|
2910
|
-
*
|
|
2986
|
+
* try {
|
|
2987
|
+
* //Creating readRaw(16448, 414816, 2) command manually and sending it using sendAdsCommand()
|
|
2988
|
+
*
|
|
2989
|
+
* const data = Buffer.alloc(12);
|
|
2990
|
+
* let pos = 0;
|
|
2991
|
+
*
|
|
2992
|
+
* //0..3 IndexGroup
|
|
2993
|
+
* data.writeUInt32LE(16448, pos);
|
|
2994
|
+
* pos += 4;
|
|
2995
|
+
*
|
|
2996
|
+
* //4..7 IndexOffset
|
|
2997
|
+
* data.writeUInt32LE(414816, pos);
|
|
2998
|
+
* pos += 4;
|
|
2999
|
+
*
|
|
3000
|
+
* //8..11 Read data length
|
|
3001
|
+
* data.writeUInt32LE(2, pos);
|
|
3002
|
+
* pos += 4;
|
|
3003
|
+
*
|
|
3004
|
+
* const res = await this.sendAdsCommand<AdsReadResponse>({
|
|
3005
|
+
* adsCommand: ADS.ADS_COMMAND.Read,
|
|
3006
|
+
* targetAmsNetId: targetOpts.amsNetId,
|
|
3007
|
+
* targetAdsPort: targetOpts.adsPort,
|
|
3008
|
+
* payload: data
|
|
3009
|
+
* });
|
|
3010
|
+
*
|
|
3011
|
+
* console.log(res.ads.payload); //<Buffer ff 7f>
|
|
3012
|
+
*
|
|
3013
|
+
* } catch (err) {
|
|
3014
|
+
* console.log("Error:", err);
|
|
3015
|
+
* }
|
|
2911
3016
|
* ```
|
|
3017
|
+
*
|
|
3018
|
+
* @template T In Typescript, the type of the ADS response. If omitted, generic {@link AdsResponse} type is used.
|
|
3019
|
+
*
|
|
3020
|
+
* @throws Throws an error if sending the command fails or if target responds with an error
|
|
3021
|
+
*
|
|
2912
3022
|
*/
|
|
2913
3023
|
async sendAdsCommand(command) {
|
|
2914
3024
|
return new Promise(async (resolve, reject) => {
|
|
@@ -3221,7 +3331,7 @@ class Client extends events_1.default {
|
|
|
3221
3331
|
this.debug(`setTcSystemToRun(): TwinCAT system at ${this.targetToString(targetOpts)} set to run mode`);
|
|
3222
3332
|
if (reconnect) {
|
|
3223
3333
|
this.debug(`setTcSystemToRun(): Reconnecting after TwinCAT system restart`);
|
|
3224
|
-
|
|
3334
|
+
this.warn("Reconnecting after TwinCAT system restart");
|
|
3225
3335
|
this.onConnectionLost();
|
|
3226
3336
|
}
|
|
3227
3337
|
}
|
|
@@ -3300,7 +3410,7 @@ class Client extends events_1.default {
|
|
|
3300
3410
|
this.debug(`restartTcSystem(): TwinCAT system was restarted`);
|
|
3301
3411
|
}
|
|
3302
3412
|
/**
|
|
3303
|
-
* Returns symbol for given variable path, such as `GVL_Test.ExampleStruct`.
|
|
3413
|
+
* Returns a symbol object for given variable path, such as `GVL_Test.ExampleStruct`.
|
|
3304
3414
|
*
|
|
3305
3415
|
* Returns previously cached value if available, otherwise reads it from the target
|
|
3306
3416
|
* and caches it.
|
|
@@ -3314,7 +3424,7 @@ class Client extends events_1.default {
|
|
|
3314
3424
|
* @example
|
|
3315
3425
|
* ```js
|
|
3316
3426
|
* try {
|
|
3317
|
-
* const symbol = await client.getSymbol('
|
|
3427
|
+
* const symbol = await client.getSymbol('GVL_Read.StandardTypes.INT_');
|
|
3318
3428
|
* } catch (err) {
|
|
3319
3429
|
* console.log("Error:", err);
|
|
3320
3430
|
* }
|
|
@@ -3610,7 +3720,7 @@ class Client extends events_1.default {
|
|
|
3610
3720
|
* //Checks if value has changed every 100ms
|
|
3611
3721
|
* //Callback is called only when the value has changed
|
|
3612
3722
|
* await client.subscribeValue(
|
|
3613
|
-
* '
|
|
3723
|
+
* 'GVL_Subscription.NumericValue_10ms',
|
|
3614
3724
|
* (data, subscription) => {
|
|
3615
3725
|
* console.log(`Value of ${subscription.symbol.name} has changed: ${data.value}`);
|
|
3616
3726
|
* },
|
|
@@ -3671,7 +3781,7 @@ class Client extends events_1.default {
|
|
|
3671
3781
|
}
|
|
3672
3782
|
}
|
|
3673
3783
|
/**
|
|
3674
|
-
* Subscribes to raw value change notifications (ADS notifications) by raw ADS address (index group, index offset and data length).
|
|
3784
|
+
* Subscribes to raw value change notifications (ADS notifications) by a raw ADS address (index group, index offset and data length).
|
|
3675
3785
|
*
|
|
3676
3786
|
* Provided callback is called with the latest value when the value changes or when
|
|
3677
3787
|
* enough time has passed (depending on settings).
|
|
@@ -3758,7 +3868,7 @@ class Client extends events_1.default {
|
|
|
3758
3868
|
* //Checks if value has changed every 100ms
|
|
3759
3869
|
* //Callback is called only when the value has changed
|
|
3760
3870
|
* await client.subscribe({
|
|
3761
|
-
* target: '
|
|
3871
|
+
* target: 'GVL_Subscription.NumericValue_10ms',
|
|
3762
3872
|
* callback: (data, subscription) => {
|
|
3763
3873
|
* console.log(`Value of ${subscription.symbol.name} has changed: ${data.value}`);
|
|
3764
3874
|
* },
|
|
@@ -3830,19 +3940,8 @@ class Client extends events_1.default {
|
|
|
3830
3940
|
throw new client_error_1.default(`unsubscribe(): Client is not connected. Use connect() to connect to the target first.`);
|
|
3831
3941
|
}
|
|
3832
3942
|
this.debug(`unsubscribe(): Unsubscribing %o`, subscription);
|
|
3833
|
-
//Allocating bytes for request
|
|
3834
|
-
const data = Buffer.alloc(4);
|
|
3835
|
-
let pos = 0;
|
|
3836
|
-
//0..3 Notification handle
|
|
3837
|
-
data.writeUInt32LE(subscription.notificationHandle, pos);
|
|
3838
|
-
pos += 4;
|
|
3839
3943
|
try {
|
|
3840
|
-
|
|
3841
|
-
adsCommand: ADS.ADS_COMMAND.DeleteNotification,
|
|
3842
|
-
targetAmsNetId: subscription.remoteAddress.amsNetId,
|
|
3843
|
-
targetAdsPort: subscription.remoteAddress.adsPort,
|
|
3844
|
-
payload: data
|
|
3845
|
-
});
|
|
3944
|
+
await this.deleteNotificationHandle(subscription.notificationHandle, subscription.remoteAddress);
|
|
3846
3945
|
const address = ADS.amsAddressToString(subscription.remoteAddress);
|
|
3847
3946
|
if (this.activeSubscriptions[address]) {
|
|
3848
3947
|
delete this.activeSubscriptions[address][subscription.notificationHandle];
|
|
@@ -3854,6 +3953,33 @@ class Client extends events_1.default {
|
|
|
3854
3953
|
throw new client_error_1.default(`unsubscribe(): Unsubscribing failed`, err);
|
|
3855
3954
|
}
|
|
3856
3955
|
}
|
|
3956
|
+
/**
|
|
3957
|
+
* Sends a delete ADS notification command to the target system.
|
|
3958
|
+
*
|
|
3959
|
+
* Doesn't delete subscriptions, just sends the ADS command.
|
|
3960
|
+
*
|
|
3961
|
+
* @param notificationHandle Notification handle to delete (number)
|
|
3962
|
+
* @param targetAmsAddress Target system address
|
|
3963
|
+
*
|
|
3964
|
+
* @throws NOTE: Throws error if failed
|
|
3965
|
+
*/
|
|
3966
|
+
async deleteNotificationHandle(notificationHandle, targetAmsAddress) {
|
|
3967
|
+
this.debug(`deleteNotificationHandle(): Sending DeleteNotification command for handle ${notificationHandle} to ${ADS.amsAddressToString(targetAmsAddress)}`);
|
|
3968
|
+
//Allocating bytes for request
|
|
3969
|
+
const data = Buffer.alloc(4);
|
|
3970
|
+
let pos = 0;
|
|
3971
|
+
//0..3 Notification handle
|
|
3972
|
+
data.writeUInt32LE(notificationHandle, pos);
|
|
3973
|
+
pos += 4;
|
|
3974
|
+
const res = await this.sendAdsCommand({
|
|
3975
|
+
adsCommand: ADS.ADS_COMMAND.DeleteNotification,
|
|
3976
|
+
targetAmsNetId: targetAmsAddress.amsNetId,
|
|
3977
|
+
targetAdsPort: targetAmsAddress.adsPort,
|
|
3978
|
+
payload: data
|
|
3979
|
+
});
|
|
3980
|
+
this.debug(`deleteNotificationHandle(): Notification for handle ${notificationHandle} (${ADS.amsAddressToString(targetAmsAddress)}) deleted!`);
|
|
3981
|
+
return res.ads;
|
|
3982
|
+
}
|
|
3857
3983
|
/**
|
|
3858
3984
|
* Unsubscribes all active subscription (deletes all ADS notifications).
|
|
3859
3985
|
*
|
|
@@ -4268,16 +4394,30 @@ class Client extends events_1.default {
|
|
|
4268
4394
|
}
|
|
4269
4395
|
}
|
|
4270
4396
|
/**
|
|
4271
|
-
*
|
|
4397
|
+
* Reads raw data from the target system by a raw ADS address (index group, index offset and data length).
|
|
4398
|
+
*
|
|
4399
|
+
* This is the ADS protocol `Read` command.
|
|
4272
4400
|
*
|
|
4273
|
-
*
|
|
4401
|
+
* @example
|
|
4402
|
+
* ```js
|
|
4403
|
+
* try {
|
|
4404
|
+
* const data = await client.readRaw(16448, 414816, 2);
|
|
4405
|
+
* console.log(data); //<Buffer ff 7f>
|
|
4274
4406
|
*
|
|
4275
|
-
*
|
|
4407
|
+
* const converted = await client.convertFromRaw(data, 'INT');
|
|
4408
|
+
* console.log(converted); //32767
|
|
4409
|
+
*
|
|
4410
|
+
* } catch (err) {
|
|
4411
|
+
* console.log("Error:", err);
|
|
4412
|
+
* }
|
|
4413
|
+
* ```
|
|
4276
4414
|
*
|
|
4277
4415
|
* @param indexGroup Index group (address) of the data to read
|
|
4278
4416
|
* @param indexOffset Index offset (address) of the data to read
|
|
4279
4417
|
* @param size Data length to read (bytes)
|
|
4280
4418
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
4419
|
+
*
|
|
4420
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
4281
4421
|
*/
|
|
4282
4422
|
async readRaw(indexGroup, indexOffset, size, targetOpts = {}) {
|
|
4283
4423
|
if (!this.connection.connected) {
|
|
@@ -4312,16 +4452,28 @@ class Client extends events_1.default {
|
|
|
4312
4452
|
}
|
|
4313
4453
|
}
|
|
4314
4454
|
/**
|
|
4315
|
-
*
|
|
4455
|
+
* Writes raw data to the target system by a raw ADS address (index group, index offset and data length).
|
|
4456
|
+
*
|
|
4457
|
+
* This is the ADS protocol `Write` command.
|
|
4316
4458
|
*
|
|
4317
|
-
*
|
|
4459
|
+
* @example
|
|
4460
|
+
* ```js
|
|
4461
|
+
* try {
|
|
4462
|
+
* const data = await client.convertToRaw(32767, 'INT');
|
|
4463
|
+
* console.log(data); //<Buffer ff 7f>
|
|
4318
4464
|
*
|
|
4319
|
-
*
|
|
4465
|
+
* await client.writeRaw(16448, 414816, data);
|
|
4466
|
+
* } catch (err) {
|
|
4467
|
+
* console.log("Error:", err);
|
|
4468
|
+
* }
|
|
4469
|
+
* ```
|
|
4320
4470
|
*
|
|
4321
4471
|
* @param indexGroup Index group (address) of the data to write to
|
|
4322
4472
|
* @param indexOffset Index offset (address) of the data to write to
|
|
4323
4473
|
* @param value Data to write
|
|
4324
4474
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
4475
|
+
*
|
|
4476
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
4325
4477
|
*/
|
|
4326
4478
|
async writeRaw(indexGroup, indexOffset, value, targetOpts = {}) {
|
|
4327
4479
|
if (!this.connection.connected) {
|
|
@@ -4357,16 +4509,45 @@ class Client extends events_1.default {
|
|
|
4357
4509
|
}
|
|
4358
4510
|
}
|
|
4359
4511
|
/**
|
|
4360
|
-
*
|
|
4512
|
+
* Sends multiple `readRaw()` commands in one ADS packet.
|
|
4361
4513
|
*
|
|
4362
|
-
*
|
|
4514
|
+
* Reads raw data from the target system by a raw ADS addresses (index group, index offset and data length).
|
|
4515
|
+
* Results are returned for each command separately - see {@link ReadRawMultiResult} for details.
|
|
4363
4516
|
*
|
|
4364
|
-
*
|
|
4517
|
+
* Uses ADS sum command under the hood (better and faster performance).
|
|
4518
|
+
* See [Beckhoff Information System](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_adsdll2/9007199379576075.html&id=9180083787138954512) for more info.
|
|
4365
4519
|
*
|
|
4366
|
-
*
|
|
4520
|
+
* @example
|
|
4521
|
+
* ```js
|
|
4522
|
+
* try {
|
|
4523
|
+
* const results = await client.readRawMulti([
|
|
4524
|
+
* {
|
|
4525
|
+
* indexGroup: 16448,
|
|
4526
|
+
* indexOffset: 414816,
|
|
4527
|
+
* size: 2
|
|
4528
|
+
* },
|
|
4529
|
+
* {
|
|
4530
|
+
* indexGroup: 16448,
|
|
4531
|
+
* indexOffset: 414900,
|
|
4532
|
+
* size: 128
|
|
4533
|
+
* }
|
|
4534
|
+
* ]);
|
|
4535
|
+
*
|
|
4536
|
+
* if(results[0].success) {
|
|
4537
|
+
* console.log(`First result: ${results[0].value}`); //First result: <Buffer ff 7f>
|
|
4538
|
+
* } else {
|
|
4539
|
+
* console.log(`First read command failed: ${results[0].errorStr}`);
|
|
4540
|
+
* }
|
|
4541
|
+
*
|
|
4542
|
+
* } catch (err) {
|
|
4543
|
+
* console.log("Error:", err);
|
|
4544
|
+
* }
|
|
4545
|
+
* ```
|
|
4367
4546
|
*
|
|
4368
4547
|
* @param commands Array of read commands
|
|
4369
4548
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
4549
|
+
*
|
|
4550
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
4370
4551
|
*/
|
|
4371
4552
|
async readRawMulti(commands, targetOpts = {}) {
|
|
4372
4553
|
if (!this.connection.connected) {
|
|
@@ -4442,16 +4623,50 @@ class Client extends events_1.default {
|
|
|
4442
4623
|
}
|
|
4443
4624
|
}
|
|
4444
4625
|
/**
|
|
4445
|
-
*
|
|
4626
|
+
* Sends multiple `writeRaw()` commands in one ADS packet.
|
|
4627
|
+
*
|
|
4628
|
+
* Writes raw data to the target system by a raw ADS addresses (index group and index offset).
|
|
4629
|
+
* Results are returned for each command separately - see {@link WriteRawMultiResult} for details.
|
|
4446
4630
|
*
|
|
4447
|
-
*
|
|
4631
|
+
* Uses ADS sum command under the hood (better and faster performance).
|
|
4632
|
+
* See [Beckhoff Information System](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_adsdll2/9007199379576075.html&id=9180083787138954512) for more info.
|
|
4448
4633
|
*
|
|
4449
|
-
*
|
|
4634
|
+
* @example
|
|
4635
|
+
* ```js
|
|
4636
|
+
* try {
|
|
4637
|
+
* const data1 = await client.convertToRaw(32767, 'INT');
|
|
4638
|
+
* console.log(data1); //<Buffer ff 7f>
|
|
4639
|
+
*
|
|
4640
|
+
* const data2 = Buffer.alloc(128); //example
|
|
4641
|
+
*
|
|
4642
|
+
* const results = await client.writeRawMulti([
|
|
4643
|
+
* {
|
|
4644
|
+
* indexGroup: 16448,
|
|
4645
|
+
* indexOffset: 414816,
|
|
4646
|
+
* value: data1
|
|
4647
|
+
* },
|
|
4648
|
+
* {
|
|
4649
|
+
* indexGroup: 16448,
|
|
4650
|
+
* indexOffset: 414900,
|
|
4651
|
+
* value: data2
|
|
4652
|
+
* }
|
|
4653
|
+
* ]);
|
|
4654
|
+
*
|
|
4655
|
+
* if(results[0].success) {
|
|
4656
|
+
* console.log(`First write command successful`);
|
|
4657
|
+
* } else {
|
|
4658
|
+
* console.log(`First write command failed: ${results[0].errorStr}`);
|
|
4659
|
+
* }
|
|
4450
4660
|
*
|
|
4451
|
-
*
|
|
4661
|
+
* } catch (err) {
|
|
4662
|
+
* console.log("Error:", err);
|
|
4663
|
+
* }
|
|
4664
|
+
* ```
|
|
4452
4665
|
*
|
|
4453
4666
|
* @param commands Array of write commands
|
|
4454
4667
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
4668
|
+
*
|
|
4669
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
4455
4670
|
*/
|
|
4456
4671
|
async writeRawMulti(commands, targetOpts = {}) {
|
|
4457
4672
|
if (!this.connection.connected) {
|
|
@@ -4523,16 +4738,37 @@ class Client extends events_1.default {
|
|
|
4523
4738
|
}
|
|
4524
4739
|
}
|
|
4525
4740
|
/**
|
|
4526
|
-
*
|
|
4741
|
+
* Reads raw data from the target system by variable path (such as `GVL_Test.ExampleStruct`).
|
|
4527
4742
|
*
|
|
4528
|
-
*
|
|
4743
|
+
* Supports also reading `POINTER` and `REFERENCE` values (see example).
|
|
4529
4744
|
*
|
|
4530
|
-
*
|
|
4745
|
+
* This uses the ADS `READ_SYMVAL_BYNAME` under the hood, so only one
|
|
4746
|
+
* round-trip is needed.
|
|
4531
4747
|
*
|
|
4532
|
-
*
|
|
4748
|
+
* @example
|
|
4749
|
+
* ```js
|
|
4750
|
+
* try {
|
|
4751
|
+
* const data = await client.readRawByPath('GVL_Read.StandardTypes.INT_');
|
|
4752
|
+
* console.log(data); //<Buffer ff 7f>
|
|
4753
|
+
*
|
|
4754
|
+
* const converted = await client.convertFromRaw(data, 'INT');
|
|
4755
|
+
* console.log(converted); //32767
|
|
4756
|
+
*
|
|
4757
|
+
* //Reading a POINTER value (Note the dereference operator ^)
|
|
4758
|
+
* const ptrValue = await client.readRawByPath('GVL_Read.ComplexTypes.POINTER_^');
|
|
4759
|
+
*
|
|
4760
|
+
* //Reading a REFERENCE value
|
|
4761
|
+
* const refValue = await client.readRawByPath('GVL_Read.ComplexTypes.REFERENCE_');
|
|
4762
|
+
*
|
|
4763
|
+
* } catch (err) {
|
|
4764
|
+
* console.log("Error:", err);
|
|
4765
|
+
* }
|
|
4766
|
+
* ```
|
|
4533
4767
|
*
|
|
4534
4768
|
* @param path Variable path in the PLC to read (such as `GVL_Test.ExampleStruct`)
|
|
4535
4769
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
4770
|
+
*
|
|
4771
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
4536
4772
|
*/
|
|
4537
4773
|
async readRawByPath(path, targetOpts = {}) {
|
|
4538
4774
|
if (!this.connection.connected) {
|
|
@@ -4574,17 +4810,38 @@ class Client extends events_1.default {
|
|
|
4574
4810
|
}
|
|
4575
4811
|
}
|
|
4576
4812
|
/**
|
|
4577
|
-
*
|
|
4813
|
+
* Writes raw data to the target system by variable path (such as `GVL_Test.ExampleStruct`).
|
|
4578
4814
|
*
|
|
4579
|
-
*
|
|
4815
|
+
* Supports also writing `POINTER` and `REFERENCE` values (see example).
|
|
4580
4816
|
*
|
|
4581
|
-
*
|
|
4817
|
+
* NOTE: Unlike with {@link readRawByPath}(), this command uses multiple ADS requests for the operation.
|
|
4582
4818
|
*
|
|
4583
|
-
*
|
|
4819
|
+
* @example
|
|
4820
|
+
* ```js
|
|
4821
|
+
* try {
|
|
4822
|
+
* const data = await client.convertToRaw(32767, 'INT');
|
|
4823
|
+
* console.log(data); //<Buffer ff 7f>
|
|
4584
4824
|
*
|
|
4585
|
-
*
|
|
4825
|
+
* await client.writeRawByPath('GVL_Write.StandardTypes.INT_', data);
|
|
4826
|
+
*
|
|
4827
|
+
* //Writing a POINTER value (Note the dereference operator ^)
|
|
4828
|
+
* const ptrValue = ...
|
|
4829
|
+
* await client.writeRawByPath('GVL_Read.ComplexTypes.POINTER_^', ptrValue);
|
|
4830
|
+
*
|
|
4831
|
+
* //Writing a REFERENCE value
|
|
4832
|
+
* const refValue = ...
|
|
4833
|
+
* await client.readRawByPath('GVL_Read.ComplexTypes.REFERENCE_');
|
|
4834
|
+
*
|
|
4835
|
+
* } catch (err) {
|
|
4836
|
+
* console.log("Error:", err);
|
|
4837
|
+
* }
|
|
4838
|
+
* ```
|
|
4839
|
+
*
|
|
4840
|
+
* @param path Variable path in the PLC to write (such as `GVL_Test.ExampleStruct`)
|
|
4586
4841
|
* @param value Data to write
|
|
4587
4842
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
4843
|
+
*
|
|
4844
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
4588
4845
|
*/
|
|
4589
4846
|
async writeRawByPath(path, value, targetOpts = {}) {
|
|
4590
4847
|
if (!this.connection.connected) {
|
|
@@ -4594,12 +4851,12 @@ class Client extends events_1.default {
|
|
|
4594
4851
|
try {
|
|
4595
4852
|
let handle = null;
|
|
4596
4853
|
try {
|
|
4597
|
-
handle = await this.createVariableHandle(path);
|
|
4598
|
-
await this.writeRawByHandle(handle, value);
|
|
4854
|
+
handle = await this.createVariableHandle(path, targetOpts);
|
|
4855
|
+
await this.writeRawByHandle(handle, value, targetOpts);
|
|
4599
4856
|
}
|
|
4600
4857
|
finally {
|
|
4601
4858
|
if (handle) {
|
|
4602
|
-
await this.deleteVariableHandle(handle);
|
|
4859
|
+
await this.deleteVariableHandle(handle, targetOpts);
|
|
4603
4860
|
}
|
|
4604
4861
|
}
|
|
4605
4862
|
this.debug(`writeRawByPath(): Writing raw data to ${path} done (${value.byteLength} bytes)`);
|
|
@@ -4610,25 +4867,41 @@ class Client extends events_1.default {
|
|
|
4610
4867
|
}
|
|
4611
4868
|
}
|
|
4612
4869
|
/**
|
|
4613
|
-
*
|
|
4870
|
+
* Writes raw data to the target system by a raw ADS address (index group, index offset)
|
|
4871
|
+
* and reads the result as raw data.
|
|
4872
|
+
*
|
|
4873
|
+
* This is the ADS protocol `ReadWrite` command.
|
|
4614
4874
|
*
|
|
4615
|
-
*
|
|
4875
|
+
* @example
|
|
4876
|
+
* ```js
|
|
4877
|
+
* const { ADS } = require('../dist/ads-client');
|
|
4616
4878
|
*
|
|
4617
|
-
*
|
|
4879
|
+
* try {
|
|
4880
|
+
* //Reading raw value by symbol path (= same as readRawByPath())
|
|
4881
|
+
* const path = ADS.encodeStringToPlcStringBuffer('GVL_Read.StandardTypes.INT_');
|
|
4882
|
+
* const data = await client.readWriteRaw(ADS.ADS_RESERVED_INDEX_GROUPS.SymbolValueByName, 0, 0xFFFFFFFF, path);
|
|
4883
|
+
* console.log(data); //<Buffer ff 7f>
|
|
4884
|
+
*
|
|
4885
|
+
* } catch (err) {
|
|
4886
|
+
* console.log("Error:", err);
|
|
4887
|
+
* }
|
|
4888
|
+
* ```
|
|
4618
4889
|
*
|
|
4619
4890
|
* @param indexGroup Address index group
|
|
4620
4891
|
* @param indexOffset Address index offset
|
|
4621
|
-
* @param size
|
|
4622
|
-
* @param
|
|
4892
|
+
* @param size Data length to read (bytes)
|
|
4893
|
+
* @param value Value to write
|
|
4623
4894
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
4895
|
+
*
|
|
4896
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
4624
4897
|
*/
|
|
4625
|
-
async readWriteRaw(indexGroup, indexOffset, size,
|
|
4898
|
+
async readWriteRaw(indexGroup, indexOffset, size, value, targetOpts = {}) {
|
|
4626
4899
|
if (!this.connection.connected) {
|
|
4627
4900
|
throw new client_error_1.default(`readWriteRaw(): Client is not connected. Use connect() to connect to the target first.`);
|
|
4628
4901
|
}
|
|
4629
|
-
this.debug(`readWriteRaw(): Sending ReadWrite command (${JSON.stringify({ indexGroup, indexOffset, size })} with ${
|
|
4902
|
+
this.debug(`readWriteRaw(): Sending ReadWrite command (${JSON.stringify({ indexGroup, indexOffset, size })} with ${value.byteLength} bytes of data`);
|
|
4630
4903
|
//Allocating bytes for request
|
|
4631
|
-
const data = Buffer.alloc(16 +
|
|
4904
|
+
const data = Buffer.alloc(16 + value.byteLength);
|
|
4632
4905
|
let pos = 0;
|
|
4633
4906
|
//0..3 IndexGroup
|
|
4634
4907
|
data.writeUInt32LE(indexGroup, pos);
|
|
@@ -4640,11 +4913,11 @@ class Client extends events_1.default {
|
|
|
4640
4913
|
data.writeUInt32LE(size, pos);
|
|
4641
4914
|
pos += 4;
|
|
4642
4915
|
//12..15 Write data length
|
|
4643
|
-
data.writeUInt32LE(
|
|
4916
|
+
data.writeUInt32LE(value.byteLength, pos);
|
|
4644
4917
|
pos += 4;
|
|
4645
4918
|
//16..n Write data
|
|
4646
|
-
|
|
4647
|
-
pos +=
|
|
4919
|
+
value.copy(data, pos);
|
|
4920
|
+
pos += value.byteLength;
|
|
4648
4921
|
try {
|
|
4649
4922
|
const res = await this.sendAdsCommand({
|
|
4650
4923
|
adsCommand: ADS.ADS_COMMAND.ReadWrite,
|
|
@@ -4661,14 +4934,52 @@ class Client extends events_1.default {
|
|
|
4661
4934
|
}
|
|
4662
4935
|
}
|
|
4663
4936
|
/**
|
|
4664
|
-
*
|
|
4937
|
+
* Sends multiple `readWriteRaw()` commands in one ADS packet.
|
|
4938
|
+
*
|
|
4939
|
+
* Writes raw data to the target system by a raw ADS address (index group, index offset)
|
|
4940
|
+
* and reads the result as raw data.
|
|
4941
|
+
*
|
|
4942
|
+
* Uses ADS sum command under the hood (better and faster performance).
|
|
4943
|
+
* See [Beckhoff Information System](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_adsdll2/9007199379576075.html&id=9180083787138954512) for more info.
|
|
4944
|
+
*
|
|
4945
|
+
* @example
|
|
4946
|
+
* ```js
|
|
4947
|
+
* try {
|
|
4948
|
+
* //Reading raw values by symbol paths (= same as readRawByPath())
|
|
4949
|
+
* const path = ADS.encodeStringToPlcStringBuffer('GVL_Read.StandardTypes.INT_');
|
|
4950
|
+
* const path2 = ADS.encodeStringToPlcStringBuffer('GVL_Read.StandardTypes.REAL_');
|
|
4951
|
+
*
|
|
4952
|
+
* const results = await client.readWriteRawMulti([
|
|
4953
|
+
* {
|
|
4954
|
+
* indexGroup: ADS.ADS_RESERVED_INDEX_GROUPS.SymbolValueByName,
|
|
4955
|
+
* indexOffset: 0,
|
|
4956
|
+
* size: 0xFFFF,
|
|
4957
|
+
* value: path
|
|
4958
|
+
* },
|
|
4959
|
+
* {
|
|
4960
|
+
* indexGroup: ADS.ADS_RESERVED_INDEX_GROUPS.SymbolValueByName,
|
|
4961
|
+
* indexOffset: 0,
|
|
4962
|
+
* size: 0xFFFF,
|
|
4963
|
+
* value: path2
|
|
4964
|
+
* }
|
|
4965
|
+
* ]);
|
|
4966
|
+
*
|
|
4967
|
+
* if(results[0].success) {
|
|
4968
|
+
* console.log(`First result: ${results[0].value}`); //First result: <Buffer ff 7f>
|
|
4969
|
+
* } else {
|
|
4970
|
+
* console.log(`First read/write command failed: ${results[0].errorStr}`);
|
|
4971
|
+
* }
|
|
4665
4972
|
*
|
|
4666
|
-
*
|
|
4973
|
+
* } catch (err) {
|
|
4974
|
+
* console.log("Error:", err);
|
|
4975
|
+
* }
|
|
4667
4976
|
*
|
|
4668
|
-
*
|
|
4977
|
+
* ```
|
|
4669
4978
|
*
|
|
4670
|
-
* @param commands Array of read
|
|
4979
|
+
* @param commands Array of read/write commands
|
|
4671
4980
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
4981
|
+
*
|
|
4982
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
4672
4983
|
*/
|
|
4673
4984
|
async readWriteRawMulti(commands, targetOpts = {}) {
|
|
4674
4985
|
if (!this.connection.connected) {
|
|
@@ -4676,7 +4987,7 @@ class Client extends events_1.default {
|
|
|
4676
4987
|
}
|
|
4677
4988
|
this.debug(`readWriteRawMulti(): Sending ${commands.length} ReadWrite commands`);
|
|
4678
4989
|
const totalSize = commands.reduce((total, command) => total + command.size, 0);
|
|
4679
|
-
const totalWriteSize = commands.reduce((total, command) => total + command.
|
|
4990
|
+
const totalWriteSize = commands.reduce((total, command) => total + command.value.byteLength, 0);
|
|
4680
4991
|
//Allocating bytes for request
|
|
4681
4992
|
const data = Buffer.alloc(16 + commands.length * 16 + totalWriteSize);
|
|
4682
4993
|
let pos = 0;
|
|
@@ -4704,13 +5015,13 @@ class Client extends events_1.default {
|
|
|
4704
5015
|
data.writeUInt32LE(command.size, pos);
|
|
4705
5016
|
pos += 4;
|
|
4706
5017
|
//12..15 Write data length
|
|
4707
|
-
data.writeUInt32LE(command.
|
|
5018
|
+
data.writeUInt32LE(command.value.byteLength, pos);
|
|
4708
5019
|
pos += 4;
|
|
4709
5020
|
});
|
|
4710
5021
|
//data
|
|
4711
5022
|
commands.forEach(command => {
|
|
4712
|
-
command.
|
|
4713
|
-
pos += command.
|
|
5023
|
+
command.value.copy(data, pos);
|
|
5024
|
+
pos += command.value.byteLength;
|
|
4714
5025
|
});
|
|
4715
5026
|
try {
|
|
4716
5027
|
const res = await this.sendAdsCommand({
|
|
@@ -4759,16 +5070,32 @@ class Client extends events_1.default {
|
|
|
4759
5070
|
}
|
|
4760
5071
|
}
|
|
4761
5072
|
/**
|
|
4762
|
-
*
|
|
5073
|
+
* Reads variable's value from the target system by a variable path (such as `GVL_Test.ExampleStruct`)
|
|
5074
|
+
* and returns the value as a Javascript object.
|
|
4763
5075
|
*
|
|
4764
|
-
*
|
|
5076
|
+
* Returns variable's
|
|
5077
|
+
* - converted value
|
|
5078
|
+
* - raw value
|
|
5079
|
+
* - data type
|
|
5080
|
+
* - symbol
|
|
4765
5081
|
*
|
|
4766
|
-
*
|
|
5082
|
+
* **NOTE:** This requires that the target is a PLC runtime or has equivalent ADS protocol support.
|
|
4767
5083
|
*
|
|
4768
|
-
* @
|
|
5084
|
+
* @example
|
|
5085
|
+
* ```js
|
|
5086
|
+
* try {
|
|
5087
|
+
* const res = await client.readValue('GVL_Read.StandardTypes.INT_');
|
|
5088
|
+
* console.log(res.value);
|
|
5089
|
+
* } catch (err) {
|
|
5090
|
+
* console.log("Error:", err);
|
|
5091
|
+
* }
|
|
5092
|
+
* ```
|
|
5093
|
+
*
|
|
5094
|
+
* @param path Variable path in the PLC (such as `GVL_Test.ExampleStruct`)
|
|
4769
5095
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
4770
5096
|
*
|
|
4771
5097
|
* @template T In Typescript, the data type of the value, for example `readValue<number>(...)` or `readValue<ST_TypedStruct>(...)` (default: `any`)
|
|
5098
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
4772
5099
|
*/
|
|
4773
5100
|
async readValue(path, targetOpts = {}) {
|
|
4774
5101
|
if (!this.connection.connected) {
|
|
@@ -4789,7 +5116,8 @@ class Client extends events_1.default {
|
|
|
4789
5116
|
let dataType;
|
|
4790
5117
|
try {
|
|
4791
5118
|
this.debugD(`readValue(): Getting data type for ${path}`);
|
|
4792
|
-
|
|
5119
|
+
//Also passing size for TC2 pointer/pseudo type support
|
|
5120
|
+
dataType = await this.buildDataType(symbol.type, targetOpts, true, symbol.size);
|
|
4793
5121
|
}
|
|
4794
5122
|
catch (err) {
|
|
4795
5123
|
this.debug(`readValue(): Getting data type for ${path} failed: %o`, err);
|
|
@@ -4824,16 +5152,34 @@ class Client extends events_1.default {
|
|
|
4824
5152
|
};
|
|
4825
5153
|
}
|
|
4826
5154
|
/**
|
|
4827
|
-
*
|
|
5155
|
+
* Reads variable's value from the target system by a symbol object (acquired using `getSymbol()`)
|
|
5156
|
+
* and returns the value as a Javascript object.
|
|
4828
5157
|
*
|
|
4829
|
-
*
|
|
5158
|
+
* Returns variable's
|
|
5159
|
+
* - converted value
|
|
5160
|
+
* - raw value
|
|
5161
|
+
* - data type
|
|
5162
|
+
* - symbol
|
|
4830
5163
|
*
|
|
4831
|
-
*
|
|
5164
|
+
* **NOTE:** This requires that the target is a PLC runtime or has equivalent ADS protocol support.
|
|
5165
|
+
*
|
|
5166
|
+
* @example
|
|
5167
|
+
* ```js
|
|
5168
|
+
* try {
|
|
5169
|
+
* const symbol = await client.getSymbol('GVL_Read.StandardTypes.INT_');
|
|
4832
5170
|
*
|
|
4833
|
-
*
|
|
5171
|
+
* const res = await client.readValueBySymbol(symbol);
|
|
5172
|
+
* console.log(res.value);
|
|
5173
|
+
* } catch (err) {
|
|
5174
|
+
* console.log("Error:", err);
|
|
5175
|
+
* }
|
|
5176
|
+
* ```
|
|
5177
|
+
*
|
|
5178
|
+
* @param symbol Symbol object
|
|
4834
5179
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
4835
5180
|
*
|
|
4836
5181
|
* @template T In Typescript, the data type of the value, for example `readValueBySymbol<number>(...)` or `readValueBySymbol<ST_TypedStruct>(...)` (default: `any`)
|
|
5182
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
4837
5183
|
*/
|
|
4838
5184
|
async readValueBySymbol(symbol, targetOpts = {}) {
|
|
4839
5185
|
if (!this.connection.connected) {
|
|
@@ -4842,20 +5188,39 @@ class Client extends events_1.default {
|
|
|
4842
5188
|
return this.readValue(symbol.name, targetOpts);
|
|
4843
5189
|
}
|
|
4844
5190
|
/**
|
|
4845
|
-
*
|
|
5191
|
+
* Writes variable's value to the target system by a variable path (such as `GVL_Test.ExampleStruct`).
|
|
5192
|
+
* Converts the value from a Javascript object to a raw value.
|
|
4846
5193
|
*
|
|
4847
|
-
*
|
|
5194
|
+
* Returns variable's
|
|
5195
|
+
* - converted value
|
|
5196
|
+
* - raw value
|
|
5197
|
+
* - data type
|
|
5198
|
+
* - symbol
|
|
4848
5199
|
*
|
|
4849
|
-
*
|
|
5200
|
+
* **NOTE:** Do not use `autoFill` for `UNION` types, it doesn't work correctly.
|
|
4850
5201
|
*
|
|
4851
|
-
* **NOTE:**
|
|
5202
|
+
* **NOTE:** This requires that the target is a PLC runtime or has equivalent ADS protocol support.
|
|
4852
5203
|
*
|
|
4853
|
-
* @
|
|
5204
|
+
* @example
|
|
5205
|
+
* ```js
|
|
5206
|
+
* try {
|
|
5207
|
+
* const value = {
|
|
5208
|
+
* example: true
|
|
5209
|
+
* };
|
|
5210
|
+
*
|
|
5211
|
+
* const res = await client.writeValue('GVL_Read.StandardTypes.INT_', value);
|
|
5212
|
+
* } catch (err) {
|
|
5213
|
+
* console.log("Error:", err);
|
|
5214
|
+
* }
|
|
5215
|
+
* ```
|
|
5216
|
+
*
|
|
5217
|
+
* @param path Variable path in the PLC (such as `GVL_Test.ExampleStruct`)
|
|
4854
5218
|
* @param value Value to write
|
|
4855
5219
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
4856
|
-
* @param autoFill If
|
|
5220
|
+
* @param autoFill If set and the data type is a container (`STRUCT`, `FUNCTION_BLOCK` etc.), missing properties are automatically set to active values read from target (kept as-is).
|
|
4857
5221
|
*
|
|
4858
5222
|
* @template T In Typescript, the data type of the value, for example `writeValue<number>(...)` or `writeValue<ST_TypedStruct>(...)`
|
|
5223
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
4859
5224
|
*/
|
|
4860
5225
|
async writeValue(path, value, autoFill = false, targetOpts = {}) {
|
|
4861
5226
|
if (!this.connection.connected) {
|
|
@@ -4876,7 +5241,8 @@ class Client extends events_1.default {
|
|
|
4876
5241
|
let dataType;
|
|
4877
5242
|
try {
|
|
4878
5243
|
this.debugD(`writeValue(): Getting data type for ${path}`);
|
|
4879
|
-
|
|
5244
|
+
//Also passing size for TC2 pointer/pseudo type support
|
|
5245
|
+
dataType = await this.buildDataType(symbol.type, targetOpts, true, symbol.size);
|
|
4880
5246
|
}
|
|
4881
5247
|
catch (err) {
|
|
4882
5248
|
this.debug(`writeValue(): Getting data type for ${path} failed: %o`, err);
|
|
@@ -4934,20 +5300,37 @@ class Client extends events_1.default {
|
|
|
4934
5300
|
};
|
|
4935
5301
|
}
|
|
4936
5302
|
/**
|
|
4937
|
-
*
|
|
5303
|
+
* Writes variable's value to the target system by a symbol object (acquired using `getSymbol()`).
|
|
5304
|
+
* Converts the value from a Javascript object to a raw value.
|
|
4938
5305
|
*
|
|
4939
|
-
*
|
|
5306
|
+
* Returns variable's
|
|
5307
|
+
* - converted value
|
|
5308
|
+
* - raw value
|
|
5309
|
+
* - data type
|
|
5310
|
+
* - symbol
|
|
4940
5311
|
*
|
|
4941
|
-
*
|
|
5312
|
+
* **NOTE:** Do not use `autoFill` for `UNION` types, it doesn't work correctly.
|
|
4942
5313
|
*
|
|
4943
|
-
* **NOTE:**
|
|
5314
|
+
* **NOTE:** This requires that the target is a PLC runtime or has equivalent ADS protocol support.
|
|
5315
|
+
*
|
|
5316
|
+
* @example
|
|
5317
|
+
* ```js
|
|
5318
|
+
* try {
|
|
5319
|
+
* const symbol = await client.getSymbol('GVL_Read.StandardTypes.INT_');
|
|
4944
5320
|
*
|
|
4945
|
-
*
|
|
5321
|
+
* const res = await client.writeValueBySymbol(symbol, 32767);
|
|
5322
|
+
* } catch (err) {
|
|
5323
|
+
* console.log("Error:", err);
|
|
5324
|
+
* }
|
|
5325
|
+
* ```
|
|
5326
|
+
*
|
|
5327
|
+
* @param symbol Symbol object
|
|
4946
5328
|
* @param value Value to write
|
|
4947
5329
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
4948
|
-
* @param autoFill If
|
|
5330
|
+
* @param autoFill If set and the data type is a container (`STRUCT`, `FUNCTION_BLOCK` etc.), missing properties are automatically set to active values read from target (kept as-is).
|
|
4949
5331
|
*
|
|
4950
5332
|
* @template T In Typescript, the data type of the value, for example `writeValue<number>(...)` or `writeValue<ST_TypedStruct>(...)`
|
|
5333
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
4951
5334
|
*/
|
|
4952
5335
|
async writeValueBySymbol(symbol, value, autoFill = false, targetOpts = {}) {
|
|
4953
5336
|
if (!this.connection.connected) {
|
|
@@ -4956,14 +5339,27 @@ class Client extends events_1.default {
|
|
|
4956
5339
|
return this.writeValue(symbol.name, value, autoFill, targetOpts);
|
|
4957
5340
|
}
|
|
4958
5341
|
/**
|
|
4959
|
-
* **TODO - DOCUMENTATION ONGOING**
|
|
4960
|
-
*
|
|
4961
5342
|
* Returns a default (empty) Javascript object representing provided PLC data type.
|
|
4962
5343
|
*
|
|
4963
|
-
* @
|
|
5344
|
+
* @example
|
|
5345
|
+
* ```js
|
|
5346
|
+
* try {
|
|
5347
|
+
* const res = await client.getDefaultPlcObject('INT');
|
|
5348
|
+
* console.log(res); //0
|
|
5349
|
+
|
|
5350
|
+
* const res2 = await client.getDefaultPlcObject('Tc2_Standard.TON');
|
|
5351
|
+
* console.log(res2); //{ IN: false, PT: 0, Q: false, ET: 0, M: false, StartTime: 0 }
|
|
5352
|
+
*
|
|
5353
|
+
* } catch (err) {
|
|
5354
|
+
* console.log("Error:", err);
|
|
5355
|
+
* }
|
|
5356
|
+
* ```
|
|
5357
|
+
*
|
|
5358
|
+
* @param dataType Data type name in the PLC as string (such as `ST_Struct`) or data type object (acquired using {@link getDataType}())
|
|
4964
5359
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
4965
5360
|
*
|
|
4966
|
-
* @template T Typescript data type of the PLC data, for example `
|
|
5361
|
+
* @template T Typescript data type of the PLC data, for example `getDefaultPlcObject<number>(...)` or `getDefaultPlcObject<ST_TypedStruct>(...)`
|
|
5362
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
4967
5363
|
*/
|
|
4968
5364
|
async getDefaultPlcObject(dataType, targetOpts = {}) {
|
|
4969
5365
|
if (!this.connection.connected) {
|
|
@@ -4987,15 +5383,28 @@ class Client extends events_1.default {
|
|
|
4987
5383
|
return value;
|
|
4988
5384
|
}
|
|
4989
5385
|
/**
|
|
4990
|
-
*
|
|
5386
|
+
* Converts raw data to a Javascript object by using the provided data type.
|
|
5387
|
+
*
|
|
5388
|
+
* @example
|
|
5389
|
+
* ```js
|
|
5390
|
+
* try {
|
|
5391
|
+
* const data = await client.readRaw(16448, 414816, 2);
|
|
5392
|
+
* console.log(data); //<Buffer ff 7f>
|
|
4991
5393
|
*
|
|
4992
|
-
*
|
|
5394
|
+
* const converted = await client.convertFromRaw(data, 'INT');
|
|
5395
|
+
* console.log(converted); //32767
|
|
4993
5396
|
*
|
|
4994
|
-
*
|
|
4995
|
-
*
|
|
5397
|
+
* } catch (err) {
|
|
5398
|
+
* console.log("Error:", err);
|
|
5399
|
+
* }
|
|
5400
|
+
* ```
|
|
5401
|
+
*
|
|
5402
|
+
* @param data Raw data (acquired for example using {@link readRaw}())
|
|
5403
|
+
* @param dataType Data type name in the PLC as string (such as `ST_Struct`) or data type object (acquired using {@link getDataType}())
|
|
4996
5404
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
4997
5405
|
*
|
|
4998
5406
|
* @template T Typescript data type of the PLC data, for example `convertFromRaw<number>(...)` or `convertFromRaw<ST_TypedStruct>(...)`
|
|
5407
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
4999
5408
|
*/
|
|
5000
5409
|
async convertFromRaw(data, dataType, targetOpts = {}) {
|
|
5001
5410
|
if (!this.connection.connected) {
|
|
@@ -5027,18 +5436,27 @@ class Client extends events_1.default {
|
|
|
5027
5436
|
return value;
|
|
5028
5437
|
}
|
|
5029
5438
|
/**
|
|
5030
|
-
*
|
|
5439
|
+
* Converts a Javascript object to raw data by using the provided data type.
|
|
5031
5440
|
*
|
|
5032
|
-
*
|
|
5441
|
+
* **NOTE:** Do not use `autoFill` for `UNION` types, it doesn't work correctly.
|
|
5442
|
+
|
|
5443
|
+
* @example
|
|
5444
|
+
* ```js
|
|
5445
|
+
* try {
|
|
5446
|
+
* const data = await client.convertToRaw(32767, 'INT');
|
|
5447
|
+
* console.log(data); //<Buffer ff 7f>
|
|
5033
5448
|
*
|
|
5034
|
-
*
|
|
5449
|
+
* } catch (err) {
|
|
5450
|
+
* console.log("Error:", err);
|
|
5451
|
+
* }
|
|
5452
|
+
* ```
|
|
5035
5453
|
*
|
|
5036
|
-
* @param value
|
|
5037
|
-
* @param dataType Data type name in the PLC as string (such as `ST_Struct`) or
|
|
5038
|
-
* @param autoFill If
|
|
5454
|
+
* @param value Value to convert
|
|
5455
|
+
* @param dataType Data type name in the PLC as string (such as `ST_Struct`) or data type object (acquired using {@link getDataType}())
|
|
5456
|
+
* @param autoFill autoFill If set and the data type is a container (`STRUCT`, `FUNCTION_BLOCK` etc.), missing properties are automatically set to default values (`0` or empty string).
|
|
5039
5457
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
5040
5458
|
*
|
|
5041
|
-
* @
|
|
5459
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5042
5460
|
*/
|
|
5043
5461
|
async convertToRaw(value, dataType, autoFill = false, targetOpts = {}) {
|
|
5044
5462
|
if (!this.connection.connected) {
|
|
@@ -5094,14 +5512,39 @@ class Client extends events_1.default {
|
|
|
5094
5512
|
}
|
|
5095
5513
|
}
|
|
5096
5514
|
/**
|
|
5097
|
-
*
|
|
5515
|
+
* Creates a handle to a variable at the target system by variable path (such as `GVL_Test.ExampleStruct`).
|
|
5098
5516
|
*
|
|
5099
|
-
*
|
|
5517
|
+
* The handle can be then used for reading and writing the value.
|
|
5100
5518
|
*
|
|
5101
|
-
*
|
|
5519
|
+
* Reading and writing dereferenced `POINTER` and `REFERENCE` values is also possible.
|
|
5520
|
+
* See {@link readRawByHandle}() and {@link writeRawByHandle}().
|
|
5102
5521
|
*
|
|
5103
|
-
*
|
|
5104
|
-
*
|
|
5522
|
+
* NOTE: The handle should be deleted after it's no longer needed,
|
|
5523
|
+
* as there is a limited amount of handles available. See {@link deleteVariableHandle}().
|
|
5524
|
+
*
|
|
5525
|
+
* @example
|
|
5526
|
+
* ```js
|
|
5527
|
+
* try {
|
|
5528
|
+
* //POINTER value (Note the dereference operator ^)
|
|
5529
|
+
* const handle1 = await client.createVariableHandle('GVL_Read.ComplexTypes.POINTER_^');
|
|
5530
|
+
* const value = await client.readRawByHandle(handle1);
|
|
5531
|
+
* await client.deleteVariableHandle(handle1);
|
|
5532
|
+
*
|
|
5533
|
+
* const handle2 = await client.createVariableHandle('GVL_Read.StandardTypes.INT_');
|
|
5534
|
+
* const value2 = await client.readRawByHandle(handle2);
|
|
5535
|
+
* await client.deleteVariableHandle(handle2);
|
|
5536
|
+
*
|
|
5537
|
+
* //Now you use convertFromRaw() to get actual values
|
|
5538
|
+
*
|
|
5539
|
+
* } catch (err) {
|
|
5540
|
+
* console.log("Error:", err);
|
|
5541
|
+
* }
|
|
5542
|
+
* ```
|
|
5543
|
+
*
|
|
5544
|
+
* @param path Variable path in the PLC to read (such as `GVL_Test.ExampleStruct`)
|
|
5545
|
+
* @param targetOpts Optional target settings that override values in `settings`
|
|
5546
|
+
*
|
|
5547
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5105
5548
|
*/
|
|
5106
5549
|
async createVariableHandle(path, targetOpts = {}) {
|
|
5107
5550
|
if (!this.connection.connected) {
|
|
@@ -5159,18 +5602,44 @@ class Client extends events_1.default {
|
|
|
5159
5602
|
}
|
|
5160
5603
|
}
|
|
5161
5604
|
/**
|
|
5162
|
-
*
|
|
5605
|
+
* Sends multiple {@link createVariableHandle}() commands in one ADS packet.
|
|
5163
5606
|
*
|
|
5164
|
-
*
|
|
5607
|
+
* Creates a handle to a variable at the target system by variable path (such as `GVL_Test.ExampleStruct`).
|
|
5165
5608
|
*
|
|
5166
|
-
*
|
|
5609
|
+
* The handle can be then used for reading and writing the value.
|
|
5167
5610
|
*
|
|
5168
|
-
*
|
|
5611
|
+
* Reading and writing dereferenced `POINTER` and `REFERENCE` values is also possible.
|
|
5612
|
+
* See {@link readRawByHandle}() and {@link writeRawByHandle}().
|
|
5169
5613
|
*
|
|
5170
|
-
*
|
|
5614
|
+
* NOTE: The handle should be deleted after it's no longer needed,
|
|
5615
|
+
* as there is a limited amount of handles available. See {@link deleteVariableHandle}().
|
|
5616
|
+
*
|
|
5617
|
+
* Uses ADS sum command under the hood (better and faster performance).
|
|
5618
|
+
* See [Beckhoff Information System](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_adsdll2/9007199379576075.html&id=9180083787138954512) for more info.
|
|
5619
|
+
*
|
|
5620
|
+
* @example
|
|
5621
|
+
* ```js
|
|
5622
|
+
* try {
|
|
5623
|
+
* const results = await client.createVariableHandleMulti([
|
|
5624
|
+
* 'GVL_Read.StandardTypes.INT_',
|
|
5625
|
+
* 'GVL_Read.StandardTypes.REAL_'
|
|
5626
|
+
* ]);
|
|
5171
5627
|
*
|
|
5172
|
-
*
|
|
5628
|
+
* if(results[0].success) {
|
|
5629
|
+
* console.log(`First handle: ${results[0].handle}`);
|
|
5630
|
+
* } else {
|
|
5631
|
+
* console.log(`Creating first handle failed: ${results[0].errorStr}`);
|
|
5632
|
+
* }
|
|
5633
|
+
*
|
|
5634
|
+
* } catch (err) {
|
|
5635
|
+
* console.log("Error:", err);
|
|
5636
|
+
* }
|
|
5637
|
+
* ```
|
|
5638
|
+
*
|
|
5639
|
+
* @param paths Array of variable paths in the PLC to read (such as `GVL_Test.ExampleStruct`)
|
|
5173
5640
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
5641
|
+
*
|
|
5642
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5174
5643
|
*/
|
|
5175
5644
|
async createVariableHandleMulti(paths, targetOpts = {}) {
|
|
5176
5645
|
if (!this.connection.connected) {
|
|
@@ -5275,12 +5744,26 @@ class Client extends events_1.default {
|
|
|
5275
5744
|
}
|
|
5276
5745
|
}
|
|
5277
5746
|
/**
|
|
5278
|
-
*
|
|
5747
|
+
* Deletes a variable handle that was previously created
|
|
5748
|
+
* using {@link createVariableHandle}().
|
|
5749
|
+
*
|
|
5750
|
+
* @example
|
|
5751
|
+
* ```js
|
|
5752
|
+
* try {
|
|
5753
|
+
* const handle = createVariableHandle(...);
|
|
5279
5754
|
*
|
|
5280
|
-
*
|
|
5755
|
+
* //After use, deleting the handle
|
|
5756
|
+
* await client.deleteVariableHandle(handle);
|
|
5757
|
+
*
|
|
5758
|
+
* } catch (err) {
|
|
5759
|
+
* console.log("Error:", err);
|
|
5760
|
+
* }
|
|
5761
|
+
* ```
|
|
5281
5762
|
*
|
|
5282
5763
|
* @param handle Variable handle to delete
|
|
5283
|
-
* @param targetOpts Optional target settings that override values in `settings`
|
|
5764
|
+
* @param targetOpts Optional target settings that override values in `settings`
|
|
5765
|
+
*
|
|
5766
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5284
5767
|
*/
|
|
5285
5768
|
async deleteVariableHandle(handle, targetOpts = {}) {
|
|
5286
5769
|
if (!this.connection.connected) {
|
|
@@ -5318,16 +5801,38 @@ class Client extends events_1.default {
|
|
|
5318
5801
|
}
|
|
5319
5802
|
}
|
|
5320
5803
|
/**
|
|
5321
|
-
*
|
|
5804
|
+
* Sends multiple {@link deleteVariableHandle}() commands in one ADS packet.
|
|
5805
|
+
*
|
|
5806
|
+
* Deletes a variable handle that was previously created
|
|
5807
|
+
* using {@link createVariableHandle}().
|
|
5808
|
+
*
|
|
5809
|
+
* Uses ADS sum command under the hood (better and faster performance).
|
|
5810
|
+
* See [Beckhoff Information System](https://infosys.beckhoff.com/english.php?content=../content/1033/tc3_adsdll2/9007199379576075.html&id=9180083787138954512) for more info.
|
|
5322
5811
|
*
|
|
5323
|
-
*
|
|
5812
|
+
* @example
|
|
5813
|
+
* ```js
|
|
5814
|
+
* try {
|
|
5815
|
+
* const handle1 = createVariableHandle(...);
|
|
5816
|
+
* const handle2 = createVariableHandle(...);
|
|
5324
5817
|
*
|
|
5325
|
-
*
|
|
5818
|
+
* //After use, deleting the handles
|
|
5819
|
+
* const results = await client.deleteVariableHandleMulti([handle1, handle2]);
|
|
5326
5820
|
*
|
|
5327
|
-
*
|
|
5821
|
+
* if(results[0].success) {
|
|
5822
|
+
* console.log(`First deleted`);
|
|
5823
|
+
* } else {
|
|
5824
|
+
* console.log(`Deleting first handle failed: ${results[0].errorStr}`);
|
|
5825
|
+
* }
|
|
5826
|
+
*
|
|
5827
|
+
* } catch (err) {
|
|
5828
|
+
* console.log("Error:", err);
|
|
5829
|
+
* }
|
|
5830
|
+
* ```
|
|
5328
5831
|
*
|
|
5329
5832
|
* @param handles Array of variable handles to delete
|
|
5330
5833
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
5834
|
+
*
|
|
5835
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5331
5836
|
*/
|
|
5332
5837
|
async deleteVariableHandleMulti(handles, targetOpts = {}) {
|
|
5333
5838
|
if (!this.connection.connected) {
|
|
@@ -5399,13 +5904,24 @@ class Client extends events_1.default {
|
|
|
5399
5904
|
}
|
|
5400
5905
|
}
|
|
5401
5906
|
/**
|
|
5402
|
-
*
|
|
5907
|
+
* Reads raw data from the target system by a previously created variable handle (acquired using {@link createVariableHandle}())
|
|
5403
5908
|
*
|
|
5404
|
-
*
|
|
5909
|
+
* @example
|
|
5910
|
+
* ```js
|
|
5911
|
+
* try {
|
|
5912
|
+
* const handle = await client.createVariableHandle('GVL_Read.StandardTypes.INT_'');
|
|
5913
|
+
* const value = await client.readRawByHandle(handle);
|
|
5914
|
+
* await client.deleteVariableHandle(handle);
|
|
5405
5915
|
*
|
|
5916
|
+
* } catch (err) {
|
|
5917
|
+
* console.log("Error:", err);
|
|
5918
|
+
* }
|
|
5919
|
+
* ```
|
|
5406
5920
|
* @param handle Variable handle
|
|
5407
5921
|
* @param size Optional data length to read (bytes) - as default, size in handle is used if available. Uses 0xFFFFFFFF as fallback.
|
|
5408
|
-
* @param targetOpts Optional target settings that override values in `settings`
|
|
5922
|
+
* @param targetOpts Optional target settings that override values in `settings`
|
|
5923
|
+
*
|
|
5924
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5409
5925
|
*/
|
|
5410
5926
|
async readRawByHandle(handle, size, targetOpts = {}) {
|
|
5411
5927
|
if (!this.connection.connected) {
|
|
@@ -5429,13 +5945,28 @@ class Client extends events_1.default {
|
|
|
5429
5945
|
}
|
|
5430
5946
|
}
|
|
5431
5947
|
/**
|
|
5432
|
-
*
|
|
5948
|
+
* Writes raw data to the target system by a previously created variable handle (acquired using {@link createVariableHandle}())
|
|
5433
5949
|
*
|
|
5434
|
-
*
|
|
5950
|
+
* @example
|
|
5951
|
+
* ```js
|
|
5952
|
+
* try {
|
|
5953
|
+
* const value = await client.convertToRaw(32767, 'INT');
|
|
5954
|
+
* console.log(value); //<Buffer ff 7f>
|
|
5955
|
+
*
|
|
5956
|
+
* const handle = await client.createVariableHandle('GVL_Read.StandardTypes.INT_');
|
|
5957
|
+
* await client.writeRawByHandle(handle, value);
|
|
5958
|
+
* await client.deleteVariableHandle(handle);
|
|
5959
|
+
*
|
|
5960
|
+
* } catch (err) {
|
|
5961
|
+
* console.log("Error:", err);
|
|
5962
|
+
* }
|
|
5963
|
+
* ```
|
|
5435
5964
|
*
|
|
5436
5965
|
* @param handle Variable handle
|
|
5437
5966
|
* @param value Data to write
|
|
5438
|
-
* @param targetOpts Optional target settings that override values in `settings`
|
|
5967
|
+
* @param targetOpts Optional target settings that override values in `settings`
|
|
5968
|
+
*
|
|
5969
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5439
5970
|
*/
|
|
5440
5971
|
async writeRawByHandle(handle, value, targetOpts = {}) {
|
|
5441
5972
|
if (!this.connection.connected) {
|
|
@@ -5453,12 +5984,23 @@ class Client extends events_1.default {
|
|
|
5453
5984
|
}
|
|
5454
5985
|
}
|
|
5455
5986
|
/**
|
|
5456
|
-
*
|
|
5987
|
+
* Reads raw data from the target system by a symbol object (acquired using `getSymbol()`)
|
|
5457
5988
|
*
|
|
5458
|
-
*
|
|
5989
|
+
* @example
|
|
5990
|
+
* ```js
|
|
5991
|
+
* try {
|
|
5992
|
+
* const symbol = await client.getSymbol('GVL_Read.StandardTypes.INT_');
|
|
5993
|
+
* const value = await client.readRawBySymbol(symbol);
|
|
5459
5994
|
*
|
|
5460
|
-
*
|
|
5461
|
-
*
|
|
5995
|
+
* } catch (err) {
|
|
5996
|
+
* console.log("Error:", err);
|
|
5997
|
+
* }
|
|
5998
|
+
* ```
|
|
5999
|
+
*
|
|
6000
|
+
* @param symbol Symbol
|
|
6001
|
+
* @param targetOpts Optional target settings that override values in `settings`
|
|
6002
|
+
*
|
|
6003
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5462
6004
|
*/
|
|
5463
6005
|
async readRawBySymbol(symbol, targetOpts = {}) {
|
|
5464
6006
|
if (!this.connection.connected) {
|
|
@@ -5476,13 +6018,27 @@ class Client extends events_1.default {
|
|
|
5476
6018
|
}
|
|
5477
6019
|
}
|
|
5478
6020
|
/**
|
|
5479
|
-
*
|
|
6021
|
+
* Writes raw data to the target system by a symbol object (acquired using `getSymbol()`)
|
|
5480
6022
|
*
|
|
5481
|
-
*
|
|
6023
|
+
* @example
|
|
6024
|
+
* ```js
|
|
6025
|
+
* try {
|
|
6026
|
+
* const value = await client.convertToRaw(32767, 'INT');
|
|
6027
|
+
* console.log(value); //<Buffer ff 7f>
|
|
5482
6028
|
*
|
|
5483
|
-
*
|
|
6029
|
+
* const symbol = await client.getSymbol('GVL_Read.StandardTypes.INT_');
|
|
6030
|
+
* await client.writeRawBySymbol(symbol, value);
|
|
6031
|
+
*
|
|
6032
|
+
* } catch (err) {
|
|
6033
|
+
* console.log("Error:", err);
|
|
6034
|
+
* }
|
|
6035
|
+
* ```
|
|
6036
|
+
*
|
|
6037
|
+
* @param symbol Symbol
|
|
5484
6038
|
* @param value Data to write
|
|
5485
6039
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
6040
|
+
*
|
|
6041
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5486
6042
|
*/
|
|
5487
6043
|
async writeRawBySymbol(symbol, value, targetOpts = {}) {
|
|
5488
6044
|
if (!this.connection.connected) {
|
|
@@ -5499,21 +6055,40 @@ class Client extends events_1.default {
|
|
|
5499
6055
|
}
|
|
5500
6056
|
}
|
|
5501
6057
|
/**
|
|
5502
|
-
*
|
|
6058
|
+
* Invokes a function block RPC method on the target system.
|
|
5503
6059
|
*
|
|
5504
|
-
*
|
|
6060
|
+
* Returns the return value of the method and outputs (if any).
|
|
5505
6061
|
*
|
|
5506
|
-
*
|
|
6062
|
+
* NOTE: In the PLC, `{attribute 'TcRpcEnable'}` is required above the `METHOD` definition.
|
|
5507
6063
|
*
|
|
5508
|
-
*
|
|
6064
|
+
* @example
|
|
6065
|
+
* ```js
|
|
6066
|
+
* try {
|
|
6067
|
+
* const res = await client.invokeRpcMethod('GVL_RPC.RpcBlock', 'Calculator', {
|
|
6068
|
+
* Value1: 1,
|
|
6069
|
+
* Value2: 123
|
|
6070
|
+
* });
|
|
5509
6071
|
*
|
|
5510
|
-
*
|
|
5511
|
-
*
|
|
5512
|
-
*
|
|
6072
|
+
* console.log(res);
|
|
6073
|
+
* //{
|
|
6074
|
+
* // returnValue: true,
|
|
6075
|
+
* // outputs: { Sum: 124, Product: 123, Division: 0.008130080997943878 }
|
|
6076
|
+
* //}
|
|
6077
|
+
*
|
|
6078
|
+
* } catch (err) {
|
|
6079
|
+
* console.log("Error:", err);
|
|
6080
|
+
* }
|
|
6081
|
+
* ```
|
|
6082
|
+
*
|
|
6083
|
+
* @param path Variable path in the PLC of the function block instance (such as `GVL_Test.ExampleBlock`)
|
|
6084
|
+
* @param method Function block method name
|
|
6085
|
+
* @param parameters Method parameters (inputs) (if any)
|
|
5513
6086
|
* @param targetOpts Optional target settings that override values in `settings`
|
|
5514
6087
|
*
|
|
5515
6088
|
* @template T Typescript data type of the method return value, for example `invokeRpcMethod<number>(...)` or `invokeRpcMethod<ST_TypedStruct>(...)`
|
|
5516
6089
|
* @template U Typescript data type of the method outputs object, for example `invokeRpcMethod<number, ST_TypedStruct>(...)`
|
|
6090
|
+
*
|
|
6091
|
+
* @throws Throws an error if sending the command fails or if the target responds with an error.
|
|
5517
6092
|
*/
|
|
5518
6093
|
async invokeRpcMethod(path, method, parameters = {}, targetOpts = {}) {
|
|
5519
6094
|
if (!this.connection.connected) {
|