123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- import type { NamespaceTable } from "./NameSpaceTable";
- import { coerceNodeId, NodeId } from "./NodeId";
- import { UAReference } from "./UAReference";
- import { assert } from "@/util/assert";
- import { XMLElem, type IToXML } from "@/util/XmlElem";
- import { UARolePermission } from "./UARolePermission";
- import { type IAddressSpace } from "./IAddressSpace";
- import { ReferenceTypeIds } from "./opcua_node_ids";
- import { UALocalizedText } from "./UALocalizedText";
- export class UABaseNode implements IToXML{
- public static nullBaseNode=new UABaseNode({browseName: "", addressSpace: {} as IAddressSpace, nodeId: NodeId.nullNodeId});
- public nodeId!: NodeId;
- public nodeClass="UABaseNode";
- public browseName!: string;
- public addressSpace!: IAddressSpace
- public displayName: UALocalizedText[]=[];
- public description: UALocalizedText[]=[];
- public symbolicName?: string;
- public releaseStatus!: string;
- public hasNoPermissions!: boolean;
- public writeMask!: number;
- public userWriteMask!: number;
- public category: string[]=[];
- public documentation?: string;
- public accessRestriction?: number;
- public rolePermissions: UARolePermission[]=[];
- public references: UAReference[]=[];
-
- constructor(options: UABaseNodeOptions) {
- Object.assign(this, options);
- }
- reIndex(nst: NamespaceTable, onst: NamespaceTable) {
- const nsName=onst.getUri(this.nodeId.namespace);
- assert(nsName!=undefined)
- const newIndex=nst.getIndex(nsName);
- assert(newIndex!=undefined)
- this.nodeId.namespace=newIndex;
- for(const uaref of this.references) {
- uaref.reIndex(nst, onst);
- }
- }
- getParent(): UABaseNode|null {
- const ref=this.getParentRef();
- if(ref?.fromNode===this)
- return ref.toNode;
- if(ref?.toNode===this)
- return ref.fromNode;
- return null;
- }
- setParent(newParentNode: UABaseNode, newRefType: string) {
- const hierReferences=this.addressSpace.getSubTreeAsList("ns=0;i="+ReferenceTypeIds.HierarchicalReferences);
- let href;
- for(const r of this.references) { //find current href in references
- href=hierReferences.find((n) => {n.browseName===r.referenceType})
- }
- for(let i=this.references.length;i--;i>=0) { //remove current href from references
- const r=this.references[i];
- if(r.referenceType==href?.browseName)
- this.references.splice(i,1);
- }
- const newRefA=new UAReference(this.nodeId, newRefType, newParentNode.nodeId, true);
- newRefA.fromNode=this;
- newRefA.toNode=newParentNode;
- this.references.push(newRefA);
- const newRefB=new UAReference(newParentNode.nodeId, newRefType, this.nodeId, false);
- newRefB.fromNode=this;
- newRefB.toNode=newParentNode;
- this.references.push(newRefB);
- }
- getParentRef(): UAReference|null{
- for(const ref of this.references) {
- switch(ref.referenceType) {
- case 'HasComponent':
- case 'HasOrderedComponent':
- case 'Organizes':
- case 'HasProperty':
- case 'HasSubtype':
- case 'HasAddIn':
- if(ref.fromNode===this&&ref.isForward===false)
- return ref;
- if(ref.toNode===this&&ref.isForward===true)
- return ref;
- }
- }
- return null;
- }
- getChildren(): UABaseNode[] {
- const children: UABaseNode[]=[];
- for(const ref of this.references) {
- switch(ref.referenceType) {
- case 'HasComponent':
- case 'HasOrderedComponent':
- case 'Organizes':
- case 'HasProperty':
- case 'HasSubtype':
- case 'HasAddIn':
- if(ref.isForward&&ref.fromNode===this) {
- if(!children.includes(ref.toNode))
- children.push(ref.toNode);
- }
- if(!ref.isForward&&ref.toNode===this) {
- if(!children.includes(ref.fromNode))
- children.push(ref.fromNode);
- }
- break;
- }
- }
- return children;
- }
- getModellingRule(): string|undefined{
- for(const ref of this.references) {
- if(ref.referenceType == "HasModellingRule"){
- return ref.toNode.browseName;
- }
- }
- return undefined;
- }
- //TODO: check name, function, etc. or delete
- getInstanceDecl(nid: string, mrType: string){
- const aggregates = (this.addressSpace?.getSubTreeAsList("i="+ReferenceTypeIds.Aggregates)||[]);
- const aggStrings:String[] = [];
- aggregates.forEach((item) => {
- aggStrings.push(item.browseName);
- })
- const node = (this.addressSpace.findNode(nid)) as UABaseNode;
- const res:string[] = [];
- for(const item of node.references) {
- if(aggStrings.includes(item.referenceType) && item.isForward){
- const mr = item.toNode.getModellingRule()||"";
- if(mr == mrType){
- res.push(item.toNode.browseName);
- }
- }
- }
- return res;
- }
-
- getNodeVersion(nid:string){
- const node = this.addressSpace.findNode(nid);
- if(!node)
- return "";
- const refs = node.references;
- refs.forEach((ref)=>{
- if(ref.toNode.browseName == "NodeVersion" && ref.isForward){
- return ref.toNode.nodeId.toString()
- }else if(ref.fromNode.browseName == "NodeVersion" && !ref.isForward){
- return ref.fromNode.nodeId.toString()
- }
- })
- }
- resolveReferences(nm: Map<string, UABaseNode>) {
- for(const ref of this.references) {
- const fromNode=nm.get(ref.fromRef.toString())
- if(fromNode)
- ref.fromNode=fromNode;
- const toNode=nm.get(ref.toRef.toString())
- if(!toNode)
- continue; //TODO: if we cant find the node; the parser is still incomplete or the nodeset is broken
-
- ref.toNode=toNode;
- if(ref.fromNode.nodeId.toString()===this.nodeId.toString()){ //add this reference to referenced node
- //Bug? when loading from filedrop; fromNode is a proxy; this is not.
- if(!ref.toNode.references.includes(ref))
- ref.toNode.references.push(ref);
- }
- }
- }
- static fromXML(xmlObject: any, addressSpace: IAddressSpace): UABaseNode{
- const references:UAReference[]=[];
- const nodeId=coerceNodeId(xmlObject['@_NodeId']);
- for(const xmlref of xmlObject['References']?.['Reference']||[]) {
- references.push(UAReference.fromXML(xmlref, nodeId));
- }
- const displayNames:UALocalizedText[]=[];
- for(const xmldn of xmlObject['DisplayName']||[]) {
- displayNames.push(UALocalizedText.fromXML("DisplayName", xmldn));
- }
- const descriptions:UALocalizedText[]=[];
- for(const desc of xmlObject['Description']||[]) {
- descriptions.push(UALocalizedText.fromXML("Description", desc));
- }
- const permissions:UARolePermission[]=[];
- for(const perm of xmlObject['RolePermissions']?.['RolePermission']||[]) {
- permissions.push(UARolePermission.fromXML(perm));
- }
- const ua=new UABaseNode({addressSpace: addressSpace,
- nodeId: nodeId,
- browseName: xmlObject['@_BrowseName'],
- writeMask: Number(xmlObject['@_WriteMask']||0),
- userWriteMask: Number(xmlObject['@_UserWriteMask']||0),
- accessRestriction: xmlObject['@_AccessRestrictions'],
- hasNoPermissions: xmlObject['@_HasNoPermissions']||false,
- symbolicName: xmlObject['@_SymbolicName'],
- releaseStatus: xmlObject['@_ReleaseStatus']||"Released",
- displayName: displayNames,
- description: descriptions,
- category: xmlObject['Category'],
- documentation: xmlObject['Documentation']?.['#text'],
- references: references,
- rolePermissions: permissions,
- });
- return ua;
- }
- toXML(lnst: NamespaceTable, gnst: NamespaceTable): XMLElem {
- const nid=UABaseNode.localNodeId(this.nodeId, lnst, gnst);
- const elem =new XMLElem('UABaseNode');
- elem.attr('NodeId', nid.toString())
- .attr('BrowseName', this.browseName)
- .attr('WriteMask', this.writeMask.toString())
- .attr('UserWriteMask', this.userWriteMask.toString())
- .attr('AccessRestriction', this.accessRestriction?.toString())
- .attr('HasNoPermissions', this.hasNoPermissions)
- .attr('SymbolicName', this.symbolicName)
- .attr('ReleaseStatus',this.releaseStatus)
- .attr('Documentation', this.documentation);
-
- for(const c in this.category) {
- elem.attr('Category', c);
- }
- const refs=new XMLElem('References');
- for(const ref of this.references) {
- if(ref.fromNode.nodeId.toString()==this.nodeId.toString()) //on load resolveReferences() duplicates references to both sides. skip them for export
- refs.add(ref.toXML(lnst, gnst));
- }
- if(refs.elements.length>0)
- elem.add(refs);
- for(const dn of this.displayName)
- elem.add(dn.toXML());
- for(const desc of this.description)
- elem.add(desc.toXML());
- const perms=new XMLElem('RolePermissions')
- for(const perm of this.rolePermissions)
- perms.add(perm.toXML());
- if(perms.elements.length>0)
- elem.add(perms);
- return elem;
- }
- static localNodeId(nodeId: NodeId, lnst: NamespaceTable, gnst: NamespaceTable) {
- const ns_uri=gnst.getUri(nodeId.namespace);
- assert(ns_uri);
- const nsIdx=lnst.getIndex(ns_uri);
- assert(nsIdx);
- const tmpNode= coerceNodeId(nodeId.toString());
- tmpNode.namespace=nsIdx;
- return tmpNode;
- }
- }
- export interface UABaseNodeOptions {
- addressSpace: IAddressSpace;
- nodeId: NodeId;
- browseName: string;
- writeMask?: number;
- userWriteMask?: number;
- accessRestriction?: number;
- hasNoPermissions?: boolean;
- symbolicName?: string;
- releaseStatus?: string;
- displayName?: UALocalizedText[];
- description?: UALocalizedText[];
- category?: string[];
- documentation?: string;
- references?: UAReference[];
- rolePermissions?: UARolePermission[];
- }
|