"use strict";
// HasProperty Variable InstrumentRange Range PropertyType Optional
// HasProperty Variable EURange Range PropertyType Mandatory
// HasProperty Variable EngineeringUnits EUInformation PropertyType Optional
const assert = require("node-opcua-assert").assert;
const _ = require("underscore");
const DataType = require("node-opcua-variant").DataType;
const Variant = require("node-opcua-variant").Variant;
const StatusCodes = require("node-opcua-status-code").StatusCodes;
const EUInformation = require("node-opcua-data-access").EUInformation;
const Range = require("node-opcua-data-access").Range;
const add_dataItem_stuff = require("./UADataItem").add_dataItem_stuff;
const Namespace = require("../namespace").Namespace;
exports.install = function (AddressSpace) {
/**
* @method addDataItem
* @param options
* @param options.browseName {String}
* @param options.definition {String}
* @param [options.valuePrecision {Double |null} =null]
* @param options.dataType {NodeId} // todo :check
* @param options.value
* @param options.componentOf
* @return {UAVariable}
*/
Namespace.prototype.addDataItem = function(options) {
const namespace = this;
const addressSpace = namespace.addressSpace;
assert(addressSpace instanceof AddressSpace);
const dataType = options.dataType || "Number";
const dataItemType = addressSpace.findVariableType("DataItemType");
const variable = namespace.addVariable(_.extend(options, {
typeDefinition: dataItemType.nodeId,
dataType: dataType
}));
add_dataItem_stuff(variable, options);
variable.install_extra_properties();
return variable;
};
/**
*
* @method addAnalogDataItem
*
* AnalogDataItem DataItems that represent continuously-variable physical quantities ( e.g., length, temperature) , in
* contrast to the digital representation of data in discrete items
* NOTE Typical examples are the values provided by temperature sensors or pressure sensors. OPC UA defines a specific
* UAVariableType to identify an AnalogItem. Properties describe the possible ranges of AnalogItems.
*
*
* @example:
*
*
* namespace.add_analog_dataItem({
* componentOf: parentObject,
* browseName: "TemperatureSensor",
*
* definition: "(tempA -25) + tempB",
* valuePrecision: 0.5,
* //-
* instrumentRange: { low: 100 , high: 200}, // optional
* engineeringUnitsRange: { low: 100 , high: 200}, // mandatory
* engineeringUnits: standardUnits.degree_celsius,, // optional
*
* // access level
* accessLevel: 1
* minimumSamplingInterval: 10,
*
* });
*
* @param options
* @param options.browseName {String}
* @param options.definition {String}
* @param [options.valuePrecision {Double |null} =null]
* @param options.instrumentRange
* @param options.instrumentRange.low {Double}
* @param options.instrumentRange.high {Double}
* @param options.engineeringUnitsRange.low {Double}
* @param options.engineeringUnitsRange.high {Double}
* @param options.engineeringUnits {String}
* @param options.dataType {NodeId} // todo :check
* @param [options.accessLevel {AccessLevelFlag} = "CurrentRead | CurrentWrite"]
* @param [options.userAccessLevel {AccessLevelFlag} = "CurrentRead | CurrentWrite"]
* @param options.value
* @param [options.modellingRule]
* @return {UAVariable}
*/
AddressSpace.prototype.addAnalogDataItem = function (options) {
return this._resolveRequestedNamespace(options).addAnalogDataItem(options);
};
Namespace.prototype.addAnalogDataItem = function (options) {
const namespace = this;
const addressSpace = namespace.addressSpace;
assert(options.hasOwnProperty("engineeringUnitsRange"), "expecting engineeringUnitsRange");
const dataType = options.dataType || "Number";
const analogItemType = addressSpace.findVariableType("AnalogItemType");
assert(analogItemType, "expecting AnalogItemType to be defined , check nodeset xml file");
let clone_options = _.clone(options);
clone_options = _.extend(clone_options, {
typeDefinition: analogItemType.nodeId,
dataType: dataType
});
const variable = namespace.addVariable(clone_options);
//var variable = namespace.addVariable({
// componentOf: options.componentOf,
// organizedBy: options.organizedBy,
// browseName: options.browseName,
// nodeId: options.nodeId,
// value: options.value,
// accessLevel: options.accessLevel,
// userAccessLevel: options.userAccessLevel,
// modellingRule: options.modellingRule
//
// typeDefinition: analogItemType.nodeId,
// dataType: dataType,
//});
add_dataItem_stuff(variable, options);
// mandatory (EURange in the specs)
// OPC Unified Architecture, Part 8 6 Release 1.02
// EURange defines the value range likely to be obtained in normal operation. It is intended for such
// use as automatically scaling a bar graph display
// Sensor or instrument failure or deactivation can result in a return ed item value which is actually
// outside of this range. Client software must be prepared to deal with this possibility. Similarly a client
// may attempt to write a value that is outside of this range back to the server. The exact behaviour
// (accept, reject, clamp, etc.) in this case is server - dependent. However , in general servers shall be
// prepared to handle this.
// Example: EURange ::= {-200.0,1400.0}
const euRange = namespace.addVariable({
propertyOf: variable,
typeDefinition: "PropertyType",
browseName: {name:"EURange",namespaceIndex:0},
dataType: "Range",
minimumSamplingInterval: 0,
value: new Variant({
dataType: DataType.ExtensionObject, value: new Range(options.engineeringUnitsRange)
}),
modellingRule: options.modellingRule
});
const handler = variable.handle_semantic_changed.bind(variable);
euRange.on("value_changed",handler);
if (options.hasOwnProperty("instrumentRange")) {
const instrumentRange =namespace.addVariable({
propertyOf: variable,
typeDefinition: "PropertyType",
browseName: {name:"InstrumentRange",namespaceIndex:0},
dataType: "Range",
minimumSamplingInterval: 0,
accessLevel: "CurrentRead | CurrentWrite",
value: new Variant({
dataType: DataType.ExtensionObject, value: new Range(options.instrumentRange)
}),
modellingRule: options.modellingRule ? "Mandatory" : undefined
});
instrumentRange.on("value_changed",handler);
}
if (options.hasOwnProperty("engineeringUnits")) {
const engineeringUnits = new EUInformation(options.engineeringUnits);
assert(engineeringUnits instanceof EUInformation, "expecting engineering units");
// EngineeringUnits specifies the units for the DataItem‟s value (e.g., DEGC, hertz, seconds). The
// EUInformation type is specified in 5.6.3.
const eu = namespace.addVariable({
propertyOf: variable,
typeDefinition: "PropertyType",
browseName: {name:"EngineeringUnits",namespaceIndex:0},
dataType: "EUInformation",
minimumSamplingInterval: 0,
accessLevel: "CurrentRead",
value: new Variant({
dataType: DataType.ExtensionObject, value: engineeringUnits
}),
modellingRule: options.modellingRule ? "Mandatory" : undefined
});
eu.on("value_changed",handler);
}
variable.install_extra_properties();
return variable;
};
};