UNPKG

tc-context

Version:

TwinCAT ADS Communication Library for creating an active TwinCAT Context, with automatic symbol and type mapping

949 lines (948 loc) 46.8 kB
"use strict"; // tc-binding.ts /** * Module, which contains the definitions of all types of Bindings, which act as a layer between `TcSymbol` and the * `TcCom` Object. It manages type checking, all the communication, and event emission, as well as memory locations, which * are associated with the `TcSymbol` * * * Licensed under MIT License. * * Copyright (c) 2020 Dmitrij Trifanov <d.v.trifanov@gmail.com> * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. * * @packageDocumentation */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TcNamespaceBinding = exports.TcArrayBinding = exports.TcStructureBinding = exports.TcEnumBinding = exports.TcStringBinding = exports.TcNumericBinding = exports.TcBooleanBinding = exports.TcSymbolBinding = exports.TcBinding = void 0; //----IMPORTS... const debug_1 = __importDefault(require("debug")); const check_types_1 = __importDefault(require("check-types")); const tc_com_1 = require("./tc-com"); const tc_exception_1 = require("./tc-exception"); const tc_event_1 = require("./tc-event"); /** * Class which acts as an abstraction layer between a `TcSymbol` and the `TcCom` layer. * It is responsible for value conversion to Data and from it, as well as Type Checking, * storing all the Memory location which must be read, how to execute clearing of a Symbol. * * By itself, the `TcBinding` also acts as a Symbol Pointer, which is used for subscribing * for change notifications * * Lastly, it is also the EventEmitter for the `TcSymbol` * */ class TcBinding extends tc_event_1.TcEmitter { /** * Constructor for a Binding with no information on memory location, but definition of its components * and different parameters, used by derived classes * * @param context - The `TcContext` which owns this binding * @param symbol - The `TcSymbol` which owns this binding * @param parent - Parent Emitter, to whom a event will be propagate to * @param onSet - Alias, which to use in place of 'set' * @param onGet - Alias, which to use in place of 'get' * @param onClear - Alias, which to use in place of 'cleared' * @param onChange - Alias, which to use in place of 'changed' * @param debug - If enabled, will produce debug information */ constructor(context, symbol, parent, onSet, onGet, onClear, onChange, debug = false) { super(parent); /** * Stores an array of memory locations, which can be read * @internal */ this.__readPackages = []; /** * Stores an array of data packages locations, which can be used to clear the `TcBinding` * @internal */ this.__clearPackages = []; /** * @internal */ this.__indexGroup = 0; /** * @internal */ this.__indexOffset = 0; /** * @internal */ this.__size = 0; /** * @internal */ this.__log = debug_1.default(`TcContext::TcBinding`); this.__context = context; this.__symbol = symbol; this.__isValid = true; this.__log.enabled = debug; this.__onSet = onSet || 'set'; this.__onGet = onGet || 'get'; this.__onClear = onClear || 'cleared'; this.__onChange = onChange || 'changed'; this.__log(`Creating TcBinding[${this.__symbol.$path}]`); } /** * Index Group of this `TcBinding` */ get indexGroup() { return this.__indexGroup; } ; /** * Index Offset of this `TcBinding` */ get indexOffset() { return this.__indexOffset; } ; /** * Size of the Symbol, this `TcBinding` points to */ get size() { return this.__size; } ; /** * The `TcSymbol` owner of this `TcBinding` */ get symbol() { return this.__symbol; } ; /** * The `TcContext` owner of this `TcBinding` */ get context() { return this.__context; } ; /** * Flag, when if true, `TcBinding` is valid */ get isValid() { return this.__isValid; } /** * Flag, when if true, `TcBinding` is ReadOnly an no write operation can be invoked */ get readOnly() { return this.symbol.$readOnly; } ; /** * Performs a read of all the Memory Pointers, belonging to this `TcBinding` * * @throws {@link TcBindingIsInvalidException} - Attempting to use an invalid `TcBinding` * @throws {@link TcBindingOutOfRangeException} - Failure splitting reading buffer * @throws {@link TcComIsInvalidException} - Attempted to use an Invalid `TcCom` Object for subscription * @throws {@link TcComDataReadException} - Failed to read data pointers * @throws {@link TcComFromRawException} - Failed to convert the Raw Data * * @return - Values, of the Target PLC Symbol, which belong to this `TcBinding` * */ async read() { this.__log(`read() : Reading from TcBinding[${this.symbol.$path}]`); if (!this.isValid) throw new tc_exception_1.TcBindingIsInvalidException(this.context, this, `Attempting to read an Invalidated TcBinding[${this.symbol.$path}]`); const result = await this.context.COM.read(this.readPackages).then(dataPackages => this.fromRaw(dataPackages)); this.__log(`read() : Completed reading TcBinding[${this.symbol.$path}]`); this.__log(result); return result; } /** * Performs a write operation by converting values to memory locations and data to send, * which are part of this `TcBinding` * * @param value - The value that is to be written to the `TcBinding` * * @throws {@link TcBindingIsInvalidException} - Attempting to use an invalid `TcBinding` * @throws {@link TcBindingOutOfRangeException} - Failure splitting reading buffer * @throws {@link TcBindingReadOnlyException} - Attempting to write to a ReadOnly `TcBinding` * @throws {@link TcBindingInvalidTypeException} - Type mismatch with one a value, that is to be written * @throws {@link TcComIsInvalidException} - Attempted to use an Invalid `TcCom` Object for subscription * @throws {@link TcComDataWriteException} - Failed to write data packages * @throws {@link TcComToRawException} - Failed to convert the Raw Data * * @return - The value which was written to the Target PLC Symbol, which belong to this `TcBinding` * */ async write(value) { this.__log(`write() : Writing to TcBinding[${this.symbol.$path}]`); this.__log(value); if (!this.isValid) throw new tc_exception_1.TcBindingIsInvalidException(this.context, this, `Attempting to write to an Invalidated TcBinding[${this.symbol.$path}]`); await this.toRaw(value).then(dataPackages => this.context.COM.write(dataPackages)); this.__log(`write() : Completed writing TcBinding[${this.symbol.$path}]`); return value; } /** * Clears the data of all non-ReadOnly `TcBindings`, which belong to this `TcBinding` * * @throws {@link TcBindingIsInvalidException} - Attempting operation on an invalidated `TcBinding` * @throws {@link TcBindingReadOnlyException} - Attempting to clear a ReadOnly `TcBinding` * @throws {@link TcComIsInvalidException} - Attempting operation on an invalidated `TcCom` Object * @throws {@link TcComDataWriteException} - Failed to write data packages * */ async clear() { this.__log(`clear() : Clearing to TcBinding[${this.symbol.$path}]`); if (!this.isValid) throw new tc_exception_1.TcBindingIsInvalidException(this.context, this, `Attempting to clear an Invalidated TcBinding[${this.symbol.$path}]`); if (this.readOnly) throw new tc_exception_1.TcBindingReadOnlyException(this.context, this, `Attempting to clear a Readonly TcBinding[${this.symbol.$path}]`); await this.context.COM.write(this.clearPackages); this.__log(`write() : Completed clearing TcBinding[${this.symbol.$path}]`); return; } /** * Checks the input, to see if it valid and can be safely written to the Target PLC * * @throws {@link TcBindingIsInvalidException} - Attempting operation on an invalidated `TcBinding` * @throws {@link TcBindingReadOnlyException} - Attempting to write to a ReadOnly `TcBinding` * * @param value - The value to check for validity */ checkInput(value) { if (!this.isValid) throw new tc_exception_1.TcBindingIsInvalidException(this.context, this, `Attempting to convert Data to TcDataPackage using an Invalidated TcBinding[${this.symbol.$path}]`); if (this.readOnly) throw new tc_exception_1.TcBindingReadOnlyException(this.context, this, `Attempting to write to a Readonly TcBinding[${this.symbol.$path}]`); } /** * Emits a 'set' event, unless it was aliased to a custom name * * @param data - The data, to pass along with the event */ emitSet(data) { this.emit(this.__onSet, data); } /** * Emits a 'get' event, unless it was aliased to a custom name * * @param data - The data, to pass along with the event */ emitGet(data) { this.emit(this.__onGet, data); } /** * Emits a 'cleared' event, unless it was aliased to a custom name * * @param data - The data, to pass along with the event */ emitCleared(data) { this.emit(this.__onClear, data); } /** * Emits a 'changed' event, unless it was aliased to a custom name * * @param data - The data, to pass along with the event */ emitChange(data) { this.emit(this.__onChange, data); } /** * Performs a subscription of this `TcBinding` for monitoring value change * * @throws {@link TcBindingIsInvalidException} - Attempting operation on an invalidated `TcBinding` * @throws {@link TcComIsInvalidException} - Attempted to use an Invalid `TcCom` Object for subscription * @throws {@link TcComSubscribeException} - Failed to subscribe to the provided pointer * * @param sampling - The speed at which change is detected * @param callback - Callback, which is invoked when a change does happened */ async subscribe(sampling, callback) { if (!this.isValid) throw new tc_exception_1.TcBindingIsInvalidException(this.context, this, `Attempting to subscribe to an Invalidated TcBinding[${this.symbol.$path}]`); if (!this.__subscription) { this.__subscription = await this.context.COM.subscribe(sampling, this, async () => { this.__log(`subscribe#callback() : Received change event TcBinding ${this.symbol.$path}`); if (callback) { const result = await this.read(); callback(result); } }); this.__log(`subscribe() : Successfully subscribed to TcBinding TcBinding[${this.symbol.$path}]`); } else this.__log(`subscribe() : Attempting to subscribe to an already subscribed TcBinding[${this.symbol.$path}]`); } /** * Unsubscribes this `TcBinding` from monitoring value changes * * @throws {@link TcBindingIsInvalidException} - Attempting operation on an invalidated `TcBinding` * @throws {@link TcComIsInvalidException} - Attempted to use an Invalid `TcCom` Object for subscription * @throws {@link TcComUnsubscribeException} - Failed to unsubscribe the handle */ async unsubscribe() { if (!this.isValid) throw new tc_exception_1.TcBindingIsInvalidException(this.context, this, `Attempting to unsubscribe from an Invalidated TcBinding[${this.symbol.$path}]`); if (this.__subscription) { await this.context.COM.unsubscribe(this.__subscription); this.__subscription = undefined; this.__log(`unsubscribe() : Successfully unsubscribed from TcBinding TcBinding[${this.symbol.$path}]`); } else this.__log(`unsubscribe() : Attempting to unsubscribe from an already unsubscribed TcBinding[${this.symbol.$path}]`); } /** * Get all the Data Packages, needed to perform a clear operation */ get clearPackages() { return this.__clearPackages; } ; /** * Get all the Memory Pointers, needed to perform a read operation */ get readPackages() { return this.__readPackages; } ; /** * Invalidates the provided `TcBinding` * * @param binding - The `TcBinding`, which is to be invalidated */ static invalidate(binding) { binding.__isValid = false; } } exports.TcBinding = TcBinding; /** * Base class for `TcBindings` used on Target PLC Symbols. This excludes `PROGRAMS` and * variable lists */ class TcSymbolBinding extends TcBinding { /** * Constructs a binding with information of the Symbol location, and the default Type Parameters * * @param symbol - The `TcSymbol` which owns this binding * @param pointer - The memory location in the PLC, where the Symbol is located * @param parameters - Symbol Type data * @param parent - The parent of this Symbol, to whom events are propagated * @param debug - If enabled, will produce debug information */ constructor(symbol, pointer, parameters, parent, debug = false) { super(parameters.context, symbol, parent, parameters.onGet, parameters.onSet, parameters.onClear, parameters.onChange, debug); this.__indexGroup = pointer.indexGroup; this.__indexOffset = pointer.indexOffset; this.__size = pointer.size; } } exports.TcSymbolBinding = TcSymbolBinding; /** * Base class for `TcBindings` used on PlC Symbols, that are not structured. * This excludes `Structures`, `Function_Blocks` and `Unions` * * `TcSimpleBinding` have an explicit default value */ class TcSimpleBinding extends TcSymbolBinding { /** * Constructs a binding with information of the Symbol location, and the default Type Parameters * * @param symbol - The `TcSymbol` which owns this binding * @param pointer - The memory location in the PLC, where the Symbol is located * @param parameters - Symbol Type data * @param parent - The parent of this Symbol, to whom events are propagated * @param debug - If enabled, will produce debug information */ constructor(symbol, pointer, parameters, parent, debug = false) { super(symbol, pointer, parameters, parent, debug); this.__defaultValue = parameters.defaultBuffer; this.__type = parameters.name; this.__readPackages.push({ indexGroup: pointer.indexGroup, indexOffset: pointer.indexOffset, size: pointer.size }); if (this.__defaultValue !== undefined) { this.__clearPackages.push({ indexGroup: this.indexGroup, indexOffset: this.indexOffset, data: this.__defaultValue }); } } /** * Converts Data Packages from ADS to Values * * @throws {@link TcBindingIsInvalidException} - Attempting to use an invalid `TcBinding` * @throws {@link TcBindingOutOfRangeException} - Failure splitting reading buffer * @throws {@link TcComIsInvalidException} - Attempted to use an Invalid `TcCom` Object for subscription * @throws {@link TcComFromRawException} - Failed to convert the Raw Data * * @param dataPackages - The ADS Data packages, that are to be transformed */ async fromRaw(dataPackages) { this.__log(`fromRaw() : Parsing Data Package for TcBinding[${this.symbol.$path}]`); if (!this.isValid) throw new tc_exception_1.TcBindingIsInvalidException(this.context, this, `Attempting to convert TcDataPackage using an Invalidated TcBinding[${this.symbol.$path}]`); if (dataPackages.length > 1) throw new tc_exception_1.TcBindingOutOfRangeException(this.context, this, `Attempting to convert TcDataPackage length greater than 1 to a Simple TcBinding[${this.symbol.$path}]`); const value = await this.context.COM.fromRaw(this.__type, dataPackages[0].data); this.__log(`fromRaw() : Parsed Data Package to value ${value} for TcBinding ${this.symbol.$path}`); return value; } /** * Converts Data Packages from ADS to Values * * @throws {@link TcBindingIsInvalidException} - Attempting to use an invalid `TcBinding` * @throws {@link TcBindingOutOfRangeException} - Failure splitting reading buffer * @throws {@link TcComIsInvalidException} - Attempted to use an Invalid `TcCom` Object for subscription * @throws {@link TcComFromRawException} - Failed to convert the Raw Data * * @param dataPackages - The ADS Data packages, that are to be transformed */ async toRaw(value) { this.__log(`toRaw() : Parsing value ${value} to Raw for TcBinding[${this.symbol.$path}]`); this.checkInput(value); const result = await this.context.COM.toRaw(this.__type, value).then(buffer => [{ indexGroup: this.indexGroup, indexOffset: this.indexOffset, data: buffer }]); this.__log(`toRaw() : Parsed value ${value} to Raw for TcBinding[${this.symbol.$path}]`); return result; } /** * Checks the input, to see if it valid and can be safely written to the Target PLC * * @throws {@link TcBindingIsInvalidException} - Attempting operation on an invalidated `TcBinding` * @throws {@link TcBindingReadOnlyException} - Attempting to write to a ReadOnly `TcBinding` * * @param value - The value to check for validity */ checkInput(value) { super.checkInput(value); if (!check_types_1.default.primitive(value) && !check_types_1.default.instance(value, BigInt)) throw new tc_exception_1.TcBindingInvalidTypeException(this.context, this, `Attempting to write a structured Value to a Simple TcBinding[${this.symbol.$path}]`); } } /** * Base class for `TcBindings´ used for PLC Symbols of Type `Structure`, `Function_Block` and `Union` */ class TcComplexBinding extends TcSymbolBinding { constructor() { super(...arguments); /** * @internal */ this.__childrenBindings = {}; } /** * Internal method, for adding a Child `TcBinding` to a specified `TcComplexBinding` * * @param binding - The binding, to which the child is added * @param child - The child, which is to be added to the binding * * @internal */ static addChild(binding, child) { binding.__addChild(child); } /** * Internal method, for adding a Child `TcBinding` as part of this `TcComplexBinding` * * @param child - The Child that is to be added */ __addChild(child) { this.__childrenBindings[child.key] = child.binding; this.__clearPackages.push(...child.binding.clearPackages); this.__readPackages.push(...child.binding.readPackages); } } /** * `TcBinding` for attaching to `BOOL` PLC Symbol */ class TcBooleanBinding extends TcSimpleBinding { /** * Checks the input, to see if it valid and can be safely written to the Target PLC * * @throws {@link TcBindingIsInvalidException} - Attempting operation on an invalidated `TcBinding` * @throws {@link TcBindingReadOnlyException} - Attempting to write to a ReadOnly `TcBinding` * * @param value - The value to check for validity */ checkInput(value) { super.checkInput(value); if (!check_types_1.default.boolean(value)) throw new tc_exception_1.TcBindingInvalidTypeException(this.context, this, `Attempting to write a non-boolean Value to a Boolean TcBinding[${this.symbol.$path}]`); } } exports.TcBooleanBinding = TcBooleanBinding; /** * `TcBinding` for attaching to Numeric PLC Symbols */ class TcNumericBinding extends TcSimpleBinding { /** * Constructs a binding with information of the Symbol location, and the default Type Parameters * * @param symbol - The `TcSymbol` which owns this binding * @param pointer - The memory location in the PLC, where the Symbol is located * @param parameters - Symbol Type data * @param parent - The parent of this Symbol, to whom events are propagated * @param debug - If enabled, will produce debug information */ constructor(symbol, pointer, parameters, parent, debug = false) { super(symbol, pointer, parameters, parent, debug); this.__adst = parameters.adst; this.__upperBorder = parameters.upperBorder; this.__lowerBorder = parameters.lowerBorder; } /** * Access the maximum value, that is safe to write to Symbol */ get upperBorder() { return this.__upperBorder; } ; /** * Access the minimum value, that is safe to write to Symbol */ get lowerBorder() { return this.__lowerBorder; } ; /** * Checks the input, to see if it valid and can be safely written to the Target PLC * * @throws {@link TcBindingIsInvalidException} - Attempting operation on an invalidated `TcBinding` * @throws {@link TcBindingReadOnlyException} - Attempting to write to a ReadOnly `TcBinding` * * @param value - The value to check for validity */ checkInput(value) { super.checkInput(value); if ((this.__adst === tc_com_1.ADST.UINT64 || this.__adst === tc_com_1.ADST.INT64) && !check_types_1.default.instance(value, BigInt)) throw new tc_exception_1.TcBindingInvalidTypeException(this.context, this, `Attempting to write a non-BigInt value to Numeric TcBinding[${this.symbol.$path}]`); if (this.__adst !== tc_com_1.ADST.UINT64 && this.__adst !== tc_com_1.ADST.INT64 && !check_types_1.default.number(value)) throw new tc_exception_1.TcBindingInvalidTypeException(this.context, this, `Attempting to write a non-number value to Numeric TcBinding[${this.symbol.$path}]`); if (value > this.__upperBorder || value < this.__lowerBorder) throw new tc_exception_1.TcBindingOutOfRangeException(this.context, this, `Attempting to write value, which is outside of range [ ${this.__lowerBorder}:${this.__upperBorder} ] to Numeric TcBinding[${this.symbol.$path}]`); } /** * Converts Data Packages from ADS to Values * * @throws {@link TcBindingIsInvalidException} - Attempting to use an invalid `TcBinding` * @throws {@link TcBindingOutOfRangeException} - Failure splitting reading buffer * @throws {@link TcComIsInvalidException} - Attempted to use an Invalid `TcCom` Object for subscription * @throws {@link TcComFromRawException} - Failed to convert the Raw Data * * @param dataPackages - The ADS Data packages, that are to be transformed */ async fromRaw(value) { const result = await super.fromRaw(value); return (this.__adst !== tc_com_1.ADST.UINT64) ? result : BigInt.asUintN(64, result); } } exports.TcNumericBinding = TcNumericBinding; /** * `TcBinding` for attaching to `STRING` or `WSTRING` PLC Symbols */ class TcStringBinding extends TcSimpleBinding { /** * Constructs a binding with information of the Symbol location, and the default Type Parameters * * @param symbol - The `TcSymbol` which owns this binding * @param pointer - The memory location in the PLC, where the Symbol is located * @param parameters - Symbol Type data * @param parent - The parent of this Symbol, to whom events are propagated * @param debug - If enabled, will produce debug information */ constructor(symbol, pointer, parameters, parent, debug = false) { super(symbol, pointer, parameters, parent, debug); this.__length = parameters.length; } /** * Access the maximum length of a string, that is safe to write to the PLC Symbol */ get length() { return this.__length; } ; /** * Checks the input, to see if it valid and can be safely written to the Target PLC * * @throws {@link TcBindingIsInvalidException} - Attempting operation on an invalidated `TcBinding` * @throws {@link TcBindingReadOnlyException} - Attempting to write to a ReadOnly `TcBinding` * * @param value - The value to check for validity */ checkInput(value) { super.checkInput(value); if (!check_types_1.default.string(value)) throw new tc_exception_1.TcBindingInvalidTypeException(this.context, this, `Attempting to write a non-string Value to a String TcBinding[${this.symbol.$path}]`); if (value.length > this.__length) throw new tc_exception_1.TcBindingOutOfRangeException(this.context, this, `Attempting to write a string longer than ${this.__length} to a String TcBinding[${this.symbol.$path}]`); } } exports.TcStringBinding = TcStringBinding; /** * `TcBinding` for attaching to `ENUM` PLC Symbols */ class TcEnumBinding extends TcSimpleBinding { /** * Constructs a binding with information of the Symbol location, and the default Type Parameters * * @param symbol - The `TcSymbol` which owns this binding * @param pointer - The memory location in the PLC, where the Symbol is located * @param parameters - Symbol Type data * @param parent - The parent of this Symbol, to whom events are propagated * @param debug - If enabled, will produce debug information */ constructor(symbol, pointer, parameters, parent, debug = false) { super(symbol, pointer, parameters, parent, debug); /** * @internal */ this.__buffers = {}; this.__fields = parameters.fields; this.__buffers = parameters.buffers; } /** * Access the fields, which are allowed to be written to the PLC Symbol */ get fields() { return this.__fields; } ; /** * Converts Data Packages from ADS to Values * * @throws {@link TcBindingIsInvalidException} - Attempting to use an invalid `TcBinding` * @throws {@link TcBindingOutOfRangeException} - Failure splitting reading buffer * @throws {@link TcComIsInvalidException} - Attempted to use an Invalid `TcCom` Object for subscription * @throws {@link TcComFromRawException} - Failed to convert the Raw Data * * @param dataPackages - The ADS Data packages, that are to be transformed */ async fromRaw(value) { const result = (await super.fromRaw(value)); return `${this.__type}.${result.name}`; } /** * Converts Data Packages from ADS to Values * * @throws {@link TcBindingIsInvalidException} - Attempting to use an invalid `TcBinding` * @throws {@link TcBindingOutOfRangeException} - Failure splitting reading buffer * @throws {@link TcComIsInvalidException} - Attempted to use an Invalid `TcCom` Object for subscription * @throws {@link TcComFromRawException} - Failed to convert the Raw Data * * @param dataPackages - The ADS Data packages, that are to be transformed */ async toRaw(value) { super.checkInput(value); if (!check_types_1.default.string(value)) throw new tc_exception_1.TcBindingInvalidTypeException(this.context, this, `Attempting to write a non-enum Value to an Enum TcBinding[${this.symbol.$path}]`); if (!this.__buffers[value]) throw new tc_exception_1.TcBindingOutOfRangeException(this.context, this, `Attempting to write a non-existent enum field to an Enum TcBinding[${this.symbol.$path}]`); return [{ indexGroup: this.indexGroup, indexOffset: this.indexOffset, data: this.__buffers[value] }]; } } exports.TcEnumBinding = TcEnumBinding; /** * `TcBinding` for attaching to `Structures`, `Function_Blocks` or `Unions` PLC Symbols */ class TcStructureBinding extends TcComplexBinding { /** * Checks the input, to see if it valid and can be safely written to the Target PLC * * @throws {@link TcBindingIsInvalidException} - Attempting operation on an invalidated `TcBinding` * @throws {@link TcBindingReadOnlyException} - Attempting to write to a ReadOnly `TcBinding` * * @param value - The value to check for validity */ checkInput(value) { super.checkInput(value); if (!check_types_1.default.object(value)) throw new tc_exception_1.TcBindingInvalidTypeException(this.context, this, `Attempting to write a non-structured Value to a Structured TcBinding[${this.symbol.$path}]`); } /** * Converts Data Packages from ADS to Values * * @throws {@link TcBindingIsInvalidException} - Attempting to use an invalid `TcBinding` * @throws {@link TcBindingOutOfRangeException} - Failure splitting reading buffer * @throws {@link TcComIsInvalidException} - Attempted to use an Invalid `TcCom` Object for subscription * @throws {@link TcComFromRawException} - Failed to convert the Raw Data * * @param dataPackages - The ADS Data packages, that are to be transformed */ async fromRaw(dataPackages) { this.__log(`fromRaw() : Parsing Data Package for Structured TcBinding[${this.symbol.$path}]`); if (!this.isValid) throw new tc_exception_1.TcBindingIsInvalidException(this.context, this, `Attempting to convert TcDataPackage using an Invalidated TcBinding[${this.symbol.$path}]`); const result = {}; const promises = []; let start = 0; for (let [memberName, memberValue] of Object.entries(this.__childrenBindings)) { this.__log(`fromRaw() : -> Accessing [${memberName}] Child for Value conversion of TcBinding[${this.symbol.$path}]`); let length = memberValue.readPackages.length; result[memberName] = undefined; promises.push(memberValue.fromRaw(dataPackages.slice(start, start + length)).then(convertedValue => { result[memberName] = convertedValue; })); start += length; } await Promise.all(promises); this.__log(`fromRaw() : Parsed Data Package for Structured TcBinding[${this.symbol.$path}]`); return result; } /** * Converts Data Packages from ADS to Values * * @throws {@link TcBindingIsInvalidException} - Attempting to use an invalid `TcBinding` * @throws {@link TcBindingOutOfRangeException} - Failure splitting reading buffer * @throws {@link TcComIsInvalidException} - Attempted to use an Invalid `TcCom` Object for subscription * @throws {@link TcComFromRawException} - Failed to convert the Raw Data * * @param dataPackages - The ADS Data packages, that are to be transformed */ async toRaw(value) { this.__log(`toRaw() : Parsing structured value to Raw for Structured TcBinding[${this.symbol.$path}]`); this.checkInput(value); let promises = []; for (let [memberName, memberValue] of Object.entries(value)) { this.__log(`toRaw() : -> accessing [${memberValue.key}] Member for Raw conversion of TcBinding[${this.symbol.$path}]`); const child = this.__childrenBindings[memberName]; if (child) { promises.push(child.toRaw(memberValue)); } else throw new tc_exception_1.TcBindingOutOfRangeException(this.context, this, `Attempting to write a non-existing Member field [${memberName}] of TcBinding[${this.symbol.$path}]`); } return Promise.all(promises).then(dataPackages => { this.__log(`toRaw() : Parsed Value for Structured TcBinding[${this.symbol.$path}]`); const result = []; dataPackages.forEach(dataPackage => result.push(...dataPackage)); return result; }); } /** * Will attempt to invoke the provided method, based on the variable path and the method name, with the * provided arguments. * * As of now, no type checking is performed on the passed arguments, and this function acts as a simple through put * to the `TcCom` Module * * @param path - The full path to the `Function_Block`, whose method is called * @param method - The method name, that is to be called * @param args - All the arguments, as an object, that are passed to the method * * @throws {@link TcComIsInvalidException} - Attempted to use an Invalid `TcCom` Object for subscription * @throws {@link TcComMethodCallException} - Failed to call the Rpc Method on the PLC Side * * @return - The result of the method call * */ async callMethod(path, method, args) { return this.context.COM.callMethod(path, method, args); } } exports.TcStructureBinding = TcStructureBinding; /** * `TcBinding` for attaching to `ARRAY OF...` PLC Symbols */ class TcArrayBinding extends TcComplexBinding { /** * Constructs a binding with information of the Symbol location, and the default Type Parameters * * @param symbol - The `TcSymbol` which owns this binding * @param pointer - The memory location in the PLC, where the Symbol is located * @param parameters - Symbol Type data * @param dimension - The dimension definition of this Array Symbol * @param parent - The parent of this Symbol, to whom events are propagated * @param debug - If enabled, will produce debug information */ constructor(symbol, pointer, parameters, dimension, parent, debug = false) { super(symbol, pointer, parameters, parent, debug); this.__startIndex = dimension.startIndex; this.__length = dimension.length; } /** * Access the Start Index of this Array */ get startIndex() { return this.__startIndex; } ; /** * Access the length of this Array */ get length() { return this.__length; } /** * Checks the input, to see if it valid and can be safely written to the Target PLC * * @throws {@link TcBindingIsInvalidException} - Attempting operation on an invalidated `TcBinding` * @throws {@link TcBindingReadOnlyException} - Attempting to write to a ReadOnly `TcBinding` * * @param value - The value to check for validity */ checkInput(value) { super.checkInput(value); if (!check_types_1.default.array(value)) throw new tc_exception_1.TcBindingInvalidTypeException(this.context, this, `Attempting to write a non-array Value to an Array TcBinding[${this.symbol.$path}]`); if (value.length > this.__length) throw new tc_exception_1.TcBindingOutOfRangeException(this.context, this, `Attempting to write an array of length [${value.length}] to a Array of length ${this.__length} of TcBinding[${this.symbol.$path}]`); } /** * Converts Data Packages from ADS to Values * * @throws {@link TcBindingIsInvalidException} - Attempting to use an invalid `TcBinding` * @throws {@link TcBindingOutOfRangeException} - Failure splitting reading buffer * @throws {@link TcComIsInvalidException} - Attempted to use an Invalid `TcCom` Object for subscription * @throws {@link TcComFromRawException} - Failed to convert the Raw Data * * @param dataPackages - The ADS Data packages, that are to be transformed */ async toRaw(value) { this.__log(`toRaw() : Parsing array value to Raw for Array TcBinding[${this.symbol.$path}]`); this.checkInput(value); let promises = []; value.forEach((indexedValue, index) => { this.__log(`toRaw() : -> accessing [${index + this.__startIndex}] Index for Raw conversion of TcBinding[${this.symbol.$path}]`); const child = this.__childrenBindings[index + this.__startIndex]; if (child) { promises.push(child.toRaw(indexedValue)); } else throw new tc_exception_1.TcBindingOutOfRangeException(this.context, this, `Attempting to write a non-existing Index field [${index + this.__startIndex}] of TcBinding[${this.symbol.$path}]`); }); this.__log(`toRaw() : Parsed Value for Array TcBinding[${this.symbol.$path}]`); return Promise.all(promises).then(dataPackages => { const result = []; dataPackages.forEach(dataPackage => result.push(...dataPackage)); return result; }); } /** * Converts Data Packages from ADS to Values * * @throws {@link TcBindingIsInvalidException} - Attempting to use an invalid `TcBinding` * @throws {@link TcBindingOutOfRangeException} - Failure splitting reading buffer * @throws {@link TcComIsInvalidException} - Attempted to use an Invalid `TcCom` Object for subscription * @throws {@link TcComFromRawException} - Failed to convert the Raw Data * * @param dataPackages - The ADS Data packages, that are to be transformed */ async fromRaw(dataPackages) { this.__log(`fromRaw() : Parsing Data Package for Array TcBinding[${this.symbol.$path}]`); if (!this.isValid) throw new tc_exception_1.TcBindingIsInvalidException(this.context, this, `Attempting to convert TcDataPackage using an Invalidated TcBinding[${this.symbol.$path}]`); const result = []; const promises = []; let start = 0; for (let [memberName, memberValue] of Object.entries(this.__childrenBindings)) { this.__log(`fromRaw() : -> accessing [${memberName}] Index for Value conversion of TcBinding[${this.symbol.$path}]`); let length = memberValue.readPackages.length; result[parseInt(memberName) - this.__startIndex] = undefined; promises.push(memberValue.fromRaw(dataPackages.slice(start, start + length)).then(convertedValue => { result[parseInt(memberName) - this.__startIndex] = convertedValue; })); start += length; } await Promise.all(promises); this.__log(`fromRaw() : Parsed Data Package for Array TcBinding[${this.symbol.$path}]`); return result; } } exports.TcArrayBinding = TcArrayBinding; /** * `TcBinding` for attaching to `PROGRAMS` or `Variable Lists` PLC Symbols. * * The `TcNamespaceBinding` is unique, because it has no parent - it is the entry point, * as well as its Memory Definition is based on the Children passed to it */ class TcNamespaceBinding extends TcBinding { /** * Constructor a namespace Binding, which will grow and adjust, as children are added to it * * @param context - The `TcContext` which owns this binding * @param symbol - The `TcSymbol` which owns this binding * @param parent - Parent Emitter, to whom a event will be propagate to * @param debug - If enabled, will produce debug information */ constructor(context, symbol, parent, debug = false) { super(context, symbol, parent, undefined, undefined, undefined, undefined, debug); /** * @internal */ this.__childrenBindings = {}; } /** * Converts Data Packages from ADS to Values * * @throws {@link TcBindingIsInvalidException} - Attempting to use an invalid `TcBinding` * @throws {@link TcBindingOutOfRangeException} - Failure splitting reading buffer * @throws {@link TcComIsInvalidException} - Attempted to use an Invalid `TcCom` Object for subscription * @throws {@link TcComFromRawException} - Failed to convert the Raw Data * * @param dataPackages - The ADS Data packages, that are to be transformed */ async fromRaw(dataPackages) { this.__log(`fromRaw() : Parsing Data Package for Namespace TcBinding[${this.symbol.$path}]`); if (!this.isValid) throw new tc_exception_1.TcBindingIsInvalidException(this.context, this, `Attempting to convert TcDataPackage using an Invalidated TcBinding[${this.symbol.$path}]`); const result = {}; const promises = []; let start = 0; for (let [memberName, memberValue] of Object.entries(this.__childrenBindings)) { this.__log(`fromRaw() : -> Accessing [${memberName}] Child for Value conversion of TcBinding[${this.symbol.$path}]`); let length = memberValue.readPackages.length; result[memberName] = undefined; promises.push(memberValue.fromRaw(dataPackages.slice(start, start + length)).then(convertedValue => { result[memberName] = convertedValue; })); start += length; } await Promise.all(promises); this.__log(`fromRaw() : Parsed Data Package for Namespace TcBinding[${this.symbol.$path}]`); return result; } /** * Converts Data Packages from ADS to Values * * @throws {@link TcBindingIsInvalidException} - Attempting to use an invalid `TcBinding` * @throws {@link TcBindingOutOfRangeException} - Failure splitting reading buffer * @throws {@link TcComIsInvalidException} - Attempted to use an Invalid `TcCom` Object for subscription * @throws {@link TcComFromRawException} - Failed to convert the Raw Data * * @param dataPackages - The ADS Data packages, that are to be transformed */ async toRaw(value) { this.__log(`toRaw() : Parsing structured value to Raw for Namespace TcBinding[${this.symbol.$path}]`); this.checkInput(value); let promises = []; for (let [memberName, memberValue] of Object.entries(value)) { this.__log(`toRaw() : -> accessing [${memberValue.key}] Member for Raw conversion of TcBinding[${this.symbol.$path}]`); const child = this.__childrenBindings[memberName]; if (child) { promises.push(child.toRaw(memberValue)); } else throw new tc_exception_1.TcBindingOutOfRangeException(this.context, this, `Attempting to write a non-existing Member field [${memberName}] of TcBinding[${this.symbol.$path}]`); } return Promise.all(promises).then(dataPackages => { this.__log(`toRaw() : Parsed Value for Namespace TcBinding[${this.symbol.$path}]`); const result = []; dataPackages.forEach(dataPackage => result.push(...dataPackage)); return result; }); } /** * Checks the input, to see if it valid and can be safely written to the Target PLC * * @throws {@link TcBindingIsInvalidException} - Attempting operation on an invalidated `TcBinding` * @throws {@link TcBindingReadOnlyException} - Attempting to write to a ReadOnly `TcBinding` * * @param value - The value to check for validity */ checkInput(value) { super.checkInput(value); if (!check_types_1.default.object(value)) throw new tc_exception_1.TcBindingInvalidTypeException(this.context, this, `Attempting to write a non-structured Value to a Namespace TcBinding[${this.symbol.$path}]`); } /** * Internal method, for adding a Child `TcSymbolBinding` as part of this `TcNamespaceBinding` * * The method also readjusts the indexOffset and size of this `TcNamespaceBinding` * * @param child - The Child that is to be added */ __addChild(child) { this.__childrenBindings[child.key] = child.binding; this.__clearPackages.push(...child.binding.clearPackages); this.__readPackages.push(...child.binding.readPackages); //Compute the index Offsets and Group if (this.__indexGroup === 0) { this.__indexGroup = child.binding.indexGroup; } else if (this.__indexGroup !== child.binding.indexGroup) { throw new Error('IndexGroup of Namespace is invalid. Unsupported situation'); } if (this.__indexOffset === 0) { this.__indexOffset = child.binding.indexOffset; this.__size = child.binding.size; } else { if (this.__indexOffset + this.size < child.binding.indexOffset) { this.__size = child.binding.indexOffset - this.__indexOffset + child.binding.size; } else if (this.__indexOffset > child.binding.indexOffset) { this.__size = this.__indexOffset - child.binding.indexOffset + this.size; this.__indexOffset = child.binding.indexOffset; } } } /** * Internal method, for adding a Child `TcSymbolBinding` as part of this `TcNamespaceBinding` * * @param child - The Child that is to be added */ static addChild(binding, child) { binding.__addChild(child); } } exports.TcNamespaceBinding = TcNamespaceBinding;