Commit 9a5b56dc authored by Administrator's avatar Administrator
Browse files

Initial commit

parents
out/
*.bak
\ No newline at end of file
<opc:TypeDictionary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://bitctrl.de/UA/BCUA/" DefaultByteOrder="LittleEndian" xmlns:opc="http://opcfoundation.org/BinarySchema/" xmlns:ua="http://opcfoundation.org/UA/" TargetNamespace="http://bitctrl.de/UA/BCUA/">
<opc:Import Namespace="http://opcfoundation.org/UA/"/>
<opc:EnumeratedType LengthInBits="32" Name="BCSyncType">
<opc:EnumeratedValue Name="OPCUA" Value="0"/>
<opc:EnumeratedValue Name="VDV301" Value="1"/>
<opc:EnumeratedValue Name="HTTP" Value="2"/>
<opc:EnumeratedValue Name="None" Value="3"/>
</opc:EnumeratedType>
</opc:TypeDictionary>
File added
This diff is collapsed.
<xs:schema elementFormDefault="qualified" targetNamespace="http://bitctrl.de/UA/BCUA/Types.xsd" xmlns:tns="http://bitctrl.de/UA/BCUA/Types.xsd" xmlns:ua="http://opcfoundation.org/UA/2008/02/Types.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:import namespace="http://opcfoundation.org/UA/2008/02/Types.xsd"/>
<xs:simpleType name="BCSyncType">
<xs:restriction base="xs:string">
<xs:enumeration value="OPCUA_0"/>
<xs:enumeration value="VDV301_1"/>
<xs:enumeration value="HTTP_2"/>
<xs:enumeration value="None_3"/>
</xs:restriction>
</xs:simpleType>
<xs:element type="tns:BCSyncType" name="BCSyncType"/>
<xs:complexType name="ListOfBCSyncType">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" type="tns:BCSyncType" name="BCSyncType" nillable="true"/>
</xs:sequence>
</xs:complexType>
<xs:element type="tns:ListOfBCSyncType" name="ListOfBCSyncType" nillable="true"/>
</xs:schema>
###########################
# UaModeler Project File #
# Generated at 29.07.2020 #
###########################
PROJECTNAME = BCUAModel
TEMPLATESET = ($TEMPLATES)/cpp/v1_6/t3/cpp_server.tt2set
MODELS += ($MODELS)/Opc.Ua.NodeSet2.ua \
bcua.nodeset.ua
VARIABLES+= bcua.nodeset.ua: CreateStringNodeIds=false; DllExportPrefix=BCUA; NamespaceName=BCUA; NamespacePrefix=BCUA;
COLORS = ($MODELS)/Opc.Ua.NodeSet2.ua: #0000ff\
bcua.nodeset.ua: #0000ff
OUTPUTPATH =
GENERATE += bcua.nodeset.ua
NODEACCESSINFOROLES += Administrator \
SecurityAdmin \
ConfigurationAdmin \
Operator \
Observer
/**
* Beispiel für "http://bitctrl.de/UA/BCUA/"-Modell
* @module interface.js
* @author Geniriert durch UARTS @ BitCtrl Systems GmbH ModellGenerator
*/
import * as BCUA from '../interface.js';
export default async ( ) => {
debugger;
console.log( `First Example: ${BCUA.NamespaceURI}` );
trace( `First Example: ${BCUA.NamespaceURI}` );
alert( `First Example: ${BCUA.NamespaceURI}` );
};
/**
* Interface mit Beispielen für "http://bitctrl.de/UA/BCUA/"-Modell
* @module interface.js
* @author Geniriert durch UARTS @ BitCtrl Systems GmbH ModellGenerator
*/
export {default as firstExample} from './0000_firstExample.js';
/**
* Interface für "http://bitctrl.de/UA/BCUA/"-Modell
* @module interface.js
* @author Geniriert durch UARTS @ BitCtrl Systems GmbH ModellGenerator
*/
export * from './libs/bcuaconst.js';
export { BCUAModell as Modell } from './libs/bcuamodell.js';
export * from './libs/aggregatorfactory.js';
\ No newline at end of file
/**
* @module modellgenerator.js
* @author Aleksej Tokarev @ BitCtrl Systems GmbH
*/
import * as BCUA from './../interface.js';
import * as UA from './../../../../js/interface.js';
const FS = UARTS.Filesystem( );
/**
* TODO: Nach Bedarf erweitern
* Faktory die dafür zuständig ist, dass die Aggregatoren gebaut und richtig ausgefüllt werden
*/
export class AggregatorFactory extends UA.Tools.EventEmitter {
constructor( pMixNodeId, pModule = null, pObjSharedData ) {
super( );
this.node = pMixNodeId instanceof UANodeId ? pMixNodeId.node : pMixNodeId;
if ( !this.node || !this.node.isTypeOf( BCUA.ID.AggregatorType ) ) {
throw new TypeError( 'Ungültige Aggregator-Node. Node mus vom Type AggregatprType sein.' );
}
if ( pModule !== null && Object.prototype.toString.call( pModule ) !== '[object Module]' ) {
throw new TypeError( `Ungültige Argument "pModule". Muss "[object Module]" ist aber "${Object.prototype.toString.call( pModule )}"` );
}
this.module = pModule || null;
this.type = this.node.typeDefinition;
this.share = { };
if ( this.module ) {
this.share[ this.module.ModellName.toUpperCase( ) ] = this.module;
}
UA.Tools.deepAssign( this.share, pObjSharedData );
this.script = null;
this.tools = null;
this.initScript = null; // UANode
this.dynComponents = null; // UANode
this.components = { }; // Object of UANodes
}
async build( ) {
// Asyncron aufrufen, damit alle Callbacks getriegert und empfangen werden können
return new Promise( ( pFunResolve, pFunReject ) => {
setTimeout( ( ) => {
try {
let time = Date.now( );
this.emit( AggregatorFactory.EVENT.BEFORE_INIT, { startTime: time } );
this._init( ).then( ( ) => {
time = Date.now( ) - time;
this.emit( AggregatorFactory.EVENT.AFTER_INIT, { endTime: time } );
trace( `Aggregator ${this.node.browseName} / ${this.node.nodeId} wurde innerhalb von ${ time } ms. initialisiert.\n` );
pFunResolve( this );
} ).catch( pFunReject );
} catch( pErr ) {
pFunReject( pErr );
}
}, 0 );
} );
}
async _init( ) {
let proppertiesRefs = this.node.referenceQuery.HasProperty;
if ( proppertiesRefs && ( this.initScript = proppertiesRefs.InitScript )) {
this.initScript = this.initScript[ 0 ].target.node;
}
if ( proppertiesRefs && ( this.dynComponents = proppertiesRefs.DynComponents )) {
this.dynComponents = this.dynComponents[ 0 ].target.node;
}
await this.recalculateComponents( )._loadAndRunScriptIfNecessary( );
return this;
}
/**
* Alte Callbacks werden von alten Komponenten entfernt,
* Neue Komponenten werden geholt und mit callbacks versehen
*/
recalculateComponents( ) {
this.emit( AggregatorFactory.EVENT.BEFORE_RECALCULATE_COMPONENTS, this.components );
// Calbackst aus alten Komponeneten entfernen
this.unlinkOnWriteValue( );
let componentsRefs = this.node.referenceQuery.HasComponent; // TODO: Aleksej 23.09.2019 Mann konnte hier über this.node.components optimieren. Jetzt gibt es aber Vorteil, dass Nodes über Browsename dirckt als Node erreichbar sind
this.components = { };
if ( componentsRefs ) {
let cmpntRefs;
for ( let cmpntName in componentsRefs ) {
cmpntRefs = componentsRefs[ cmpntName ];
if ( cmpntRefs.length > 1 ) {
throw new Error( `Alle Komponenten innerhalb einer Aggregator dürfen eineindeutige Browsename haben. Innerhalb von Aggregaor ${this.node.browseName} gibt es ${cmpntRefs.length} Komponenten mit Browsename ${cmpntName}.` );
}
// Es wird nur das erste genommen, denn Aggregator erlaubt nicht doppelte Browsenamen innerhalb von einem Aggregator
this.components[ cmpntName ] = cmpntRefs[ 0 ].target.node;
if ( this.components[ cmpntName ] instanceof UAVariable ) {
this.components[ cmpntName ].on( UARTS.Enums.CallbackName.WriteAttribute, UARTS.Enums.AttributesMask.Value, pEvt => { // pEvt, pAnyVal
//this.components[ cmpntName ].onWriteValue( pEvt => { // pEvt, pAnyVal
this.setLastChangeNodeId( pEvt.node.nodeId );
} );
}
}
}
this.emit( AggregatorFactory.EVENT.AFTER_RECALCULATE_COMPONENTS, this.components );
return this;
}
unlinkOnWriteValue( ) {
let oldComponents = this.components || { };
this.emit( AggregatorFactory.EVENT.BEFORE_UNLINK_ON_WRITE_VALUE, this.components );
for ( let cmpnt in oldComponents ) {
cmpnt = oldComponents[ cmpnt ];
// TODO: Hier die Callbacks entfrnen. Zurzeit gibt es noch kein Mechanisus mit dem mann es machen kann.
// Sobald es vorhanden wird, hier implimentieren
}
this.emit( AggregatorFactory.EVENT.AFTER_UNLINK_ON_WRITE_VALUE, this.components );
return this;
}
async _loadScriptFromFileFor( pNodeScript ) {
let file = null, script = '';
if ( this?.module?.AggregatorsPath ) {
let path = UA.Tools.resolveBackToRoot( pNodeScript ).reverse( ).map( pNode => UA.Tools.trimBrowseName( pNode ) ).join( '/' ) + '.js';
path = this.module.AggregatorsPath + path;
try {
FS.findFile( path, pEvt => {
if ( pEvt.fileSize > 0 ) {
file = new UARTS.File( );
file.open( path );
}
} );
} catch ( pErr ) { /* Keine Dateien gefunden */}
if ( file instanceof UARTS.File && file.isOpen ) {
let buff = new Uint8Array( Number( file.size ) ); // Uint8Array kan nicht mit BigInt umgehen. Da hier zimlich kleine Skripts geladen werden, muss Number völlig reichen.
await file.read( buff );
script = utf8array2string( buff );
file.close( );
}
}
return script;
}
async _getParentScriptRec( pNode ) {
if ( !( pNode instanceof UANode ) ) return '';
let
script = '',
superType = null,
superAggregator = null,
componentOf = pNode.lastReferenceQuery.ComponentOf,
componentName = componentOf && Object.keys( componentOf )[ 0 ]; // KomponentenName rausziehen um später es von ReferenceQuery zu hollen
componentOf = componentOf[ componentName ]?.first;
// Manager für Manager nach oben gehen
if ( componentOf && componentOf.isTypeOf( UA.BC.ID.ManagerOfAggregatorType ) ) {
superType = componentOf.typeDefinition;
superAggregator = superType?.lastReferenceQuery?.HasComponent;
superAggregator = superAggregator && superAggregator[ pNode.symbolicName ]?.first
}
// Wenn kein Instanzdeklaration vorhanden ist, gleich TypeDefinition hollen
if ( !superAggregator ) {
superAggregator = pNode.typeDefinition;
}
if ( superAggregator && superAggregator.isTypeOf( BCUA.ID.AggregatorType ) ) {
let
propertys = superAggregator.lastReferenceQuery.HasProperty,
initScript = propertys?.InitScript?.first;
if ( initScript ) {
// Script in ensprechende Datei suchen
script = script || await this._loadScriptFromFileFor( initScript );
// Wenn keine Dateiscript gefunden wurde, direkt im Script nachschlagen
script = script || initScript?.value?.value;
}
// Wenn auch in Datei nichts gefunden wurde, bei ParentNode nachschauen
script = script || await this._getParentScriptRec( superAggregator?.node );
}
return script;
}
async _loadAndRunScriptIfNecessary( ) {
if ( this.hasInitScript( ) ) {
// Suchen in der Datei für Aggregator selbst
let script = await this._loadScriptFromFileFor( this.initScript );
// Wenn keine zuständige Script ind Datei gefunden wurde
if (!script) {
// Suchen in Node selbst
script = this?.initScript?.value?.value || '';
// wenn nicht gefunden, suchen im Type
if ( !script ) {
script = await this._getParentScriptRec( this.node );
}
}
if ( script && !this?.initScript?.value?.value ) {
this.initScript.writeAttribute( UARTS.Enums.AttributeId.Value, new UAValueString( script || '' ) );
}
if ( script && AggregatorFactory.preventDebug ) {
script = script.replace( UA.Tools.REGEXP.DEBUGGER_STATEMENT, '' );
}
// TODO: entfernen. Nur für testzwäcke
//script = 'debugger; // Muss aus AggregatorFactory._loadAndRunScriptIfNecessary entfernt werden\n' + script;
this.emit( AggregatorFactory.EVENT.BEFORE_SCRIPT_CALL, script );
try {
script = new Function( script );
let result;
// Bei Instanzdeklarations kein Call machen
if( !this.node.isInstanceDeclaration( ) ) {
result = script.call( globalThis, this, this.share );
}
this.script = script;
this.emit( AggregatorFactory.EVENT.AFTER_SCRIPT_CALL, result );
} catch ( pErr ) {
console.error( `Call Aggreagtor Script "${UA.Tools.resolveBackToRoot( this.initScript ).reverse( ).map( n => n.browseName ).join( '/' ) }" Error. NodeId of Aggregator: ${this.node.nodeId}`, pErr );
this.emit( AggregatorFactory.EVENT.SCRIPT_ERROR, pErr );
}
}
return this;
}
hasInitScript( ) {
return this.initScript instanceof UAVariable;
}
hasDynComponents( ) {
return this.dynComponents instanceof UAVariable;
}
setLastChangeNodeId( pMixNodeId ) {
if ( pMixNodeId instanceof UANode ) pMixNodeId = pMixNodeId.nodeId;
if ( this.hasEventListenerFor( AggregatorFactory.EVENT.BEFORE_SET_CHENGED_NODEID ) ) {
this.emit( AggregatorFactory.EVENT.BEFORE_SET_CHENGED_NODEID, {
newNodeId: pMixNodeId,
oldNodeId: this.getLastChangeNodeId( )
} );
}
if ( pMixNodeId instanceof UANodeId && this.node instanceof UAVariable ) {
this.node.writeAttribute( UARTS.Enums.AttributeId.Value, new UAValueNodeId( pMixNodeId ) );
//this.node.value = new UAValueNodeId( pNodeId );
}
return this;
}
getLastChangeNodeId( ) {
if ( this.node instanceof UAVariable && this.node.value ) {
return UANodeId( this.node.value );
}
return null;
}
}
/**
* Liste von möglichen Events
* @type Object
*/
AggregatorFactory.EVENT = Object.freeze( {
BEFORE_INIT : 'AggregatorFactory.EVENT.BEFORE_INIT',
BEFORE_SCRIPT_CALL : 'AggregatorFactory.EVENT.BEFORE_SCRIPT_CALL',
BEFORE_SET_CHENGED_NODEID : 'AggregatorFactory.EVENT.BEFORE_SET_CHENGED_NODEID',
BEFORE_UNLINK_ON_WRITE_VALUE : 'AggregatorFactory.EVENT.BEFORE_UNLINK_ON_WRITE_VALUE',
BEFORE_RECALCULATE_COMPONENTS : 'AggregatorFactory.EVENT.BEFORE_RECALCULATE_COMPONENTS',
AFTER_INIT : 'AggregatorFactory.EVENT.AFTER_INIT',
AFTER_SCRIPT_CALL : 'AggregatorFactory.EVENT.AFTER_SCRIPT_CALL',
AFTER_UNLINK_ON_WRITE_VALUE : 'AggregatorFactory.EVENT.AFTER_UNLINK_ON_WRITE_VALUE',
AFTER_RECALCULATE_COMPONENTS : 'AggregatorFactory.EVENT.AFTER_RECALCULATE_COMPONENTS',
SCRIPT_ERROR : 'AggregatorFactory.EVENT.SCRIPT_ERROR',
} );
/**
* Statische Methode welche initialisiert alle Instanzen von Aggregator
* @type Function
*/
AggregatorFactory.initInstances = async ( pModule, pObjSharedData ) => {
if ( Object.prototype.toString.call( pModule ) !== '[object Module]' ) throw new TypeError( `Ungültige Argument "pModule". Muss "[object Module]" ist aber "${Object.prototype.toString.call( pModule )}"` );
let
instances = BCUA.ID.AggregatorType.node.referenceQuery.TypeDefinitionOf,
result = [ ],
aggregInst = null,
inited = [ ],
nodeIdString= null,
factory = null;
for ( let instList in instances ) {
instList = instances[ instList ];
for ( let aggregRef of instList ) {
aggregInst = aggregRef.target.node;
if ( aggregInst.nodeId.ns !== pModule.NamespaceID ) continue;
nodeIdString = aggregInst.nodeId.toString( );
if ( inited.includes( nodeIdString ) ) continue;
factory = new AggregatorFactory( aggregInst, pModule, pObjSharedData );
inited.push( nodeIdString );
result.push( factory.build( ) ); // Asyncrone fnktion wird später ausgeführt
}
}
return Promise.all( result );
};
/**
* Statische Eigenschaft welche sorgt dafür, dass alle Debugger-Statments aus aufgeruftem Script-String entfernt werden.
* Achtung! Es wird nur aus dem übergebene String entfernt. Alle aufgerufene Funktionen die debugger haben werden trotzdem diesen aufrufen.
* @type Boolean
*/
AggregatorFactory.preventDebug = false;
\ No newline at end of file
/**
* Konstanenliste für "http://bitctrl.de/UA/BCUA/"-Modell
* @module bcuaconst.js
* @author Geniriert durch UARTS @ BitCtrl Systems GmbH ModellGenerator
*/
/* eslint-disable no-undefined */
/* eslint-disable no-console */
/*
* global
* UARTS, UAValue, UAValueString, UAValueSByte,
* UAObject, UATypeDescription, UAVariable,
* UAValueExtensionObject
*/
/**
* Name des Modells "bcua"
* @constant ModellName
*/
export const ModellName = 'bcua';
/**
* Absolute Pfad zu "bcua" Modellverzeichnis
* @constant ModellPath
*/
export const ModellPath = import.meta.dirname.replace( /js[\\|\/]libs[\\|\/]*$/, '' );
// Pfadtrenner benutzte in ModellPath
const PATH_SPLITTER = Array.from( ModellPath ).pop( );
// NodeId von Root/Objects/Server/Namespaces
const NAMESPACES = UANodeId( 'i=11715' );
/**
* Absolute Pfad zu "bcua" Methoden
* @constant MethodesPath
*/
export const MethodesPath = ModellPath + 'js' + PATH_SPLITTER + 'methods/';
/**
* Absolute Pfad zu "bcua" Aggregatoren
* @constant AggregatorsPath
*/
export const AggregatorsPath = ModellPath + 'js' + PATH_SPLITTER + 'aggregators/';
/**
* Nodeset-XML-Filename von "bcua" Modell
* @constant ModellNodeset
*/
export const ModellNodeset = `${ModellName}.nodeset.xml`;
/**
* Uniform Resource Identifier (URI) für Namensraum von "bcua"
* http://bitctrl.de/UA/BCUA/
* @constant NamespaceURI
*/
export const NamespaceURI = 'http://bitctrl.de/UA/BCUA/';
// Testen, ob Nodeset (Namespaces-Object eine Componente mit Browsename gleich NamespaceURI besitzt ) bereits geladen ist, wenn nicht laden
if ( NAMESPACES.node.components.filter( pCopm => pCopm.browseName.replace( /^\d+:/, '' ) === NamespaceURI ).length <= 0 ) {
try {
UARTS.loadNodeset( ModellPath + ModellNodeset );
} catch( pErr ) {
throw new Error( `Nodeset ${ModellNodeset} für Modell "${ModellName}" konnte aus "${ModellPath}" nicht geladen werden. Fehler: ${String(pErr)}.` );
}
}
/**
* Namensraum ID von "bcua" inerhalb von UARTS
* @constant NamespaceID
*/
export const NamespaceID = ( ( ) => {
return Array.isArray( UARTS.namespaces ) ? UARTS.namespaces.indexOf( NamespaceURI ) : -1;
} )( );
/**
* Map von NodeId's aus "http://bitctrl.de/UA/BCUA/"-Modell
* @readonly
* @constant ID
*/
export const ID = Object.freeze( {
/**
* Vereinigt mehrere Datenquellen und bildet eine Schnittstelle zu großen Datenmengen ab. Wert des Aggregators ist NodeId von aggregierte Komponente welche sich zuletzt geändert hat.
* @type {UANodeId}
*/
"AggregatorType" : new UANodeId( `ns=${NamespaceID};i=2003` ),
/**
* --- NO DESCRIPTION ---
* @type {UANodeId}
*/
"BcUaObjectType" : new UANodeId( `ns=${NamespaceID};i=1002` ),
/**
* --- NO DESCRIPTION ---
* @type {UANodeId}
*/
"HasSyncConfig" : new UANodeId( `ns=${NamespaceID};i=4003` ),
/**
* --- NO DESCRIPTION ---
* @type {UANodeId}
*/
"HasSyncMode" : new UANodeId( `ns=${NamespaceID};i=4002` ),
/**
* Typen-Sammulng von BitCtrl Systems GmbH
* @type {UANodeId}
*/
"BcUaType" : new UANodeId( `ns=${NamespaceID};i=2002` ),
/**
* --- NO DESCRIPTION ---
* @type {UANodeId}
*/
"BCSyncType" : new UANodeId( `ns=${NamespaceID};i=3002` ),
/**
* --- NO DESCRIPTION ---
* @type {UANodeId}
*/
"SyncTargetType" : new UANodeId( `ns=${NamespaceID};i=2001` ),
/**
* Ist ein Oberobjekt welche verschiedene Funktionen für ein Aggregator zu verfügung stellt und der Aggregator selbst beinhaltet
* @type {UANodeId}
*/
"ManagerOfAggregatorType" : new UANodeId( `ns=${NamespaceID};i=1003` ),
/**
* --- NO DESCRIPTION ---
* @type {UANodeId}
*/
"HasHttpServerRepresentation" : new UANodeId( `ns=${NamespaceID};i=4004` ),
/**
* --- NO DESCRIPTION ---
* @type {UANodeId}
*/
"HttpServerType" : new UANodeId( `ns=${NamespaceID};i=1001` ),
} );
/**
* Map von Enumeration's aus "http://bitctrl.de/UA/BCUA/"-Modell
* @readonly
* @constant ENUM
*/
export const ENUM = Object.freeze( {
/**
* --- NO DESCRIPTION ---
* @enum {(Number|String)}
*/
"BCSyncType" : {
0 : '', '' : 0,
},
} );