APIs

Show:
"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;

    };
};