Skip to content

Commit

Permalink
implement EffectiveTransitionTime
Browse files Browse the repository at this point in the history
implement automatic update of main state
machine.lastUpdate.effectiveTransitionTime, when
the state of a sub state machine is changing
and the conditions are met.
  • Loading branch information
erossignon committed Jul 9, 2023
1 parent f6831cc commit d5493c1
Show file tree
Hide file tree
Showing 8 changed files with 405 additions and 201 deletions.
35 changes: 28 additions & 7 deletions packages/node-opcua-address-space-base/source/clone_helper.ts
@@ -1,23 +1,43 @@
import { assert } from "node-opcua-assert";
import { checkDebugFlag, make_debugLog, make_warningLog } from "node-opcua-debug";
import { checkDebugFlag, make_debugLog, make_errorLog, make_warningLog } from "node-opcua-debug";
import { BrowseDirection, NodeClass, QualifiedName } from "node-opcua-data-model";
import { makeNodeId, sameNodeId } from "node-opcua-nodeid";
import { ReferenceTypeIds } from "node-opcua-constants";

import chalk from "chalk";

import { UAObject } from "./ua_object";
import { UAVariable } from "./ua_variable";
import { UAMethod } from "./ua_method";
import { UAObjectType } from "./ua_object_type";
import { UAVariableType } from "./ua_variable_type";
import { BaseNode } from "./base_node";
import { makeNodeId, sameNodeId } from "node-opcua-nodeid";
import { ReferenceTypeIds } from "node-opcua-constants";
import { UAReference } from "./ua_reference";
import { IAddressSpace } from "./address_space";

const debugLog = make_debugLog("CLONE");
const doDebug = checkDebugFlag("CLONE");
const warningLog = make_warningLog("CLONE");

const errorLog = make_errorLog(__filename);
const doTrace = checkDebugFlag("INSTANTIATE");
const traceLog = errorLog;

type UAConcrete = UAVariable | UAObject | UAMethod;

export function fullPath(node: BaseNode): string {
const browseName = node.browseName.toString();

const parent = node.findReferencesExAsObject("Aggregates", BrowseDirection.Inverse)[0];
if (parent) {
return fullPath(parent) + "/" + browseName;
}
return browseName;
}
export function fullPath2(node: BaseNode): string {
return fullPath(node) + " (" + node.nodeId.toString() + ")";
}

//
// case 1:
// /-----------------------------\
Expand All @@ -42,7 +62,7 @@ type UAConcrete = UAVariable | UAObject | UAMethod;
// \-----------------------------/
// ^ |
// | | +----------+
// | +-------| Folder1  |
// | +-------| Folder1 |
// | +----------+
// | |
// | +--------------|- (EnabledState) (shadow element)
Expand All @@ -52,7 +72,7 @@ type UAConcrete = UAVariable | UAObject | UAMethod;
// \-----------------------------/
// |
// | | +----------+
// | +-------| Folder1  |
// | +-------| Folder1 |
// | +----------+
// | |
// | +--------------|- (EnabledState)
Expand Down Expand Up @@ -124,6 +144,8 @@ export class CloneHelper {
TO extends UAObject | UAVariable | UAMethod | UAObjectType | UAVariableType,
TC extends UAObject | UAVariable | UAMethod
>(clonedNode: TC, originalNode: TO) {
doTrace &&
traceLog(chalk.yellow("registerClonedObject"), "originalNode = ", fullPath2(originalNode), fullPath2(clonedNode));
this.mapOrgToClone.set(originalNode.nodeId.toString(), {
cloned: clonedNode,
original: originalNode
Expand All @@ -144,11 +166,10 @@ export class CloneHelper {
}
base = base.subtypeOfObj;
}
} else {
}
// find subTypeOf
}
public getCloned(originalNode: UAVariableType | UAObjectType): UAObject | UAVariable | UAMethod | null {
public getCloned(contextNode: BaseNode, originalNode: UAVariableType | UAObjectType): UAObject | UAVariable | UAMethod | null {
const info = this.mapOrgToClone.get(originalNode.nodeId.toString());
if (info) {
return info.cloned;
Expand Down
Expand Up @@ -27,7 +27,7 @@ export interface CloneExtraInfo {
level: number;
pad(): string;
registerClonedObject(clonedNode: BaseNode, originalNode: BaseNode): void;
getCloned(originalObject: BaseNode): BaseNode | null;
getCloned(contextNode: BaseNode, originalObject: BaseNode): BaseNode | null;
}

export const makeDefaultCloneExtraInfo = (): CloneExtraInfo => new CloneHelper();
Expand Down
@@ -0,0 +1,99 @@
import { assert } from "node-opcua-assert";
import { BaseNode, UAMethod, UAObject, UAReference, UAVariable, CloneFilter } from "node-opcua-address-space-base";

import { checkDebugFlag, make_debugLog, make_warningLog, make_errorLog } from "node-opcua-debug";

import { _clone_hierarchical_references } from "./base_node_private";

// const debugLog = make_debugLog(__filename);
const doDebug = checkDebugFlag(__filename);
const warningLog = make_warningLog(__filename);
const errorLog = make_errorLog(__filename);
const doTrace = checkDebugFlag("INSTANTIATE");
const traceLog = errorLog;

export class MandatoryChildOrRequestedOptionalFilter implements CloneFilter {
private readonly instance: BaseNode;
private readonly optionalsMap: any;
private readonly references: UAReference[];

constructor(instance: BaseNode, optionalsMap: any) {
// should we clone the node to be a component or propertyOf of a instance
assert(optionalsMap !== null && typeof optionalsMap === "object");
assert(null !== instance);
this.optionalsMap = optionalsMap;
this.instance = instance;
this.references = instance.allReferences();
}

public shouldKeep(node: BaseNode): boolean {
const addressSpace = node.addressSpace;

const alreadyIn = this.references.filter((r: UAReference) => {
const n = addressSpace.findNode(r.nodeId)!;
// istanbul ignore next
if (!n) {
warningLog(" cannot find node ", r.nodeId.toString());
return false;
}
return n.browseName!.name!.toString() === node.browseName!.name!.toString();
});

if (alreadyIn.length > 0) {
assert(alreadyIn.length === 1, "Duplication found ?");
// a child with the same browse name has already been install
// probably from a SuperClass, we should ignore this.
return false; // ignore
}

const modellingRule = node.modellingRule;

switch (modellingRule) {
case null:
case undefined:
// istanbul ignore next
doTrace &&
traceLog(
"node ",
node.browseName.toString(),
node.nodeId.toString(),
" has no modellingRule ",
node.parentNodeId?.toString()
);
/**
* in some badly generated NodeSet2.xml file, the modellingRule is not specified
*
* but in some other NodeSet2.xml, this means that the data are only attached to the Type node and shall not be
* instantiate in the corresponding instance (example is the state variable of a finite state machine that are only
* defined in the Type node)
*
* we should not consider it as an error, and treat it as not present
*/
return false;

case "Mandatory":
return true; // keep;
case "Optional":
// only if in requested optionals
return node.browseName!.name! in this.optionalsMap;
case "OptionalPlaceholder":
return false; // ignored
default:
return false; // ignored
}
}

public filterFor(childInstance: UAVariable | UAObject | UAMethod): CloneFilter {
const browseName: string = childInstance.browseName.name!;

let map = {};

if (browseName in this.optionalsMap) {
map = this.optionalsMap[browseName];
}
// istanbul ignore next
doTrace && traceLog("filterFor ", browseName, map);
const newFilter = new MandatoryChildOrRequestedOptionalFilter(childInstance, map);
return newFilter;
}
}

0 comments on commit d5493c1

Please sign in to comment.