diff --git a/code/package-lock.json b/code/package-lock.json index be5dd1d..1ada898 100644 --- a/code/package-lock.json +++ b/code/package-lock.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "axios": "^1.7.2", + "cors": "^2.8.5", "express": "^4.19.2", "json-ignore": "^0.4.0", "sdp-transform": "^2.14.2", @@ -646,6 +647,18 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1474,6 +1487,14 @@ "node": ">=0.10.0" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", @@ -2887,6 +2908,15 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, "cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -3470,6 +3500,11 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, "object-inspect": { "version": "1.13.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", diff --git a/code/package.json b/code/package.json index a94dec0..cccb4f3 100644 --- a/code/package.json +++ b/code/package.json @@ -23,6 +23,7 @@ "license": "ISC", "dependencies": { "axios": "^1.7.2", + "cors": "^2.8.5", "express": "^4.19.2", "json-ignore": "^0.4.0", "sdp-transform": "^2.14.2", diff --git a/code/src/NCModel/Blocks.ts b/code/src/NCModel/Blocks.ts index dce2a42..d901bbd 100644 --- a/code/src/NCModel/Blocks.ts +++ b/code/src/NCModel/Blocks.ts @@ -6,20 +6,34 @@ import { INotificationContext } from '../SessionManager'; import { myIdDecorator, NcBlockMemberDescriptor, + NcBulkValuesHolder, NcClassDescriptor, NcElementId, NcMethodDescriptor, NcMethodStatus, NcObject, + NcObjectPropertiesHolder, + NcObjectPropertiesSetValidation, NcParameterDescriptor, + NcPropertyChangeType, NcPropertyConstraints, NcPropertyDescriptor, - NcTouchpoint } from './Core'; + NcPropertyId, + NcPropertyRestoreNotice, + NcPropertyRestoreNoticeType, + NcPropertyValueHolder, + NcRestoreMode, + NcRestoreValidationStatus, + NcTouchpoint, + RestoreArguments } from './Core'; +import { ExampleControl } from './Features'; export class NcBlock extends NcObject { public static staticClassID: number[] = [ 1, 1 ]; + public static readonly RootOid: number = 1; + @myIdDecorator('1p1') public override classID: number[] = NcBlock.staticClassID; @@ -31,6 +45,10 @@ export class NcBlock extends NcObject public memberObjects: NcObject[]; + protected maxMembers: number | null; + + protected rootContext: IRootContext | null; + public constructor( oid: number, constantOid: boolean, @@ -42,9 +60,16 @@ export class NcBlock extends NcObject enabled: boolean, memberObjects: NcObject[], description: string, - notificationContext: INotificationContext) + notificationContext: INotificationContext, + rootContext: IRootContext | null, + maxMembers: number | null = null, + isRebuildable: boolean = false) { - super(oid, constantOid, ownerObject, role, userLabel, touchpoints, runtimePropertyConstraints, description, notificationContext); + super(oid, constantOid, ownerObject, role, userLabel, touchpoints, runtimePropertyConstraints, description, notificationContext, isRebuildable); + + this.maxMembers = maxMembers; + + this.rootContext = rootContext; this.enabled = enabled; this.memberObjects = memberObjects; @@ -93,7 +118,7 @@ export class NcBlock extends NcObject return new CommandResponseError(handle, NcMethodStatus.BadOid, 'OID could not be found'); } - public override InvokeMethod(socket: WebSocketConnection, oid: number, methodId: NcElementId, args: { [key: string]: any; } | null, handle: number): CommandResponseNoValue + public override InvokeMethod(oid: number, methodId: NcElementId, args: { [key: string]: any; } | null, handle: number): CommandResponseNoValue { if(oid == this.oid) { @@ -303,7 +328,7 @@ export class NcBlock extends NcObject return new CommandResponseError(handle, NcMethodStatus.InvalidRequest, 'Invalid arguments provided'); } default: - return super.InvokeMethod(socket, oid, methodId, args, handle); + return super.InvokeMethod(oid, methodId, args, handle); } } @@ -388,10 +413,13 @@ export class NcBlock extends NcObject return currentClassDescriptor; } - public UpdateMembers(memberObjects: NcObject[]) + public UpdateMembers(memberObjects: NcObject[], notify: boolean = false) { this.memberObjects = memberObjects; this.members = this.memberObjects.map(x => x.GenerateMemberDescriptor()); + + if(notify) + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(2, 2), NcPropertyChangeType.ValueChanged, this.members, null); } public GenerateMemberDescriptors(recurse: boolean) : NcBlockMemberDescriptor[] @@ -463,6 +491,63 @@ export class NcBlock extends NcObject return new Array(); } + public FindMemberByRolePath(rolePath: string[]) : NcObject | null + { + if(rolePath.length == 1 && rolePath[0] == this.role) + { + return this; + } + else if(rolePath.length > 1 && rolePath[0] == this.role) + { + let childRole = rolePath[1]; + if(this.memberObjects != null) + { + let member = this.memberObjects.find(e => e.role === childRole); + if(member) + { + if(rolePath.length == 2) + { + return member; + } + else if(member instanceof NcBlock) + { + let furtherPath = rolePath.splice(1); + return member.FindMemberByRolePath(furtherPath); + } + else + return null; + } + else + return null; + } + else + return null; + } + else + return null; + } + + public override GetAllProperties(recurse: boolean) : NcObjectPropertiesHolder[] + { + let holders = [ + new NcObjectPropertiesHolder(this.GetRolePath(), [], [ + new NcPropertyValueHolder(new NcPropertyId(2, 1), "enabled", "NcBoolean", true, this.enabled), + new NcPropertyValueHolder(new NcPropertyId(2, 2), "members", "NcBlockMemberDescriptor", true, this.members) + ], [], this.isRebuildable) + ]; + + holders[0].values = holders[0].values.concat(super.GetAllProperties(recurse)[0].values); + + if(recurse) + { + this.memberObjects.forEach(member => { + holders = holders.concat(member.GetAllProperties(recurse)); + }); + } + + return holders + } + public GenerateMemberDescriptorsByRole(role: string, caseSensitive: boolean, matchWholeString: boolean, recurse: boolean) : NcBlockMemberDescriptor[] { if(this.memberObjects != null) @@ -528,12 +613,144 @@ export class NcBlock extends NcObject else return new Array() } + + public GetRolePathUrls(): string[] + { + let urls = new Array(); + + urls = urls.concat(this.GetRolePathUrl()); + + this.memberObjects.forEach(member => { + if(member instanceof NcBlock) + urls = urls.concat(member.GetRolePathUrls()); + else + urls = urls.concat(member.GetRolePathUrl()); + }); + + return urls; + } + + public GetRolePathForMember(role: string): string[] + { + return this.GetRolePath().concat(role); + } + + public ReconstructMembers(members: NcBlockMemberDescriptor[], dataSet: NcBulkValuesHolder, applyChanges: Boolean = true) : [NcPropertyRestoreNotice | null, NcObjectPropertiesSetValidation[]] + { + //Left intentionally empty as "virtual" so that rebuildable blocks override this with the desired behaviour + return [null, []]; + } + + public override Restore(restoreArguments: RestoreArguments, applyChanges: Boolean) : NcObjectPropertiesSetValidation[] + { + let validationEntries = new Array(); + + let myRestoreData = restoreArguments.dataSet.values.find(f => f.path.join('.') == this.GetRolePath().join('.')) + if(myRestoreData) + { + let myNotices = new Array(); + + myRestoreData.values.forEach(propertyData => + { + let propertyId = NcElementId.ToPropertyString(propertyData.id); + + if(restoreArguments.restoreMode == NcRestoreMode.Rebuild) + { + if(propertyId != '1p6' && propertyId != '2p2') + myNotices.push(new NcPropertyRestoreNotice( + propertyData.id, + propertyData.name, + NcPropertyRestoreNoticeType.Warning, + "Property cannot be changed and will be left untouched")); + else + { + //Perform further validation + if(propertyId == '2p2') + { + //Structural changes to the members + let membersData = propertyData.value as NcBlockMemberDescriptor[]; + if(membersData) + { + //TODO: Might need to include the list of members in the outcomes of ReconstructMembers so that we can skip them from further restore actions because they would have already been applied when creating the member objects + + let outcomes = this.ReconstructMembers(membersData, restoreArguments.dataSet, applyChanges); + if(outcomes[0] != null) + myNotices.push(outcomes[0]); + + validationEntries = validationEntries.concat(outcomes[1]); + } + else + { + myNotices.push(new NcPropertyRestoreNotice( + propertyData.id, + propertyData.name, + NcPropertyRestoreNoticeType.Warning, + `Cannot reconstruct members because the members data is null`)); + console.log(`Cannot reconstruct members because the members data is null`); + } + } + else if(applyChanges) + { + //Perform further validations + this.Set(this.oid, propertyData.id, propertyData.value, 0); + } + } + } + else + { + if(propertyId == '2p2') + { + if(this.isRebuildable) + myNotices.push(new NcPropertyRestoreNotice( + propertyData.id, + propertyData.name, + NcPropertyRestoreNoticeType.Warning, + "Property cannot be changed and will be left untouched unless restoreMode is changed to Rebuild")); + else + myNotices.push(new NcPropertyRestoreNotice( + propertyData.id, + propertyData.name, + NcPropertyRestoreNoticeType.Warning, + "Property cannot be changed and will be left untouched")); + } + else if(propertyId != '1p6') + myNotices.push(new NcPropertyRestoreNotice( + propertyData.id, + propertyData.name, + NcPropertyRestoreNoticeType.Warning, + "Property cannot be changed and will be left untouched")); + else if(applyChanges) + { + //Perform further validations + this.Set(this.oid, propertyData.id, propertyData.value, 0); + } + } + }); + + validationEntries.push(new NcObjectPropertiesSetValidation(this.GetRolePath(), NcRestoreValidationStatus.Ok, myNotices, myNotices.length > 0 ? 'Some properties have notices' : null)); + } + + if(restoreArguments.recurse) + { + this.memberObjects.forEach(member => { + validationEntries = validationEntries.concat(member.Restore(restoreArguments, applyChanges)); + }); + } + + return validationEntries; + } +} + +export interface IRootContext +{ + AllocateOid(path: string) : number } -export class RootBlock extends NcBlock +export class RootBlock extends NcBlock implements IRootContext { + private oidAllocations: { [id: number] : string; } = {}; + public constructor( - oid: number, constantOid: boolean, ownerObject: NcObject | null, role: string, @@ -543,10 +760,11 @@ export class RootBlock extends NcBlock enabled: boolean, memberObjects: NcObject[], description: string, - notificationContext: INotificationContext) + notificationContext: INotificationContext, + maxMembers: number | null = null) { super( - oid, + NcBlock.RootOid, constantOid, ownerObject, role, @@ -556,7 +774,19 @@ export class RootBlock extends NcBlock enabled, memberObjects, description, - notificationContext); + notificationContext, + null, + maxMembers); + + this.oidAllocations[NcBlock.RootOid] = this.GetRolePathUrl(); + } + + public AllocateOid(path: string) : number + { + //Generate new oid + let newOid = Object.keys(this.oidAllocations).length + 1; + this.oidAllocations[newOid] = path; + return newOid; } public ProcessMessage(msg: string, socket: WebSocketConnection) @@ -678,12 +908,12 @@ export class RootBlock extends NcBlock else { if(commandMsg.oid == this.oid) - return this.InvokeMethod(socket, commandMsg.oid, commandMsg.methodId, commandMsg.arguments, commandMsg.handle); + return this.InvokeMethod(commandMsg.oid, commandMsg.methodId, commandMsg.arguments, commandMsg.handle); else if(this.memberObjects != null) { let member = this.FindNestedMember(commandMsg.oid); if(member) - return member.InvokeMethod(socket, commandMsg.oid, commandMsg.methodId, commandMsg.arguments, commandMsg.handle); + return member.InvokeMethod(commandMsg.oid, commandMsg.methodId, commandMsg.arguments, commandMsg.handle); else return new CommandResponseError(commandMsg.handle, NcMethodStatus.BadOid, "OID could not be found"); } @@ -705,4 +935,200 @@ export class RootBlock extends NcBlock return true; return false; } +} + +export class ExampleControlsBlock extends NcBlock +{ + public constructor( + oid: number, + constantOid: boolean, + ownerObject: NcObject | null, + role: string, + userLabel: string, + touchpoints: NcTouchpoint[] | null, + runtimePropertyConstraints: NcPropertyConstraints[] | null, + enabled: boolean, + memberObjects: NcObject[], + description: string, + notificationContext: INotificationContext, + rootContext: IRootContext | null, + maxMembers: number | null = null, + isRebuildable: boolean) + { + super( + oid, + constantOid, + ownerObject, + role, + userLabel, + touchpoints, + runtimePropertyConstraints, + enabled, + memberObjects, + description, + notificationContext, + rootContext, + maxMembers, + isRebuildable); + } + + public override GetAllProperties(recurse: boolean) : NcObjectPropertiesHolder[] + { + let holders = [ + new NcObjectPropertiesHolder(this.GetRolePath(), [], [], [ + ExampleControl.staticClassID, + ], this.isRebuildable) + ]; + + holders[0].values = holders[0].values.concat(super.GetAllProperties(recurse)[0].values); + + if(recurse) + { + this.memberObjects.forEach(member => { + holders = holders.concat(member.GetAllProperties(recurse)); + }); + } + + return holders + } + + public override ReconstructMembers(members: NcBlockMemberDescriptor[], dataSet: NcBulkValuesHolder, applyChanges: Boolean = true) : [NcPropertyRestoreNotice | null, NcObjectPropertiesSetValidation[]] + { + let blockMembersNotice: NcPropertyRestoreNotice | null = null; + + let validationEntries = new Array(); + + console.log(`Reconstructing members, count: ${members.length}, applyChanges: ${applyChanges}`); + + let controlMembers: ExampleControl[] = []; + + members.forEach(member => { + if(member.classId.join() == ExampleControl.staticClassID.join()) + { + if(this.maxMembers != null) + { + if(controlMembers.length < this.maxMembers) + { + if(this.rootContext != null) + { + let memberRolepath = this.GetRolePathForMember(member.role); + + let setValidation: NcObjectPropertiesSetValidation; + + let memberRestoreData = dataSet.values.find(f => f.path.join('.') == memberRolepath.join('.')) + if(memberRestoreData) + setValidation = this.ValidateMemberDatasetChunk(memberRestoreData); + else + setValidation = new NcObjectPropertiesSetValidation(memberRolepath, NcRestoreValidationStatus.Ok, [], "No dataset information passed but object was created successfully with defaults") + + validationEntries = validationEntries.concat(setValidation); + + if(applyChanges) + { + const exampleControl = new ExampleControl( + this.rootContext.AllocateOid(memberRolepath.join('.')), + true, + this, + member.role, + member.userLabel ?? member.role, + [], + null, + true, + "Example control worker", + this.notificationContext, + true, + memberRestoreData); + + controlMembers.push(exampleControl); + } + } + } + else + { + blockMembersNotice = new NcPropertyRestoreNotice(new NcPropertyId(2, 2), "members", NcPropertyRestoreNoticeType.Warning, `Member [${member.role}] can't be constructed because it goes over the maximum block members limit of ${this.maxMembers}`); + console.log(`Member [${member.role}] can't be constructed because it goes over the maximum block members limit of ${this.maxMembers}`); + } + } + else + { + if(this.rootContext != null) + { + let memberRolepath = this.GetRolePathForMember(member.role); + + let setValidation: NcObjectPropertiesSetValidation; + + let memberRestoreData = dataSet.values.find(f => f.path.join('.') == memberRolepath.join('.')) + if(memberRestoreData) + setValidation = this.ValidateMemberDatasetChunk(memberRestoreData); + else + setValidation = new NcObjectPropertiesSetValidation(memberRolepath, NcRestoreValidationStatus.Ok, [], "No dataset information passed but object was created successfully with defaults") + + validationEntries = validationEntries.concat(setValidation); + + if(applyChanges) + { + const exampleControl = new ExampleControl( + this.rootContext.AllocateOid(memberRolepath.join('.')), + true, + this, + member.role, + member.userLabel ?? member.role, + [], + null, + true, + "Example control worker", + this.notificationContext, + true, + memberRestoreData); + + controlMembers.push(exampleControl); + } + } + } + } + else + { + blockMembersNotice = new NcPropertyRestoreNotice(new NcPropertyId(2, 2), "members", NcPropertyRestoreNoticeType.Warning, `Member [${member.role}] can't be constructed because it has an invalid classId of ${member.classId.join('.')}`); + console.log(`Member [${member.role}] can't be constructed because it has an invalid classId of ${member.classId.join('.')}`); + } + }); + + if(applyChanges) + this.UpdateMembers(controlMembers, true); + + return [blockMembersNotice, validationEntries]; + } + + public ValidateMemberDatasetChunk(holder: NcObjectPropertiesHolder) : NcObjectPropertiesSetValidation + { + let myNotices = new Array(); + + holder.values.forEach(propertyData => + { + let propertyId = NcElementId.ToPropertyString(propertyData.id); + switch(propertyId) + { + case '1p6': + case '2p1': + case '3p1': + case '3p2': + case '3p3': + case '3p4': + case '3p5': + case '3p9': + case '3p10': + case '3p11': + case '3p12': + case '3p13': + { + //TODO: Perform further validations + } + break; + default: + myNotices.push(new NcPropertyRestoreNotice(propertyData.id, propertyData.name, NcPropertyRestoreNoticeType.Warning, "Property can't be changed and will receive a default value")); + } + }); + + return new NcObjectPropertiesSetValidation(holder.path, NcRestoreValidationStatus.Ok, myNotices, null) + } } \ No newline at end of file diff --git a/code/src/NCModel/Core.ts b/code/src/NCModel/Core.ts index a8ab1aa..3685473 100644 --- a/code/src/NCModel/Core.ts +++ b/code/src/NCModel/Core.ts @@ -1,6 +1,5 @@ import { jsonIgnoreReplacer, jsonIgnore } from 'json-ignore'; import { CommandResponseError, CommandResponseNoValue, CommandResponseWithValue } from '../NCProtocol/Commands'; -import { WebSocketConnection } from '../Server'; import { INotificationContext } from '../SessionManager'; export function myIdDecorator(identity: string) { @@ -49,6 +48,8 @@ export abstract class NcObject public ownerObject: NcObject | null; + protected isRebuildable: boolean; + public constructor( oid: number, constantOid: boolean, @@ -58,7 +59,8 @@ export abstract class NcObject touchpoints: NcTouchpoint[] | null, runtimePropertyConstraints: NcPropertyConstraints[] | null, description: string, - notificationContext: INotificationContext) + notificationContext: INotificationContext, + isRebuildable: boolean = false) { this.oid = oid; this.constantOid = constantOid; @@ -70,6 +72,8 @@ export abstract class NcObject this.runtimePropertyConstraints = runtimePropertyConstraints; this.description = description; this.notificationContext = notificationContext; + + this.isRebuildable = isRebuildable; } //'1m1' @@ -135,13 +139,42 @@ export abstract class NcObject return new CommandResponseError(handle, NcMethodStatus.BadOid, 'OID could not be found'); } - public InvokeMethod(socket: WebSocketConnection, oid: number, methodId: NcElementId, args: { [key: string]: any } | null, handle: number) : CommandResponseNoValue + public InvokeMethod(oid: number, methodId: NcElementId, args: { [key: string]: any } | null, handle: number) : CommandResponseNoValue { if(oid == this.oid) { let key: string = `${methodId.level}m${methodId.index}`; switch(key) { + case '1m1': //Get + { + if(args != null && + 'id' in args) + { + let propertyId = args['id'] as NcElementId; + if(propertyId) + return this.Get(this.oid, propertyId, handle); + else + return new CommandResponseError(handle, NcMethodStatus.InvalidRequest, 'Invalid id argument provided'); + } + else + return new CommandResponseError(handle, NcMethodStatus.InvalidRequest, 'Invalid arguments provided'); + } + case '1m2': //Set + { + if(args != null && + 'id' in args && + 'value' in args) + { + let propertyId = args['id'] as NcElementId; + if(propertyId) + return this.Set(this.oid, propertyId, args['value'], handle); + else + return new CommandResponseError(handle, NcMethodStatus.InvalidRequest, 'Invalid id argument provided'); + } + else + return new CommandResponseError(handle, NcMethodStatus.InvalidRequest, 'Invalid arguments provided'); + } case '1m3': //GetSequenceItem { if(args != null && @@ -343,6 +376,22 @@ export abstract class NcObject ); } + public GetAllProperties(recurse: boolean) : NcObjectPropertiesHolder[] + { + return [ + new NcObjectPropertiesHolder(this.GetRolePath(), [], [ + new NcPropertyValueHolder(new NcPropertyId(1, 1), "classId", "NcClassId", true, this.classID), + new NcPropertyValueHolder(new NcPropertyId(1, 2), "oid", "NcOid", true, this.oid), + new NcPropertyValueHolder(new NcPropertyId(1, 3), "constantOid", "NcBoolean", true, this.constantOid), + new NcPropertyValueHolder(new NcPropertyId(1, 4), "owner", "NcOid", true, this.owner), + new NcPropertyValueHolder(new NcPropertyId(1, 5), "role", "NcString", true, this.role), + new NcPropertyValueHolder(new NcPropertyId(1, 6), "userLabel", "NcString", false, this.userLabel), + new NcPropertyValueHolder(new NcPropertyId(1, 7), "touchpoints", "NcTouchpoint", true, this.touchpoints), + new NcPropertyValueHolder(new NcPropertyId(1, 8), "runtimePropertyConstraints", "NcPropertyConstraints", true, this.runtimePropertyConstraints), + ], [], this.isRebuildable) + ]; + } + public GetRolePath(): string[] { let rolePath: string[] = []; @@ -354,6 +403,13 @@ export abstract class NcObject return rolePath; } + + public GetRolePathUrl(): string + { + return this.GetRolePath().join('.') + "/"; + } + + public abstract Restore(restoreArguments: RestoreArguments, applyChanges: Boolean) : NcObjectPropertiesSetValidation[] } export class NcElementId extends BaseType @@ -379,6 +435,34 @@ export class NcElementId extends BaseType ], null, null, "Class element id which contains the level and index"); } + public static ToPropertyId(id: string) : NcPropertyId | null + { + let split = id.split('p'); + if(split.length == 2) + return new NcPropertyId(Number(split[0] || 0), Number(split[1] || 0)); + else + return null; + } + + public static ToMethodId(id: string) : NcMethodId | null + { + let split = id.split('m'); + if(split.length == 2) + return new NcMethodId(Number(split[0] || 0), Number(split[1] || 0)); + else + return null; + } + + public static ToPropertyString(id: NcElementId) : string + { + return `${id.level}p${id.index}`; + } + + public static ToMethodString(id: NcElementId) : string + { + return `${id.level}m${id.index}`; + } + public ToJson() { return JSON.stringify(this, jsonIgnoreReplacer); @@ -1809,4 +1893,298 @@ export class NcDatatypeDescriptorEnum extends NcDatatypeDescriptor { return JSON.stringify(this, jsonIgnoreReplacer); } -} \ No newline at end of file +} + +export class NcPropertyValueHolder extends BaseType +{ + public id: NcPropertyId; + public name: string; + public typeName: string | null; + public isReadOnly: boolean; + public value: any; + + public constructor( + id: NcPropertyId, + name: string, + typeName: string | null, + isReadOnly: boolean, + value: any) + { + super(); + + this.id = id; + this.name = name; + this.typeName = typeName; + this.isReadOnly = isReadOnly; + this.value = value; + } + + public static override GetTypeDescriptor(includeInherited: boolean): NcDatatypeDescriptor + { + return new NcDatatypeDescriptorStruct("NcPropertyValueHolder", [ + new NcFieldDescriptor("id", "NcPropertyId", false, false, null, "Property id"), + new NcFieldDescriptor("name", "NcString", false, false, null, "Property name"), + new NcFieldDescriptor("typeName", "NcName", true, false, null, "Property type name. If null it means the type is any"), + new NcFieldDescriptor("isReadOnly", "NcBoolean", false, false, null, "Is the property ReadOnly?"), + new NcFieldDescriptor("value", null, true, false, null, "Property value"), + ], null, null, "Property value holder descriptor"); + } +} + +export class NcObjectPropertiesHolder extends BaseType +{ + public path: string[]; + public dependencyPaths: string[][] + public allowedMembersClasses: number[][] + public values: NcPropertyValueHolder[]; + public isRebuildable: boolean; + + public constructor( + path: string[], + dependencyPaths: string[][], + values: NcPropertyValueHolder[], + allowedMembersClasses: number[][], + isRebuildable: boolean) + { + super(); + + this.path = path; + this.dependencyPaths = dependencyPaths; + this.allowedMembersClasses = allowedMembersClasses; + this.values = values; + this.isRebuildable = isRebuildable; + } + + public static override GetTypeDescriptor(includeInherited: boolean): NcDatatypeDescriptor + { + return new NcDatatypeDescriptorStruct("NcObjectPropertiesHolder", [ + new NcFieldDescriptor("path", "NcRolePath", false, false, null, "Object role path"), + new NcFieldDescriptor("dependencyPaths", "NcRolePath", false, true, null, "Sequence of role paths which are a dependency for this object (helpful to inform clients which objects need to be restored together)"), + new NcFieldDescriptor("allowedMembersClasses", "NcClassId", false, true, null, "Sequence of class ids allowed as members of the block (non-block objects have this as an empty sequence)"), + new NcFieldDescriptor("values", "NcPropertyValueHolder", false, true, null, "Object properties values"), + new NcFieldDescriptor("isRebuildable", "NcBoolean", false, false, null, "Describes if the object is rebuildable"), + ], null, null, "Object properties holder descriptor"); + } +} + +export class NcBulkValuesHolder extends BaseType +{ + public validationFingerprint: string | null; + public values: NcObjectPropertiesHolder[]; + + public constructor( + validationFingerprint: string | null, + values: NcObjectPropertiesHolder[]) + { + super(); + + this.validationFingerprint = validationFingerprint; + this.values = values; + } + + public static override GetTypeDescriptor(includeInherited: boolean): NcDatatypeDescriptor + { + return new NcDatatypeDescriptorStruct("NcBulkValuesHolder", [ + new NcFieldDescriptor("validationFingerprint", "NcString", true, false, null, "Optional vendor specific fingerprinting mechanism used for validation purposes"), + new NcFieldDescriptor("values", "NcObjectPropertiesHolder", false, true, null, "Values by rolePath") + ], null, null, "Bulk values holder descriptor"); + } +} + +export class NcMethodResultBulkValuesHolder extends NcMethodResult +{ + public value: NcBulkValuesHolder; + + public constructor( + status: NcMethodStatus, + value: NcBulkValuesHolder) + { + super(status); + + this.value = value; + } + + public static override GetTypeDescriptor(includeInherited: boolean): NcDatatypeDescriptor + { + let currentClassDescriptor = new NcDatatypeDescriptorStruct("NcMethodResultBulkValuesHolder", [ + new NcFieldDescriptor("value", "NcBulkValuesHolder", false, false, null, "Bulk values holder value") + ], "NcMethodResult", null, "Bulk values holder result") + + if(includeInherited) + { + let baseDescriptor = super.GetTypeDescriptor(includeInherited); + + let baseDescriptorStruct = baseDescriptor as NcDatatypeDescriptorStruct; + if(baseDescriptorStruct) + currentClassDescriptor.fields = currentClassDescriptor.fields.concat(baseDescriptorStruct.fields); + } + + return currentClassDescriptor; + } +} + +export class ConfigApiValue +{ + public value: any; + + constructor( + value: any) + { + this.value = value; + } +} + +export class ConfigApiArguments +{ + public arguments: { [key: string]: any } | null; + + constructor( + configArguments: { [key: string]: any } | null) + { + this.arguments = configArguments; + } +} + +export class RestoreBody +{ + public arguments: RestoreArguments; + + constructor( + restoreArguments: RestoreArguments) + { + this.arguments = restoreArguments; + } +} + +export enum NcRestoreMode +{ + Modify = 0, + Rebuild = 1 +} + +export class RestoreArguments +{ + public dataSet: NcBulkValuesHolder; + public recurse: boolean; + public restoreMode: NcRestoreMode; + + constructor( + dataSet: NcBulkValuesHolder, + recurse: boolean, + restoreMode: NcRestoreMode) + { + this.dataSet = dataSet; + this.recurse = recurse; + this.restoreMode = restoreMode; + } +} + +export enum NcRestoreValidationStatus +{ + Ok = 200, + Failed = 400, + NotFound = 404, + DeviceError = 500 +} + +export enum NcPropertyRestoreNoticeType +{ + Warning = 300, + Error = 400 +} + +export class NcPropertyRestoreNotice extends BaseType +{ + public id: NcPropertyId; + public name: string; + public noticeType: NcPropertyRestoreNoticeType; + public noticeMessage: string; + + public constructor( + id: NcPropertyId, + name: string, + noticeType: NcPropertyRestoreNoticeType, + noticeMessage: string) + { + super(); + + this.id = id; + this.name = name; + this.noticeType = noticeType; + this.noticeMessage = noticeMessage; + } + + public static override GetTypeDescriptor(includeInherited: boolean): NcDatatypeDescriptor + { + return new NcDatatypeDescriptorStruct("NcPropertyRestoreNotice", [ + new NcFieldDescriptor("id", "NcPropertyId", false, false, null, "Property id"), + new NcFieldDescriptor("name", "NcName", false, false, null, "Property name"), + new NcFieldDescriptor("noticeType", "NcPropertyRestoreNoticeType", false, false, null, "Property restore notice type"), + new NcFieldDescriptor("noticeMessage", "NcString", false, false, null, "Property restore notice message") + ], null, null, "Property restore notice descriptor"); + } +} + +export class NcObjectPropertiesSetValidation extends BaseType +{ + public path: string[]; + public status: NcRestoreValidationStatus; + public notices: NcPropertyRestoreNotice[]; + public statusMessage: string | null; + + public constructor( + path: string[], + status: NcRestoreValidationStatus, + notices: NcPropertyRestoreNotice[], + statusMessage: string | null) + { + super(); + + this.path = path; + this.status = status; + this.notices = notices; + this.statusMessage = statusMessage; + } + + public static override GetTypeDescriptor(includeInherited: boolean): NcDatatypeDescriptor + { + return new NcDatatypeDescriptorStruct("NcObjectPropertiesSetValidation", [ + new NcFieldDescriptor("path", "NcRolePath", false, false, null, "Object role path"), + new NcFieldDescriptor("status", "NcRestoreValidationStatus", false, false, null, "Validation status"), + new NcFieldDescriptor("notices", "NcPropertyRestoreNotice", false, true, null, "Validation property notices"), + new NcFieldDescriptor("statusMessage", "NcString", true, false, null, "Validation status message"), + ], null, null, "Object properties bulk set validation"); + } +} + +export class NcMethodResultObjectPropertiesSetValidation extends NcMethodResult +{ + public value: NcObjectPropertiesSetValidation[]; + + public constructor( + status: NcMethodStatus, + value: NcObjectPropertiesSetValidation[]) + { + super(status); + + this.value = value; + } + + public static override GetTypeDescriptor(includeInherited: boolean): NcDatatypeDescriptor + { + let currentClassDescriptor = new NcDatatypeDescriptorStruct("NcMethodResultObjectPropertiesSetValidation", [ + new NcFieldDescriptor("value", "NcObjectPropertiesSetValidation", false, true, null, "Object properties set path validations") + ], "NcMethodResult", null, "Object properties bulk set validation result") + + if(includeInherited) + { + let baseDescriptor = super.GetTypeDescriptor(includeInherited); + + let baseDescriptorStruct = baseDescriptor as NcDatatypeDescriptorStruct; + if(baseDescriptorStruct) + currentClassDescriptor.fields = currentClassDescriptor.fields.concat(baseDescriptorStruct.fields); + } + + return currentClassDescriptor; + } +} diff --git a/code/src/NCModel/Features.ts b/code/src/NCModel/Features.ts index d6df22b..6976ab0 100644 --- a/code/src/NCModel/Features.ts +++ b/code/src/NCModel/Features.ts @@ -1,6 +1,5 @@ import { jsonIgnoreReplacer, jsonIgnore } from 'json-ignore'; import { CommandResponseError, CommandResponseNoValue, CommandResponseWithValue } from '../NCProtocol/Commands'; -import { WebSocketConnection } from '../Server'; import { INotificationContext } from '../SessionManager'; import { BaseType, @@ -14,13 +13,21 @@ import { NcMethodResult, NcMethodStatus, NcObject, + NcObjectPropertiesHolder, + NcObjectPropertiesSetValidation, NcParameterConstraintsNumber, NcParameterConstraintsString, NcParameterDescriptor, NcPropertyChangeType, NcPropertyConstraints, NcPropertyDescriptor, - NcTouchpoint } from './Core'; + NcPropertyId, + NcPropertyRestoreNotice, + NcPropertyRestoreNoticeType, + NcPropertyValueHolder, + NcRestoreValidationStatus, + NcTouchpoint, + RestoreArguments} from './Core'; export abstract class NcWorker extends NcObject { @@ -42,9 +49,10 @@ export abstract class NcWorker extends NcObject runtimePropertyConstraints: NcPropertyConstraints[] | null, enabled: boolean, description: string, - notificationContext: INotificationContext) + notificationContext: INotificationContext, + isRebuildable: boolean = false) { - super(oid, constantOid, ownerObject, role, userLabel, touchpoints, runtimePropertyConstraints, description, notificationContext); + super(oid, constantOid, ownerObject, role, userLabel, touchpoints, runtimePropertyConstraints, description, notificationContext, isRebuildable); this.enabled = enabled; } @@ -111,6 +119,19 @@ export abstract class NcWorker extends NcObject return currentClassDescriptor; } + + public override GetAllProperties(recurse: boolean) : NcObjectPropertiesHolder[] + { + let properties = [ + new NcObjectPropertiesHolder(this.GetRolePath(), [], [ + new NcPropertyValueHolder(new NcPropertyId(2, 1), "enabled", "NcBoolean", false, this.enabled) + ], [], this.isRebuildable) + ]; + + properties[0].values = properties[0].values.concat(super.GetAllProperties(recurse)[0].values); + + return properties; + } } export class GainControl extends NcWorker @@ -203,6 +224,49 @@ export class GainControl extends NcWorker return currentClassDescriptor; } + + public override GetAllProperties(recurse: boolean) : NcObjectPropertiesHolder[] + { + let properties = [ + new NcObjectPropertiesHolder(this.GetRolePath(), [], [ + new NcPropertyValueHolder(new NcPropertyId(3, 1), "gainValue", "NcFloat32", false, this.gainValue) + ], [], this.isRebuildable) + ]; + + properties[0].values = properties[0].values.concat(super.GetAllProperties(recurse)[0].values); + + return properties; + } + + public override Restore(restoreArguments: RestoreArguments, applyChanges: Boolean) : NcObjectPropertiesSetValidation[] + { + let validationEntries = new Array(); + + let myRestoreData = restoreArguments.dataSet.values.find(f => f.path.join('.') == this.GetRolePath().join('.')) + if(myRestoreData) + { + let myNotices = new Array(); + + myRestoreData.values.forEach(propertyData => { + let propertyId = NcElementId.ToPropertyString(propertyData.id); + if(propertyId != '1p6' && propertyId != '3p1') + myNotices.push(new NcPropertyRestoreNotice( + propertyData.id, + propertyData.name, + NcPropertyRestoreNoticeType.Warning, + "Property cannot be changed and will be left untouched")); + else if(applyChanges) + { + //Perform further validation + this.Set(this.oid, propertyData.id, propertyData.value, 0); + } + }); + + validationEntries.push(new NcObjectPropertiesSetValidation(this.GetRolePath(), NcRestoreValidationStatus.Ok, myNotices, myNotices.length > 0 ? 'Some properties have notices' : null)); + } + + return validationEntries; + } } export class NcIdentBeacon extends NcWorker @@ -295,6 +359,48 @@ export class NcIdentBeacon extends NcWorker return currentClassDescriptor; } + + public override GetAllProperties(recurse: boolean) : NcObjectPropertiesHolder[] + { + let properties = [ + new NcObjectPropertiesHolder(this.GetRolePath(), [], [ + new NcPropertyValueHolder(new NcPropertyId(3, 1), "active", "NcBoolean", false, this.active) + ], [], this.isRebuildable) + ]; + + properties[0].values = properties[0].values.concat(super.GetAllProperties(recurse)[0].values); + return properties; + } + + public override Restore(restoreArguments: RestoreArguments, applyChanges: Boolean) : NcObjectPropertiesSetValidation[] + { + let validationEntries = new Array(); + + let myRestoreData = restoreArguments.dataSet.values.find(f => f.path.join('.') == this.GetRolePath().join('.')) + if(myRestoreData) + { + let myNotices = new Array(); + + myRestoreData.values.forEach(propertyData => { + let propertyId = NcElementId.ToPropertyString(propertyData.id); + if(propertyId != '1p6' && propertyId != '3p1') + myNotices.push(new NcPropertyRestoreNotice( + propertyData.id, + propertyData.name, + NcPropertyRestoreNoticeType.Warning, + "Property cannot be changed and will be left untouched")); + else if(applyChanges) + { + //Perform further validation + this.Set(this.oid, propertyData.id, propertyData.value, 0); + } + }); + + validationEntries.push(new NcObjectPropertiesSetValidation(this.GetRolePath(), NcRestoreValidationStatus.Ok, myNotices, myNotices.length > 0 ? 'Some properties have notices' : null)); + } + + return validationEntries; + } } enum NcOverallStatus @@ -411,6 +517,50 @@ export class NcStatusMonitor extends NcWorker return currentClassDescriptor; } + + public override GetAllProperties(recurse: boolean) : NcObjectPropertiesHolder[] + { + let properties = [ + new NcObjectPropertiesHolder(this.GetRolePath(), [], [ + new NcPropertyValueHolder(new NcPropertyId(3, 1), "overallStatus", "NcOverallStatus", true, this.overallStatus), + new NcPropertyValueHolder(new NcPropertyId(3, 2), "overallStatusMessage", "NcString", true, this.overallStatusMessage), + new NcPropertyValueHolder(new NcPropertyId(3, 3), "statusReportingDelay", "NcUint32", false, this.statusReportingDelay) + ], [], this.isRebuildable) + ]; + + properties[0].values = properties[0].values.concat(super.GetAllProperties(recurse)[0].values); + + return properties; + } + + public override Restore(restoreArguments: RestoreArguments, applyChanges: Boolean) : NcObjectPropertiesSetValidation[] + { + let validationEntries = new Array(); + + let myRestoreData = restoreArguments.dataSet.values.find(f => f.path.join('.') == this.GetRolePath().join('.')) + if(myRestoreData) + { + let myNotices = new Array(); + + myRestoreData.values.forEach(propertyData => { + if(NcElementId.ToPropertyString(propertyData.id) != '1p6') + myNotices.push(new NcPropertyRestoreNotice( + propertyData.id, + propertyData.name, + NcPropertyRestoreNoticeType.Warning, + "Property cannot be changed and will be left untouched")); + else if(applyChanges) + { + //Perform further validation + this.Set(this.oid, propertyData.id, propertyData.value, 0); + } + }); + + validationEntries.push(new NcObjectPropertiesSetValidation(this.GetRolePath(), NcRestoreValidationStatus.Ok, myNotices, myNotices.length > 0 ? 'Some properties have notices' : null)); + } + + return validationEntries; + } } enum NcLinkStatus @@ -578,25 +728,16 @@ export class NcReceiverMonitor extends NcStatusMonitor public synchronizationSourceId: string | null; @myIdDecorator('4p11') - public synchronizationSourceChanges: number; - - @myIdDecorator('4p12') public streamStatus: NcStreamStatus; - @myIdDecorator('4p13') + @myIdDecorator('4p12') public streamStatusMessage: string | null; - @myIdDecorator('4p14') + @myIdDecorator('4p13') public streamStatusTransitionCounter: number; - @myIdDecorator('4p15') - public autoResetPacketCounters: boolean; - - @myIdDecorator('4p16') - public autoResetSynchronizationSourceChanges: boolean; - - @myIdDecorator('4p17') - public autoResetStatusTransitionCounters: boolean; + @myIdDecorator('4p14') + public autoResetCounters: boolean; private lostPacketCounters: NcCounter[]; private latePacketCounters: NcCounter[]; @@ -634,7 +775,6 @@ export class NcReceiverMonitor extends NcStatusMonitor this.externalSynchronizationStatus = NcSynchronizationStatus.Healthy; this.externalSynchronizationStatusMessage = "Locked to grandmaster"; this.synchronizationSourceId = "0xD4:AD:71:FF:FE:6F:E2:80"; - this.synchronizationSourceChanges = 0; this.streamStatus = NcStreamStatus.Inactive; this.streamStatusMessage = "Receiver is inactive"; @@ -644,23 +784,30 @@ export class NcReceiverMonitor extends NcStatusMonitor this.externalSynchronizationStatusTransitionCounter = 0; this.streamStatusTransitionCounter = 0; - this.autoResetPacketCounters = true; - this.autoResetSynchronizationSourceChanges = true; - this.autoResetStatusTransitionCounters = true; + this.autoResetCounters = true; } public Connected() { - this.overallStatus = NcOverallStatus.Healthy; - this.overallStatusMessage = "Receiver is connected and healthy"; + this.overallStatus = NcOverallStatus.Healthy; //3p1 + this.overallStatusMessage = "Receiver is connected and healthy"; //3p2 - this.connectionStatus = NcConnectionStatus.Healthy; - this.connectionStatusMessage = "Receiver is connected and connection is healthy"; + this.connectionStatus = NcConnectionStatus.Healthy; //4p4 + this.connectionStatusMessage = "Receiver is connected and connection is healthy"; //4p5 - this.streamStatus = NcStreamStatus.Healthy; - this.streamStatusMessage = "Receiver is connected and stream is healthy"; + this.streamStatus = NcStreamStatus.Healthy; //4p11 + this.streamStatusMessage = "Receiver is connected and stream is healthy"; //4p12 - if(this.autoResetPacketCounters) + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(3, 1), NcPropertyChangeType.ValueChanged, this.overallStatus, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(3, 2), NcPropertyChangeType.ValueChanged, this.overallStatusMessage, null); + + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 4), NcPropertyChangeType.ValueChanged, this.connectionStatus, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 5), NcPropertyChangeType.ValueChanged, this.connectionStatusMessage, null); + + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 11), NcPropertyChangeType.ValueChanged, this.streamStatus, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 12), NcPropertyChangeType.ValueChanged, this.streamStatusMessage, null); + + if(this.autoResetCounters) { this.lostPacketCounters.forEach(counter => { counter.Reset(); @@ -669,35 +816,17 @@ export class NcReceiverMonitor extends NcStatusMonitor this.latePacketCounters.forEach(counter => { counter.Reset(); }); - } - if(this.autoResetSynchronizationSourceChanges) - { - this.synchronizationSourceChanges = 0; - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 11), NcPropertyChangeType.ValueChanged, this.synchronizationSourceChanges, null); - } - - if(this.autoResetStatusTransitionCounters) - { this.linkStatusTransitionCounter = 0; //4p3 this.connectionStatusTransitionCounter = 0; //4p6 this.externalSynchronizationStatusTransitionCounter = 0; //4p9 - this.streamStatusTransitionCounter = 0; //4p14 + this.streamStatusTransitionCounter = 0; //4p13 this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 3), NcPropertyChangeType.ValueChanged, this.linkStatusTransitionCounter, null); this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 6), NcPropertyChangeType.ValueChanged, this.connectionStatusTransitionCounter, null); this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 9), NcPropertyChangeType.ValueChanged, this.externalSynchronizationStatusTransitionCounter, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 14), NcPropertyChangeType.ValueChanged, this.streamStatusTransitionCounter, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 13), NcPropertyChangeType.ValueChanged, this.streamStatusTransitionCounter, null); } - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(3, 1), NcPropertyChangeType.ValueChanged, this.overallStatus, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(3, 2), NcPropertyChangeType.ValueChanged, this.overallStatusMessage, null); - - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 4), NcPropertyChangeType.ValueChanged, this.connectionStatus, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 5), NcPropertyChangeType.ValueChanged, this.connectionStatusMessage, null); - - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 12), NcPropertyChangeType.ValueChanged, this.streamStatus, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 13), NcPropertyChangeType.ValueChanged, this.streamStatusMessage, null); - DelayTask(1000 * this.statusReportingDelay).then(() => this.StreamBroken()); } @@ -705,16 +834,16 @@ export class NcReceiverMonitor extends NcStatusMonitor { if(this.overallStatus != NcOverallStatus.Inactive) { - this.overallStatus = NcOverallStatus.Unhealthy; - this.overallStatusMessage = "Receiver connectivity is experiencing severe issues"; + this.overallStatus = NcOverallStatus.Unhealthy; //3p1 + this.overallStatusMessage = "Receiver connectivity is experiencing severe issues"; //3p2 - this.connectionStatus = NcConnectionStatus.Unhealthy; - this.connectionStatusMessage = "Significant packet loss detected"; + this.connectionStatus = NcConnectionStatus.Unhealthy; //4p4 + this.connectionStatusMessage = "Significant packet loss detected"; //4p5 this.connectionStatusTransitionCounter++; //4p6 - this.streamStatus = NcStreamStatus.Unhealthy; - this.streamStatusMessage = "Stream cannot be decoded"; - this.streamStatusTransitionCounter++; //4p14 + this.streamStatus = NcStreamStatus.Unhealthy; //4p11 + this.streamStatusMessage = "Stream cannot be decoded"; //4p12 + this.streamStatusTransitionCounter++; //4p13 this.lostPacketCounters.find(c => c.name === 'Nic_1')?.Increment(); this.latePacketCounters.find(c => c.name === 'Nic_2')?.Increment(); @@ -725,11 +854,11 @@ export class NcReceiverMonitor extends NcStatusMonitor this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 4), NcPropertyChangeType.ValueChanged, this.connectionStatus, null); this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 5), NcPropertyChangeType.ValueChanged, this.connectionStatusMessage, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 12), NcPropertyChangeType.ValueChanged, this.streamStatus, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 13), NcPropertyChangeType.ValueChanged, this.streamStatusMessage, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 11), NcPropertyChangeType.ValueChanged, this.streamStatus, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 12), NcPropertyChangeType.ValueChanged, this.streamStatusMessage, null); this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 6), NcPropertyChangeType.ValueChanged, this.connectionStatusTransitionCounter, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 14), NcPropertyChangeType.ValueChanged, this.streamStatusTransitionCounter, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 13), NcPropertyChangeType.ValueChanged, this.streamStatusTransitionCounter, null); DelayTask(1000 * this.statusReportingDelay).then(() => this.StreamFixed()); } @@ -739,14 +868,14 @@ export class NcReceiverMonitor extends NcStatusMonitor { if(this.overallStatus != NcOverallStatus.Inactive) { - this.overallStatus = NcOverallStatus.Healthy; - this.overallStatusMessage = "Receiver is connected and healthy"; + this.overallStatus = NcOverallStatus.Healthy; //3p1 + this.overallStatusMessage = "Receiver is connected and healthy"; //3p2 - this.connectionStatus = NcConnectionStatus.Healthy; - this.connectionStatusMessage = "Receiver is connected and connection is healthy"; + this.connectionStatus = NcConnectionStatus.Healthy; //4p4 + this.connectionStatusMessage = "Receiver is connected and connection is healthy"; //4p5 - this.streamStatus = NcStreamStatus.Healthy; - this.streamStatusMessage = "Receiver is connected and stream is healthy"; + this.streamStatus = NcStreamStatus.Healthy; //4p11 + this.streamStatusMessage = "Receiver is connected and stream is healthy"; //4p12 this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(3, 1), NcPropertyChangeType.ValueChanged, this.overallStatus, null); this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(3, 2), NcPropertyChangeType.ValueChanged, this.overallStatusMessage, null); @@ -754,21 +883,21 @@ export class NcReceiverMonitor extends NcStatusMonitor this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 4), NcPropertyChangeType.ValueChanged, this.connectionStatus, null); this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 5), NcPropertyChangeType.ValueChanged, this.connectionStatusMessage, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 12), NcPropertyChangeType.ValueChanged, this.streamStatus, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 13), NcPropertyChangeType.ValueChanged, this.streamStatusMessage, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 11), NcPropertyChangeType.ValueChanged, this.streamStatus, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 12), NcPropertyChangeType.ValueChanged, this.streamStatusMessage, null); } } public Disconnected() { - this.overallStatus = NcOverallStatus.Inactive; - this.overallStatusMessage = "Receiver is inactive"; + this.overallStatus = NcOverallStatus.Inactive; //3p1 + this.overallStatusMessage = "Receiver is inactive"; //3p2 - this.connectionStatus = NcConnectionStatus.Inactive; - this.connectionStatusMessage = "Receiver is inactive"; + this.connectionStatus = NcConnectionStatus.Inactive; //4p4 + this.connectionStatusMessage = "Receiver is inactive"; //4p5 - this.streamStatus = NcStreamStatus.Inactive; - this.streamStatusMessage = "Receiver is inactive"; + this.streamStatus = NcStreamStatus.Inactive; //4p11 + this.streamStatusMessage = "Receiver is inactive"; //4p12 this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(3, 1), NcPropertyChangeType.ValueChanged, this.overallStatus, null); this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(3, 2), NcPropertyChangeType.ValueChanged, this.overallStatusMessage, null); @@ -776,8 +905,8 @@ export class NcReceiverMonitor extends NcStatusMonitor this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 4), NcPropertyChangeType.ValueChanged, this.connectionStatus, null); this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 5), NcPropertyChangeType.ValueChanged, this.connectionStatusMessage, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 12), NcPropertyChangeType.ValueChanged, this.streamStatus, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 13), NcPropertyChangeType.ValueChanged, this.streamStatusMessage, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 11), NcPropertyChangeType.ValueChanged, this.streamStatus, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 12), NcPropertyChangeType.ValueChanged, this.streamStatusMessage, null); } //'1m1' @@ -810,19 +939,13 @@ export class NcReceiverMonitor extends NcStatusMonitor case '4p10': return new CommandResponseWithValue(handle, NcMethodStatus.OK, this.synchronizationSourceId); case '4p11': - return new CommandResponseWithValue(handle, NcMethodStatus.OK, this.synchronizationSourceChanges); - case '4p12': return new CommandResponseWithValue(handle, NcMethodStatus.OK, this.streamStatus); - case '4p13': + case '4p12': return new CommandResponseWithValue(handle, NcMethodStatus.OK, this.streamStatusMessage); - case '4p14': + case '4p13': return new CommandResponseWithValue(handle, NcMethodStatus.OK, this.streamStatusTransitionCounter); - case '4p15': - return new CommandResponseWithValue(handle, NcMethodStatus.OK, this.autoResetPacketCounters); - case '4p16': - return new CommandResponseWithValue(handle, NcMethodStatus.OK, this.autoResetSynchronizationSourceChanges); - case '4p17': - return new CommandResponseWithValue(handle, NcMethodStatus.OK, this.autoResetStatusTransitionCounters); + case '4p14': + return new CommandResponseWithValue(handle, NcMethodStatus.OK, this.autoResetCounters); default: return super.Get(oid, id, handle); } @@ -858,19 +981,10 @@ export class NcReceiverMonitor extends NcStatusMonitor case '4p11': case '4p12': case '4p13': - case '4p14': return new CommandResponseError(handle, NcMethodStatus.Readonly, 'Property is readonly'); - case '4p15': - this.autoResetPacketCounters = value; - this.notificationContext.NotifyPropertyChanged(this.oid, id, NcPropertyChangeType.ValueChanged, this.autoResetPacketCounters, null); - return new CommandResponseNoValue(handle, NcMethodStatus.OK); - case '4p16': - this.autoResetSynchronizationSourceChanges = value; - this.notificationContext.NotifyPropertyChanged(this.oid, id, NcPropertyChangeType.ValueChanged, this.autoResetSynchronizationSourceChanges, null); - return new CommandResponseNoValue(handle, NcMethodStatus.OK); - case '4p17': - this.autoResetStatusTransitionCounters = value; - this.notificationContext.NotifyPropertyChanged(this.oid, id, NcPropertyChangeType.ValueChanged, this.autoResetStatusTransitionCounters, null); + case '4p14': + this.autoResetCounters = value; + this.notificationContext.NotifyPropertyChanged(this.oid, id, NcPropertyChangeType.ValueChanged, this.autoResetCounters, null); return new CommandResponseNoValue(handle, NcMethodStatus.OK); default: return super.Set(oid, id, value, handle); @@ -880,7 +994,7 @@ export class NcReceiverMonitor extends NcStatusMonitor return new CommandResponseError(handle, NcMethodStatus.BadOid, 'OID could not be found'); } - public override InvokeMethod(socket: WebSocketConnection, oid: number, methodId: NcElementId, args: { [key: string]: any; } | null, handle: number): CommandResponseNoValue + public override InvokeMethod(oid: number, methodId: NcElementId, args: { [key: string]: any; } | null, handle: number): CommandResponseNoValue { if(oid == this.oid) { @@ -893,16 +1007,10 @@ export class NcReceiverMonitor extends NcStatusMonitor case '4m2': return new CommandResponseWithValue(handle, NcMethodStatus.OK, this.latePacketCounters); case '4m3': - this.ResetPacketCounters(); - return new CommandResponseNoValue(handle, NcMethodStatus.OK); - case '4m4': - this.ResetSynchronizationSourceChanges(); - return new CommandResponseNoValue(handle, NcMethodStatus.OK); - case '4m5': - this.ResetStatusTransitionCounters(); + this.ResetCounters(); return new CommandResponseNoValue(handle, NcMethodStatus.OK); default: - return super.InvokeMethod(socket, oid, methodId, args, handle); + return super.InvokeMethod(oid, methodId, args, handle); } } @@ -910,7 +1018,7 @@ export class NcReceiverMonitor extends NcStatusMonitor return new CommandResponseError(handle, NcMethodStatus.BadOid, 'OID could not be found'); } - private ResetPacketCounters() + private ResetCounters() { this.lostPacketCounters.forEach(counter => { counter.Reset(); @@ -919,24 +1027,15 @@ export class NcReceiverMonitor extends NcStatusMonitor this.latePacketCounters.forEach(counter => { counter.Reset(); }); - } - private ResetSynchronizationSourceChanges() - { - this.synchronizationSourceChanges = 0; - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 11), NcPropertyChangeType.ValueChanged, this.synchronizationSourceChanges, null); - } - - private ResetStatusTransitionCounters() - { this.linkStatusTransitionCounter = 0; //4p3 this.connectionStatusTransitionCounter = 0; //4p6 this.externalSynchronizationStatusTransitionCounter = 0; //4p9 - this.streamStatusTransitionCounter = 0; //4p14 + this.streamStatusTransitionCounter = 0; //4p13 this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 3), NcPropertyChangeType.ValueChanged, this.linkStatusTransitionCounter, null); this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 6), NcPropertyChangeType.ValueChanged, this.connectionStatusTransitionCounter, null); this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 9), NcPropertyChangeType.ValueChanged, this.externalSynchronizationStatusTransitionCounter, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 14), NcPropertyChangeType.ValueChanged, this.streamStatusTransitionCounter, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 13), NcPropertyChangeType.ValueChanged, this.streamStatusTransitionCounter, null); } public static override GetClassDescriptor(includeInherited: boolean): NcClassDescriptor @@ -954,20 +1053,15 @@ export class NcReceiverMonitor extends NcStatusMonitor new NcPropertyDescriptor(new NcElementId(4, 8), "externalSynchronizationStatusMessage", "NcString", true, true, false, null, "External synchronization status message property"), new NcPropertyDescriptor(new NcElementId(4, 9), "externalSynchronizationStatusTransitionCounter", "NcUint64", true, false, false, null, "External synchronization status transition counter property"), new NcPropertyDescriptor(new NcElementId(4, 10), "synchronizationSourceId", "NcString", true, true, false, null, "Synchronization source id property"), - new NcPropertyDescriptor(new NcElementId(4, 11), "synchronizationSourceChanges", "NcUint64", true, false, false, null, "Synchronization source changes counter"), - new NcPropertyDescriptor(new NcElementId(4, 12), "streamStatus", "NcStreamStatus", true, false, false, null, "Stream status property"), - new NcPropertyDescriptor(new NcElementId(4, 13), "streamStatusMessage", "NcString", true, true, false, null, "Stream status message property"), - new NcPropertyDescriptor(new NcElementId(4, 14), "streamStatusTransitionCounter", "NcUint64", true, false, false, null, "Stream status transition counter property"), - new NcPropertyDescriptor(new NcElementId(4, 15), "autoResetPacketCounters", "NcBoolean", false, false, false, null, "Automatic reset packet counters property (default: true)"), - new NcPropertyDescriptor(new NcElementId(4, 16), "autoResetSynchronizationSourceChanges", "NcBoolean", false, false, false, null, "Automatic reset synchronization source changes property (default: true)"), - new NcPropertyDescriptor(new NcElementId(4, 17), "autoResetStatusTransitionCounters", "NcBoolean", false, false, false, null, "Automatic reset status transition counters property (default: true)") + new NcPropertyDescriptor(new NcElementId(4, 11), "streamStatus", "NcStreamStatus", true, false, false, null, "Stream status property"), + new NcPropertyDescriptor(new NcElementId(4, 12), "streamStatusMessage", "NcString", true, true, false, null, "Stream status message property"), + new NcPropertyDescriptor(new NcElementId(4, 13), "streamStatusTransitionCounter", "NcUint64", true, false, false, null, "Stream status transition counter property"), + new NcPropertyDescriptor(new NcElementId(4, 14), "autoResetCounters", "NcBoolean", false, false, false, null, "Automatic reset counters property (default: true)") ], [ new NcMethodDescriptor(new NcElementId(4, 1), "GetLostPacketCounters", "NcMethodResultCounters", [], "Gets the lost packet counters"), new NcMethodDescriptor(new NcElementId(4, 2), "GetLatePacketCounters", "NcMethodResultCounters", [], "Gets the late packet counters"), - new NcMethodDescriptor(new NcElementId(4, 3), "ResetPacketCounters", "NcMethodResult", [], "Resets the packet counters"), - new NcMethodDescriptor(new NcElementId(4, 4), "ResetSynchronizationSourceChanges", "NcMethodResult", [], "Resets the synchronization source changes counter property"), - new NcMethodDescriptor(new NcElementId(4, 5), "ResetStatusTransitionCounters", "NcMethodResult", [], "Resets ALL status transition counter properties") + new NcMethodDescriptor(new NcElementId(4, 3), "ResetCounters", "NcMethodResult", [], "Resets ALL counters") ], [] ); @@ -983,6 +1077,64 @@ export class NcReceiverMonitor extends NcStatusMonitor return currentClassDescriptor; } + + public override GetAllProperties(recurse: boolean) : NcObjectPropertiesHolder[] + { + let properties = [ + new NcObjectPropertiesHolder(this.GetRolePath(), [], [ + new NcPropertyValueHolder(new NcPropertyId(4, 1), "linkStatus", "NcLinkStatus", true, this.linkStatus), + new NcPropertyValueHolder(new NcPropertyId(4, 2), "linkStatusMessage", "NcString", true, this.linkStatusMessage), + new NcPropertyValueHolder(new NcPropertyId(4, 3), "linkStatusTransitionCounter", "NcUint64", true, this.linkStatusTransitionCounter), + new NcPropertyValueHolder(new NcPropertyId(4, 4), "connectionStatus", "NcConnectionStatus", true, this.connectionStatus), + new NcPropertyValueHolder(new NcPropertyId(4, 5), "connectionStatusMessage", "NcString", true, this.connectionStatusMessage), + new NcPropertyValueHolder(new NcPropertyId(4, 6), "connectionStatusTransitionCounter", "NcUint64", true, this.connectionStatusTransitionCounter), + new NcPropertyValueHolder(new NcPropertyId(4, 7), "externalSynchronizationStatus", "NcSynchronizationStatus", true, this.externalSynchronizationStatus), + new NcPropertyValueHolder(new NcPropertyId(4, 8), "externalSynchronizationStatusMessage", "NcString", true, this.externalSynchronizationStatusMessage), + new NcPropertyValueHolder(new NcPropertyId(4, 9), "externalSynchronizationStatusTransitionCounter", "NcUint64", true, this.externalSynchronizationStatusTransitionCounter), + new NcPropertyValueHolder(new NcPropertyId(4, 10), "synchronizationSourceId", "NcString", true, this.synchronizationSourceId), + new NcPropertyValueHolder(new NcPropertyId(4, 11), "streamStatus", "NcStreamStatus", true, this.streamStatus), + new NcPropertyValueHolder(new NcPropertyId(4, 12), "streamStatusMessage", "NcString", true, this.streamStatusMessage), + new NcPropertyValueHolder(new NcPropertyId(4, 13), "streamStatusTransitionCounter", "NcUint64", true, this.streamStatusTransitionCounter), + new NcPropertyValueHolder(new NcPropertyId(4, 14), "autoResetCounters", "NcBoolean", false, this.autoResetCounters) + ], [], this.isRebuildable) + ]; + + properties[0].values = properties[0].values.concat(super.GetAllProperties(recurse)[0].values); + + return properties; + } + + public override Restore(restoreArguments: RestoreArguments, applyChanges: Boolean) : NcObjectPropertiesSetValidation[] + { + let validationEntries = new Array(); + + let myRestoreData = restoreArguments.dataSet.values.find(f => f.path.join('.') == this.GetRolePath().join('.')) + if(myRestoreData) + { + let myNotices = new Array(); + + myRestoreData.values.forEach(propertyData => { + let propertyId = NcElementId.ToPropertyString(propertyData.id); + if(propertyId != '1p6' && propertyId != '4p1' && propertyId != '4p2' && propertyId != '4p3' && propertyId != '4p4' && propertyId != '4p5' && + propertyId != '4p6' && propertyId != '4p7' && propertyId != '4p8' && propertyId != '4p9' && propertyId != '4p10' && + propertyId != '4p11' && propertyId != '4p12' && propertyId != '4p13') + myNotices.push(new NcPropertyRestoreNotice( + propertyData.id, + propertyData.name, + NcPropertyRestoreNoticeType.Warning, + "Property cannot be changed and will be left untouched")); + else if(applyChanges) + { + //Perform further validation + this.Set(this.oid, propertyData.id, propertyData.value, 0); + } + }); + + validationEntries.push(new NcObjectPropertiesSetValidation(this.GetRolePath(), NcRestoreValidationStatus.Ok, myNotices, myNotices.length > 0 ? 'Some properties have notices' : null)); + } + + return validationEntries; + } } export class NcSenderMonitor extends NcStatusMonitor @@ -1023,25 +1175,16 @@ export class NcSenderMonitor extends NcStatusMonitor public synchronizationSourceId: string | null; @myIdDecorator('4p11') - public synchronizationSourceChanges: number; - - @myIdDecorator('4p12') public essenceStatus: NcEssenceStatus; - @myIdDecorator('4p13') + @myIdDecorator('4p12') public essenceStatusMessage: string | null; - @myIdDecorator('4p14') + @myIdDecorator('4p13') public essenceStatusTransitionCounter: number; - @myIdDecorator('4p15') - public autoResetErrorCounters: boolean; - - @myIdDecorator('4p16') - public autoResetSynchronizationSourceChanges: boolean; - - @myIdDecorator('4p17') - public autoResetStatusTransitionCounters: boolean; + @myIdDecorator('4p14') + public autoResetCounters: boolean; private errorCounters: NcCounter[]; @@ -1072,7 +1215,6 @@ export class NcSenderMonitor extends NcStatusMonitor this.externalSynchronizationStatus = NcSynchronizationStatus.Healthy; this.externalSynchronizationStatusMessage = "Locked to grandmaster"; this.synchronizationSourceId = "0xD4:AD:71:FF:FE:6F:E2:80"; - this.synchronizationSourceChanges = 0; this.essenceStatus = NcEssenceStatus.Inactive; this.essenceStatusMessage = "Sender is inactive"; @@ -1082,58 +1224,47 @@ export class NcSenderMonitor extends NcStatusMonitor this.externalSynchronizationStatusTransitionCounter = 0; this.essenceStatusTransitionCounter = 0; - this.autoResetErrorCounters = true; - this.autoResetSynchronizationSourceChanges = true; - this.autoResetStatusTransitionCounters = true; + this.autoResetCounters = true; this.Activated(); } public Activated() { - this.overallStatus = NcOverallStatus.Healthy; - this.overallStatusMessage = "Sender is active and healthy"; + this.overallStatus = NcOverallStatus.Healthy; //3p1 + this.overallStatusMessage = "Sender is active and healthy"; //3p2 - this.transmissionStatus = NcTransmissionStatus.Healthy; - this.transmissionStatusMessage = "Sender is active and transmission is healthy"; + this.transmissionStatus = NcTransmissionStatus.Healthy; //4p4 + this.transmissionStatusMessage = "Sender is active and transmission is healthy"; //4p5 - this.essenceStatus = NcEssenceStatus.Healthy; - this.essenceStatusMessage = "Sender is active and essence is healthy"; + this.essenceStatus = NcEssenceStatus.Healthy; //4p11 + this.essenceStatusMessage = "Sender is active and essence is healthy"; //4p12 - if(this.autoResetErrorCounters) + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(3, 1), NcPropertyChangeType.ValueChanged, this.overallStatus, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(3, 2), NcPropertyChangeType.ValueChanged, this.overallStatusMessage, null); + + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 4), NcPropertyChangeType.ValueChanged, this.transmissionStatus, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 5), NcPropertyChangeType.ValueChanged, this.transmissionStatusMessage, null); + + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 11), NcPropertyChangeType.ValueChanged, this.essenceStatus, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 12), NcPropertyChangeType.ValueChanged, this.essenceStatusMessage, null); + + if(this.autoResetCounters) { this.errorCounters.forEach(counter => { counter.Reset(); }); - } - - if(this.autoResetSynchronizationSourceChanges) - { - this.synchronizationSourceChanges = 0; - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 11), NcPropertyChangeType.ValueChanged, this.synchronizationSourceChanges, null); - } - if(this.autoResetStatusTransitionCounters) - { this.linkStatusTransitionCounter = 0; //4p3 this.transmissionStatusTransitionCounter = 0; //4p6 this.externalSynchronizationStatusTransitionCounter = 0; //4p9 - this.essenceStatusTransitionCounter = 0; //4p14 + this.essenceStatusTransitionCounter = 0; //4p13 this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 3), NcPropertyChangeType.ValueChanged, this.linkStatusTransitionCounter, null); this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 6), NcPropertyChangeType.ValueChanged, this.transmissionStatusTransitionCounter, null); this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 9), NcPropertyChangeType.ValueChanged, this.externalSynchronizationStatusTransitionCounter, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 14), NcPropertyChangeType.ValueChanged, this.essenceStatusTransitionCounter, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 13), NcPropertyChangeType.ValueChanged, this.essenceStatusTransitionCounter, null); } - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(3, 1), NcPropertyChangeType.ValueChanged, this.overallStatus, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(3, 2), NcPropertyChangeType.ValueChanged, this.overallStatusMessage, null); - - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 4), NcPropertyChangeType.ValueChanged, this.transmissionStatus, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 5), NcPropertyChangeType.ValueChanged, this.transmissionStatusMessage, null); - - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 12), NcPropertyChangeType.ValueChanged, this.essenceStatus, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 13), NcPropertyChangeType.ValueChanged, this.essenceStatusMessage, null); - // Uncomment to emulate broken transmission // DelayTask(1000 * this.statusReportingDelay).then(() => this.TransmissionBroken()); } @@ -1142,16 +1273,16 @@ export class NcSenderMonitor extends NcStatusMonitor { if(this.overallStatus != NcOverallStatus.Inactive) { - this.overallStatus = NcOverallStatus.Unhealthy; - this.overallStatusMessage = "Sender connectivity is experiencing severe issues"; + this.overallStatus = NcOverallStatus.Unhealthy; //3p1 + this.overallStatusMessage = "Sender connectivity is experiencing severe issues"; //3p2 - this.transmissionStatus = NcTransmissionStatus.Unhealthy; - this.transmissionStatusMessage = "Significant transmission errors detected"; - this.transmissionStatusTransitionCounter++; + this.transmissionStatus = NcTransmissionStatus.Unhealthy; //4p4 + this.transmissionStatusMessage = "Significant transmission errors detected"; //4p5 + this.transmissionStatusTransitionCounter++; //4p6 - this.essenceStatus = NcEssenceStatus.Unhealthy; - this.essenceStatusMessage = "Essence is unhealthy"; - this.essenceStatusTransitionCounter++; + this.essenceStatus = NcEssenceStatus.Unhealthy; //4p11 + this.essenceStatusMessage = "Essence is unhealthy"; //4p12 + this.essenceStatusTransitionCounter++; //4p13 this.errorCounters[0]?.Increment(); @@ -1161,21 +1292,50 @@ export class NcSenderMonitor extends NcStatusMonitor this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 4), NcPropertyChangeType.ValueChanged, this.transmissionStatus, null); this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 5), NcPropertyChangeType.ValueChanged, this.transmissionStatusMessage, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 12), NcPropertyChangeType.ValueChanged, this.essenceStatus, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 13), NcPropertyChangeType.ValueChanged, this.essenceStatusMessage, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 11), NcPropertyChangeType.ValueChanged, this.essenceStatus, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 12), NcPropertyChangeType.ValueChanged, this.essenceStatusMessage, null); + + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 6), NcPropertyChangeType.ValueChanged, this.transmissionStatusTransitionCounter, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 13), NcPropertyChangeType.ValueChanged, this.essenceStatusTransitionCounter, null); + + DelayTask(1000 * this.statusReportingDelay).then(() => this.TransmissionFixed()); + } + } + + public TransmissionFixed() + { + if(this.overallStatus != NcOverallStatus.Inactive) + { + this.overallStatus = NcOverallStatus.Healthy; //3p1 + this.overallStatusMessage = "Sender is active and healthy"; //3p2 + + this.transmissionStatus = NcTransmissionStatus.Healthy; //4p4 + this.transmissionStatusMessage = "Sender is active and transmission is healthy"; //4p5 + + this.essenceStatus = NcEssenceStatus.Healthy; //4p11 + this.essenceStatusMessage = "Sender is active and essence is healthy"; //4p12 + + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(3, 1), NcPropertyChangeType.ValueChanged, this.overallStatus, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(3, 2), NcPropertyChangeType.ValueChanged, this.overallStatusMessage, null); + + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 4), NcPropertyChangeType.ValueChanged, this.transmissionStatus, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 5), NcPropertyChangeType.ValueChanged, this.transmissionStatusMessage, null); + + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 11), NcPropertyChangeType.ValueChanged, this.essenceStatus, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 12), NcPropertyChangeType.ValueChanged, this.essenceStatusMessage, null); } } public Deactivated() { - this.overallStatus = NcOverallStatus.Inactive; - this.overallStatusMessage = "Sender is inactive"; + this.overallStatus = NcOverallStatus.Inactive; //3p1 + this.overallStatusMessage = "Sender is inactive"; //3p2 - this.transmissionStatus = NcTransmissionStatus.Inactive; - this.transmissionStatusMessage = "Sender is inactive"; + this.transmissionStatus = NcTransmissionStatus.Inactive; //4p4 + this.transmissionStatusMessage = "Sender is inactive"; //4p5 - this.essenceStatus = NcEssenceStatus.Inactive; - this.essenceStatusMessage = "Sender is inactive"; + this.essenceStatus = NcEssenceStatus.Inactive; //4p11 + this.essenceStatusMessage = "Sender is inactive"; //4p12 this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(3, 1), NcPropertyChangeType.ValueChanged, this.overallStatus, null); this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(3, 2), NcPropertyChangeType.ValueChanged, this.overallStatusMessage, null); @@ -1183,8 +1343,8 @@ export class NcSenderMonitor extends NcStatusMonitor this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 4), NcPropertyChangeType.ValueChanged, this.transmissionStatus, null); this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 5), NcPropertyChangeType.ValueChanged, this.transmissionStatusMessage, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 12), NcPropertyChangeType.ValueChanged, this.essenceStatus, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 13), NcPropertyChangeType.ValueChanged, this.essenceStatusMessage, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 11), NcPropertyChangeType.ValueChanged, this.essenceStatus, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 12), NcPropertyChangeType.ValueChanged, this.essenceStatusMessage, null); } //'1m1' @@ -1217,19 +1377,13 @@ export class NcSenderMonitor extends NcStatusMonitor case '4p10': return new CommandResponseWithValue(handle, NcMethodStatus.OK, this.synchronizationSourceId); case '4p11': - return new CommandResponseWithValue(handle, NcMethodStatus.OK, this.synchronizationSourceChanges); - case '4p12': return new CommandResponseWithValue(handle, NcMethodStatus.OK, this.essenceStatus); - case '4p13': + case '4p12': return new CommandResponseWithValue(handle, NcMethodStatus.OK, this.essenceStatusMessage); - case '4p14': + case '4p13': return new CommandResponseWithValue(handle, NcMethodStatus.OK, this.essenceStatusTransitionCounter); - case '4p15': - return new CommandResponseWithValue(handle, NcMethodStatus.OK, this.autoResetErrorCounters); - case '4p16': - return new CommandResponseWithValue(handle, NcMethodStatus.OK, this.autoResetSynchronizationSourceChanges); - case '4p17': - return new CommandResponseWithValue(handle, NcMethodStatus.OK, this.autoResetStatusTransitionCounters); + case '4p14': + return new CommandResponseWithValue(handle, NcMethodStatus.OK, this.autoResetCounters); default: return super.Get(oid, id, handle); } @@ -1265,19 +1419,10 @@ export class NcSenderMonitor extends NcStatusMonitor case '4p11': case '4p12': case '4p13': - case '4p14': return new CommandResponseError(handle, NcMethodStatus.Readonly, 'Property is readonly'); - case '4p15': - this.autoResetErrorCounters = value; - this.notificationContext.NotifyPropertyChanged(this.oid, id, NcPropertyChangeType.ValueChanged, this.autoResetErrorCounters, null); - return new CommandResponseNoValue(handle, NcMethodStatus.OK); - case '4p16': - this.autoResetSynchronizationSourceChanges = value; - this.notificationContext.NotifyPropertyChanged(this.oid, id, NcPropertyChangeType.ValueChanged, this.autoResetSynchronizationSourceChanges, null); - return new CommandResponseNoValue(handle, NcMethodStatus.OK); - case '4p17': - this.autoResetStatusTransitionCounters = value; - this.notificationContext.NotifyPropertyChanged(this.oid, id, NcPropertyChangeType.ValueChanged, this.autoResetStatusTransitionCounters, null); + case '4p14': + this.autoResetCounters = value; + this.notificationContext.NotifyPropertyChanged(this.oid, id, NcPropertyChangeType.ValueChanged, this.autoResetCounters, null); return new CommandResponseNoValue(handle, NcMethodStatus.OK); default: return super.Set(oid, id, value, handle); @@ -1287,7 +1432,7 @@ export class NcSenderMonitor extends NcStatusMonitor return new CommandResponseError(handle, NcMethodStatus.BadOid, 'OID could not be found'); } - public override InvokeMethod(socket: WebSocketConnection, oid: number, methodId: NcElementId, args: { [key: string]: any; } | null, handle: number): CommandResponseNoValue + public override InvokeMethod(oid: number, methodId: NcElementId, args: { [key: string]: any; } | null, handle: number): CommandResponseNoValue { if(oid == this.oid) { @@ -1298,16 +1443,10 @@ export class NcSenderMonitor extends NcStatusMonitor case '4m1': return new CommandResponseWithValue(handle, NcMethodStatus.OK, this.errorCounters); case '4m2': - this.ResetErrorCounters(); - return new CommandResponseNoValue(handle, NcMethodStatus.OK); - case '4m3': - this.ResetSynchronizationSourceChanges(); - return new CommandResponseNoValue(handle, NcMethodStatus.OK); - case '4m4': - this.ResetStatusTransitionCounters(); + this.ResetCounters(); return new CommandResponseNoValue(handle, NcMethodStatus.OK); default: - return super.InvokeMethod(socket, oid, methodId, args, handle); + return super.InvokeMethod(oid, methodId, args, handle); } } @@ -1315,29 +1454,20 @@ export class NcSenderMonitor extends NcStatusMonitor return new CommandResponseError(handle, NcMethodStatus.BadOid, 'OID could not be found'); } - private ResetErrorCounters() + private ResetCounters() { this.errorCounters.forEach(counter => { counter.Reset(); }); - } - - private ResetSynchronizationSourceChanges() - { - this.synchronizationSourceChanges = 0; - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 11), NcPropertyChangeType.ValueChanged, this.synchronizationSourceChanges, null); - } - private ResetStatusTransitionCounters() - { this.linkStatusTransitionCounter = 0; //4p3 this.transmissionStatusTransitionCounter = 0; //4p6 this.externalSynchronizationStatusTransitionCounter = 0; //4p9 - this.essenceStatusTransitionCounter = 0; //4p14 + this.essenceStatusTransitionCounter = 0; //4p13 this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 3), NcPropertyChangeType.ValueChanged, this.linkStatusTransitionCounter, null); this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 6), NcPropertyChangeType.ValueChanged, this.transmissionStatusTransitionCounter, null); this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 9), NcPropertyChangeType.ValueChanged, this.externalSynchronizationStatusTransitionCounter, null); - this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 14), NcPropertyChangeType.ValueChanged, this.essenceStatusTransitionCounter, null); + this.notificationContext.NotifyPropertyChanged(this.oid, new NcElementId(4, 13), NcPropertyChangeType.ValueChanged, this.essenceStatusTransitionCounter, null); } public static override GetClassDescriptor(includeInherited: boolean): NcClassDescriptor @@ -1355,19 +1485,14 @@ export class NcSenderMonitor extends NcStatusMonitor new NcPropertyDescriptor(new NcElementId(4, 8), "externalSynchronizationStatusMessage", "NcString", true, true, false, null, "External synchronization status message property"), new NcPropertyDescriptor(new NcElementId(4, 9), "externalSynchronizationStatusTransitionCounter", "NcUint64", true, false, false, null, "External synchronization status transition counter property"), new NcPropertyDescriptor(new NcElementId(4, 10), "synchronizationSourceId", "NcString", true, true, false, null, "Synchronization source id property"), - new NcPropertyDescriptor(new NcElementId(4, 11), "synchronizationSourceChanges", "NcUint64", true, false, false, null, "Synchronization source changes counter"), - new NcPropertyDescriptor(new NcElementId(4, 12), "essenceStatus", "NcEssenceStatus", true, false, false, null, "Essence status property"), - new NcPropertyDescriptor(new NcElementId(4, 13), "essenceStatusMessage", "NcString", true, true, false, null, "Essence status message property"), - new NcPropertyDescriptor(new NcElementId(4, 14), "essenceStatusTransitionCounter", "NcUint64", true, false, false, null, "Essence status transition counter property"), - new NcPropertyDescriptor(new NcElementId(4, 15), "autoResetErrorCounters", "NcBoolean", false, false, false, null, "Automatic reset error counters property (default: true)"), - new NcPropertyDescriptor(new NcElementId(4, 16), "autoResetSynchronizationSourceChanges", "NcBoolean", false, false, false, null, "Automatic reset synchronization source changes property (default: true)"), - new NcPropertyDescriptor(new NcElementId(4, 17), "autoResetStatusTransitionCounters", "NcBoolean", false, false, false, null, "Automatic reset synchronization source changes property (default: true)") + new NcPropertyDescriptor(new NcElementId(4, 11), "essenceStatus", "NcEssenceStatus", true, false, false, null, "Essence status property"), + new NcPropertyDescriptor(new NcElementId(4, 12), "essenceStatusMessage", "NcString", true, true, false, null, "Essence status message property"), + new NcPropertyDescriptor(new NcElementId(4, 13), "essenceStatusTransitionCounter", "NcUint64", true, false, false, null, "Essence status transition counter property"), + new NcPropertyDescriptor(new NcElementId(4, 14), "autoResetCounters", "NcBoolean", false, false, false, null, "Automatic reset counters property (default: true)") ], [ new NcMethodDescriptor(new NcElementId(4, 1), "GetTransmissionErrorCounters", "NcMethodResultCounters", [], "Gets the transmission error counters"), - new NcMethodDescriptor(new NcElementId(4, 2), "ResetErrorCounters", "NcMethodResult", [], "Resets the error counters"), - new NcMethodDescriptor(new NcElementId(4, 3), "ResetSynchronizationSourceChanges", "NcMethodResult", [], "Resets the synchronization source changes counter property"), - new NcMethodDescriptor(new NcElementId(4, 4), "ResetStatusTransitionCounters", "NcMethodResult", [], "Resets ALL status transition counter properties") + new NcMethodDescriptor(new NcElementId(4, 2), "ResetCounters", "NcMethodResult", [], "Resets ALL counters"), ], [] ); @@ -1383,6 +1508,64 @@ export class NcSenderMonitor extends NcStatusMonitor return currentClassDescriptor; } + + public override GetAllProperties(recurse: boolean) : NcObjectPropertiesHolder[] + { + let properties = [ + new NcObjectPropertiesHolder(this.GetRolePath(), [], [ + new NcPropertyValueHolder(new NcPropertyId(4, 1), "linkStatus", "NcLinkStatus", true, this.linkStatus), + new NcPropertyValueHolder(new NcPropertyId(4, 2), "linkStatusMessage", "NcString", true, this.linkStatusMessage), + new NcPropertyValueHolder(new NcPropertyId(4, 3), "linkStatusTransitionCounter", "NcUint64", true, this.linkStatusTransitionCounter), + new NcPropertyValueHolder(new NcPropertyId(4, 4), "transmissionStatus", "NcTransmissionStatus", true, this.transmissionStatus), + new NcPropertyValueHolder(new NcPropertyId(4, 5), "transmissionStatusMessage", "NcString", true, this.transmissionStatusMessage), + new NcPropertyValueHolder(new NcPropertyId(4, 6), "transmissionStatusTransitionCounter", "NcUint64", true, this.transmissionStatusTransitionCounter), + new NcPropertyValueHolder(new NcPropertyId(4, 7), "externalSynchronizationStatus", "NcSynchronizationStatus", true, this.externalSynchronizationStatus), + new NcPropertyValueHolder(new NcPropertyId(4, 8), "externalSynchronizationStatusMessage", "NcString", true, this.externalSynchronizationStatusMessage), + new NcPropertyValueHolder(new NcPropertyId(4, 9), "externalSynchronizationStatusTransitionCounter", "NcUint64", true, this.externalSynchronizationStatusTransitionCounter), + new NcPropertyValueHolder(new NcPropertyId(4, 10), "synchronizationSourceId", "NcString", true, this.synchronizationSourceId), + new NcPropertyValueHolder(new NcPropertyId(4, 11), "essenceStatus", "NcEssenceStatus", true, this.essenceStatus), + new NcPropertyValueHolder(new NcPropertyId(4, 12), "essenceStatusMessage", "NcString", true, this.essenceStatusMessage), + new NcPropertyValueHolder(new NcPropertyId(4, 13), "essenceStatusTransitionCounter", "NcUint64", true, this.essenceStatusTransitionCounter), + new NcPropertyValueHolder(new NcPropertyId(4, 14), "autoResetCounters", "NcBoolean", false, this.autoResetCounters) + ], [], this.isRebuildable) + ]; + + properties[0].values = properties[0].values.concat(super.GetAllProperties(recurse)[0].values); + + return properties; + } + + public override Restore(restoreArguments: RestoreArguments, applyChanges: Boolean) : NcObjectPropertiesSetValidation[] + { + let validationEntries = new Array(); + + let myRestoreData = restoreArguments.dataSet.values.find(f => f.path.join('.') == this.GetRolePath().join('.')) + if(myRestoreData) + { + let myNotices = new Array(); + + myRestoreData.values.forEach(propertyData => { + let propertyId = NcElementId.ToPropertyString(propertyData.id); + if(propertyId != '1p6' && propertyId != '4p1' && propertyId != '4p2' && propertyId != '4p3' && propertyId != '4p4' && propertyId != '4p5' && + propertyId != '4p6' && propertyId != '4p7' && propertyId != '4p8' && propertyId != '4p9' && propertyId != '4p10' && + propertyId != '4p11' && propertyId != '4p12' && propertyId != '4p13') + myNotices.push(new NcPropertyRestoreNotice( + propertyData.id, + propertyData.name, + NcPropertyRestoreNoticeType.Warning, + "Property cannot be changed and will be left untouched")); + else if(applyChanges) + { + //Perform further validation + this.Set(this.oid, propertyData.id, propertyData.value, 0); + } + }); + + validationEntries.push(new NcObjectPropertiesSetValidation(this.GetRolePath(), NcRestoreValidationStatus.Ok, myNotices, myNotices.length > 0 ? 'Some properties have notices' : null)); + } + + return validationEntries; + } } enum ExampleEnum @@ -1486,9 +1669,11 @@ export class ExampleControl extends NcWorker runtimePropertyConstraints: NcPropertyConstraints[] | null, enabled: boolean, description: string, - notificationContext: INotificationContext) + notificationContext: INotificationContext, + isRebuildable: boolean = false, + dataSet: NcObjectPropertiesHolder | null = null) { - super(oid, constantOid, ownerObject, role, userLabel, touchpoints, runtimePropertyConstraints, enabled, description, notificationContext); + super(oid, constantOid, ownerObject, role, userLabel, touchpoints, runtimePropertyConstraints, enabled, description, notificationContext, isRebuildable); this.enumProperty = ExampleEnum.Undefined; this.stringProperty = "test"; @@ -1503,6 +1688,45 @@ export class ExampleControl extends NcWorker this.enumSequence = [ ExampleEnum.Alpha, ExampleEnum.Gamma ]; this.numberSequence = [ 0, 50, 88]; this.objectSequence = [ new ExampleDataType(ExampleEnum.Alpha, "example", 50, false), new ExampleDataType(ExampleEnum.Gamma, "different", 75, true) ]; + + if(dataSet != null) + { + this.InitialiseFromDataset(dataSet); + console.log(`ExampleControl object [${this.role}] constructed from a dataSet`); + } + else + console.log(`ExampleControl object [${this.role}] constructed with defaults`); + } + + private InitialiseFromDataset(dataSet: NcObjectPropertiesHolder) + { + dataSet.values.forEach(propertyData => + { + let propertyId = NcElementId.ToPropertyString(propertyData.id); + switch(propertyId) + { + case '3p1': + this.enumProperty = propertyData.value; + case '3p2': + this.stringProperty = propertyData.value; + case '3p3': + this.numberProperty = propertyData.value; + case '3p4': + this.booleanProperty = propertyData.value; + case '3p5': + this.objectProperty = propertyData.value; + case '3p9': + this.stringSequence = propertyData.value; + case '3p10': + this.booleanSequence = propertyData.value; + case '3p11': + this.enumSequence = propertyData.value; + case '3p12': + this.numberSequence = propertyData.value; + case '3p13': + this.objectSequence = propertyData.value; + } + }); } //'1m1' @@ -1609,7 +1833,7 @@ export class ExampleControl extends NcWorker return new CommandResponseError(handle, NcMethodStatus.BadOid, 'OID could not be found'); } - public override InvokeMethod(socket: WebSocketConnection, oid: number, methodId: NcElementId, args: { [key: string]: any; } | null, handle: number): CommandResponseNoValue + public override InvokeMethod(oid: number, methodId: NcElementId, args: { [key: string]: any; } | null, handle: number): CommandResponseNoValue { if(oid == this.oid) { @@ -2123,7 +2347,7 @@ export class ExampleControl extends NcWorker return new CommandResponseError(handle, NcMethodStatus.InvalidRequest, 'Invalid arguments provided'); } default: - return super.InvokeMethod(socket, oid, methodId, args, handle); + return super.InvokeMethod(oid, methodId, args, handle); } } @@ -2178,4 +2402,64 @@ export class ExampleControl extends NcWorker return currentClassDescriptor; } + + public override GetAllProperties(recurse: boolean) : NcObjectPropertiesHolder[] + { + let properties = [ + new NcObjectPropertiesHolder(this.GetRolePath(), + [ + this.ownerObject?.GetRolePath() ?? [] + ], + [ + new NcPropertyValueHolder(new NcPropertyId(3, 1), "enumProperty", "ExampleEnum", false, this.enumProperty), + new NcPropertyValueHolder(new NcPropertyId(3, 2), "stringProperty", "NcString", false, this.stringProperty), + new NcPropertyValueHolder(new NcPropertyId(3, 3), "numberProperty", "NcUint64", false, this.numberProperty), + new NcPropertyValueHolder(new NcPropertyId(3, 4), "booleanProperty", "NcBoolean", false, this.booleanProperty), + new NcPropertyValueHolder(new NcPropertyId(3, 5), "objectProperty", "ExampleDataType", false, this.objectProperty), + new NcPropertyValueHolder(new NcPropertyId(3, 6), "methodNoArgsCount", "NcUint64", true, this.methodNoArgsCount), + new NcPropertyValueHolder(new NcPropertyId(3, 7), "methodSimpleArgsCount", "NcUint64", true, this.methodSimpleArgsCount), + new NcPropertyValueHolder(new NcPropertyId(3, 8), "methodObjectArgCount", "NcUint64", true, this.methodObjectArgCount), + new NcPropertyValueHolder(new NcPropertyId(3, 9), "stringSequence", "NcString", false, this.stringSequence), + new NcPropertyValueHolder(new NcPropertyId(3, 10), "booleanSequence", "NcBoolean", false, this.booleanSequence), + new NcPropertyValueHolder(new NcPropertyId(3, 11), "enumSequence", "ExampleEnum", false, this.enumSequence), + new NcPropertyValueHolder(new NcPropertyId(3, 12), "numberSequence", "NcUint64", false, this.numberSequence), + new NcPropertyValueHolder(new NcPropertyId(3, 13), "objectSequence", "ExampleDataType", false, this.objectSequence), + ], [], this.isRebuildable) + ]; + + properties[0].values = properties[0].values.concat(super.GetAllProperties(recurse)[0].values); + + return properties; + } + + public override Restore(restoreArguments: RestoreArguments, applyChanges: Boolean) : NcObjectPropertiesSetValidation[] + { + let validationEntries = new Array(); + + let myRestoreData = restoreArguments.dataSet.values.find(f => f.path.join('.') == this.GetRolePath().join('.')) + if(myRestoreData) + { + let myNotices = new Array(); + + myRestoreData.values.forEach(propertyData => { + let propertyId = NcElementId.ToPropertyString(propertyData.id); + if(propertyId != '1p6' && propertyId != '3p1' && propertyId != '3p2' && propertyId != '3p3' && propertyId != '3p4' && propertyId != '3p5' && + propertyId != '3p9' && propertyId != '3p10' && propertyId != '3p11' && propertyId != '3p12' && propertyId != '3p13') + myNotices.push(new NcPropertyRestoreNotice( + propertyData.id, + propertyData.name, + NcPropertyRestoreNoticeType.Warning, + "Property cannot be changed and will be left untouched")); + else if(applyChanges) + { + //Perform further validation + this.Set(this.oid, propertyData.id, propertyData.value, 0); + } + }); + + validationEntries.push(new NcObjectPropertiesSetValidation(this.GetRolePath(), NcRestoreValidationStatus.Ok, myNotices, myNotices.length > 0 ? 'Some properties have notices' : null)); + } + + return validationEntries; + } } \ No newline at end of file diff --git a/code/src/NCModel/Managers.ts b/code/src/NCModel/Managers.ts index 7ff9c68..4bddbf2 100644 --- a/code/src/NCModel/Managers.ts +++ b/code/src/NCModel/Managers.ts @@ -1,13 +1,13 @@ import { jsonIgnoreReplacer, jsonIgnore } from 'json-ignore'; import { CommandResponseError, CommandResponseNoValue, CommandResponseWithValue } from '../NCProtocol/Commands'; import { NcPropertyChangedEventData } from '../NCProtocol/Notifications'; -import { WebSocketConnection } from '../Server'; import { INotificationContext } from '../SessionManager'; import { NcBlock } from './Blocks'; import { BaseType, myIdDecorator, NcBlockMemberDescriptor, + NcBulkValuesHolder, NcClassDescriptor, NcDatatypeDescriptor, NcDatatypeDescriptorEnum, @@ -24,14 +24,18 @@ import { NcMethodId, NcMethodResult, NcMethodResultBlockMemberDescriptors, + NcMethodResultBulkValuesHolder, NcMethodResultClassDescriptor, NcMethodResultDatatypeDescriptor, NcMethodResultError, NcMethodResultId, NcMethodResultLength, + NcMethodResultObjectPropertiesSetValidation, NcMethodResultPropertyValue, NcMethodStatus, NcObject, + NcObjectPropertiesHolder, + NcObjectPropertiesSetValidation, NcParameterConstraints, NcParameterConstraintsNumber, NcParameterConstraintsString, @@ -42,12 +46,17 @@ import { NcPropertyConstraintsString, NcPropertyDescriptor, NcPropertyId, + NcPropertyRestoreNotice, + NcPropertyRestoreNoticeType, + NcPropertyValueHolder, + NcRestoreValidationStatus, NcTouchpoint, NcTouchpointNmos, NcTouchpointNmosChannelMapping, NcTouchpointResource, NcTouchpointResourceNmos, - NcTouchpointResourceNmosChannelMapping} from './Core'; + NcTouchpointResourceNmosChannelMapping, + RestoreArguments} from './Core'; import { ExampleDataType, ExampleControl, GainControl, NcIdentBeacon, NcReceiverMonitor, NcWorker, NcStatusMonitor, NcMethodResultCounters, NcCounter, NcSenderMonitor } from './Features'; export abstract class NcManager extends NcObject @@ -382,6 +391,58 @@ export class NcDeviceManager extends NcManager return currentClassDescriptor; } + + public override GetAllProperties(recurse: boolean) : NcObjectPropertiesHolder[] + { + let properties = [ + new NcObjectPropertiesHolder(this.GetRolePath(), [], [ + new NcPropertyValueHolder(new NcPropertyId(3, 1), "ncVersion", "NcVersionCode", true, this.ncVersion), + new NcPropertyValueHolder(new NcPropertyId(3, 2), "manufacturer", "NcManufacturer", true, this.manufacturer), + new NcPropertyValueHolder(new NcPropertyId(3, 3), "product", "NcProduct", true, this.product), + new NcPropertyValueHolder(new NcPropertyId(3, 4), "serialNumber", "NcString", true, this.serialNumber), + new NcPropertyValueHolder(new NcPropertyId(3, 5), "userInventoryCode", "NcString", false, this.userInventoryCode), + new NcPropertyValueHolder(new NcPropertyId(3, 6), "deviceName", "NcString", false, this.deviceName), + new NcPropertyValueHolder(new NcPropertyId(3, 7), "deviceName", "NcString", false, this.deviceName), + new NcPropertyValueHolder(new NcPropertyId(3, 8), "operationalState", "NcDeviceOperationalState", true, this.operationalState), + new NcPropertyValueHolder(new NcPropertyId(3, 9), "resetCause", "NcResetCause", true, this.resetCause), + new NcPropertyValueHolder(new NcPropertyId(3, 10), "message", "NcString", true, this.message), + ], [], this.isRebuildable) + ]; + + properties[0].values = properties[0].values.concat(super.GetAllProperties(recurse)[0].values); + + return properties; + } + + public override Restore(restoreArguments: RestoreArguments, applyChanges: Boolean) : NcObjectPropertiesSetValidation[] + { + let validationEntries = new Array(); + + let myRestoreData = restoreArguments.dataSet.values.find(f => f.path.join('.') == this.GetRolePath().join('.')) + if(myRestoreData) + { + let myNotices = new Array(); + + myRestoreData.values.forEach(propertyData => { + let propertyId = NcElementId.ToPropertyString(propertyData.id); + if(propertyId != '1p6' && propertyId != '3p5' && propertyId != '3p6' && propertyId != '3p7') + myNotices.push(new NcPropertyRestoreNotice( + propertyData.id, + propertyData.name, + NcPropertyRestoreNoticeType.Warning, + "Property cannot be changed and will be left untouched")); + else if(applyChanges) + { + //Perform further validation + this.Set(this.oid, propertyData.id, propertyData.value, 0); + } + }); + + validationEntries.push(new NcObjectPropertiesSetValidation(this.GetRolePath(), NcRestoreValidationStatus.Ok, myNotices, myNotices.length > 0 ? 'Some properties have notices' : null)); + } + + return validationEntries; + } } export class NcClassManager extends NcManager @@ -442,7 +503,7 @@ export class NcClassManager extends NcManager return new CommandResponseError(handle, NcMethodStatus.BadOid, 'OID could not be found'); } - public override InvokeMethod(socket: WebSocketConnection, oid: number, methodId: NcElementId, args: { [key: string]: any; } | null, handle: number): CommandResponseNoValue + public override InvokeMethod(oid: number, methodId: NcElementId, args: { [key: string]: any; } | null, handle: number): CommandResponseNoValue { if(oid == this.oid) { @@ -648,7 +709,7 @@ export class NcClassManager extends NcManager return new CommandResponseError(handle, NcMethodStatus.InvalidRequest, 'No type name has been provided'); } default: - return super.InvokeMethod(socket, oid, methodId, args, handle); + return super.InvokeMethod(oid, methodId, args, handle); } } @@ -702,7 +763,8 @@ export class NcClassManager extends NcManager '1.2.2.2': NcSenderMonitor.GetClassDescriptor(false), '1.3': NcManager.GetClassDescriptor(false), '1.3.1': NcDeviceManager.GetClassDescriptor(false), - '1.3.2': NcClassManager.GetClassDescriptor(false) + '1.3.2': NcClassManager.GetClassDescriptor(false), + '1.3.3': NcBulkPropertiesManager.GetClassDescriptor(false) }; return register; @@ -726,6 +788,7 @@ export class NcClassManager extends NcManager case '1.3': return NcManager.GetClassDescriptor(true); case '1.3.1': return NcDeviceManager.GetClassDescriptor(true); case '1.3.2': return NcClassManager.GetClassDescriptor(true); + case '1.3.3': return NcBulkPropertiesManager.GetClassDescriptor(true); default: return null; } } @@ -893,7 +956,28 @@ export class NcClassManager extends NcManager 'NcRegex': new NcDatatypeDescriptorTypeDef("NcRegex", "NcString", false, null, "Regex pattern"), 'NcPropertyConstraints': NcPropertyConstraints.GetTypeDescriptor(false), 'NcPropertyConstraintsNumber': NcPropertyConstraintsNumber.GetTypeDescriptor(false), - 'NcPropertyConstraintsString': NcPropertyConstraintsString.GetTypeDescriptor(false) + 'NcPropertyConstraintsString': NcPropertyConstraintsString.GetTypeDescriptor(false), + 'NcBulkValuesHolder': NcBulkValuesHolder.GetTypeDescriptor(false), + 'NcMethodResultBulkValuesHolder': NcMethodResultBulkValuesHolder.GetTypeDescriptor(false), + 'NcMethodResultObjectPropertiesSetValidation': NcMethodResultObjectPropertiesSetValidation.GetTypeDescriptor(false), + 'NcObjectPropertiesHolder': NcObjectPropertiesHolder.GetTypeDescriptor(false), + 'NcObjectPropertiesSetValidation': NcObjectPropertiesSetValidation.GetTypeDescriptor(false), + 'NcPropertyRestoreNotice': NcPropertyRestoreNotice.GetTypeDescriptor(false), + 'NcPropertyValueHolder': NcPropertyValueHolder.GetTypeDescriptor(false), + 'NcPropertyRestoreNoticeType': new NcDatatypeDescriptorEnum("NcPropertyRestoreNoticeType", [ + new NcEnumItemDescriptor("Warning", 300, "Warning property restore notice"), + new NcEnumItemDescriptor("Error", 400, "Error property restore notice") + ], null, "Property restore notice type enumeration"), + 'NcRestoreMode': new NcDatatypeDescriptorEnum("NcRestoreMode", [ + new NcEnumItemDescriptor("Modify", 0, "Restore mode is Modify"), + new NcEnumItemDescriptor("Rebuild", 1, "Restore mode is Rebuild") + ], null, "Restore mode enumeration"), + 'NcRestoreValidationStatus': new NcDatatypeDescriptorEnum("NcRestoreValidationStatus", [ + new NcEnumItemDescriptor("Ok", 200, "Restore was successful"), + new NcEnumItemDescriptor("Failed", 400, "Restore failed"), + new NcEnumItemDescriptor("NotFound", 404, "Restore failed because the role path is not found in the device model or the device cannot create the role path from the data set"), + new NcEnumItemDescriptor("DeviceError", 500, "Restore failed due to an internal device error preventing the restore from happening"), + ], null, "Restore validation status enumeration"), }; return register; @@ -936,11 +1020,18 @@ export class NcClassManager extends NcManager case 'NcMethodResultId': return NcMethodResultId.GetTypeDescriptor(true); case 'NcMethodResultLength': return NcMethodResultLength.GetTypeDescriptor(true); case 'NcMethodResultCounters': return NcMethodResultCounters.GetTypeDescriptor(true); + case 'NcBulkValuesHolder': return NcBulkValuesHolder.GetTypeDescriptor(true); + case 'NcMethodResultBulkValuesHolder': return NcMethodResultBulkValuesHolder.GetTypeDescriptor(true); + case 'NcMethodResultObjectPropertiesSetValidation': return NcMethodResultObjectPropertiesSetValidation.GetTypeDescriptor(true); + case 'NcObjectPropertiesHolder': return NcObjectPropertiesHolder.GetTypeDescriptor(true); + case 'NcObjectPropertiesSetValidation': return NcObjectPropertiesSetValidation.GetTypeDescriptor(true); + case 'NcPropertyRestoreNotice': return NcPropertyRestoreNotice.GetTypeDescriptor(true); + case 'NcPropertyValueHolder': return NcPropertyValueHolder.GetTypeDescriptor(true); default: return this.dataTypesRegister[name]; } } - private GetClassDescriptor(identity: number[], includeInherited: boolean) : NcClassDescriptor | null + public GetClassDescriptor(identity: number[], includeInherited: boolean) : NcClassDescriptor | null { if(includeInherited) return this.GenerateClassDescriptorWithInheritedElements(identity); @@ -952,11 +1043,182 @@ export class NcClassManager extends NcManager } } - private GetTypeDescriptor(name: string, includeInherited: boolean) : NcDatatypeDescriptor | null + public GetTypeDescriptor(name: string, includeInherited: boolean) : NcDatatypeDescriptor | null { if(includeInherited) return this.GenerateTypeDescriptorWithInheritedElements(name); else return this.dataTypesRegister[name]; } + + public override GetAllProperties(recurse: boolean) : NcObjectPropertiesHolder[] + { + let properties = [ + new NcObjectPropertiesHolder(this.GetRolePath(), [], [ + new NcPropertyValueHolder(new NcPropertyId(3, 1), "controlClasses", "NcClassDescriptor", true, this.controlClasses), + new NcPropertyValueHolder(new NcPropertyId(3, 2), "dataTypes", "NcDatatypeDescriptor", true, this.dataTypes) + ], [], this.isRebuildable) + ]; + + properties[0].values = properties[0].values.concat(super.GetAllProperties(recurse)[0].values); + + return properties; + } + + public override Restore(restoreArguments: RestoreArguments, applyChanges: Boolean) : NcObjectPropertiesSetValidation[] + { + let validationEntries = new Array(); + + let myRestoreData = restoreArguments.dataSet.values.find(f => f.path.join('.') == this.GetRolePath().join('.')) + if(myRestoreData) + { + let myNotices = new Array(); + + myRestoreData.values.forEach(propertyData => { + if(NcElementId.ToPropertyString(propertyData.id) != '1p6') + myNotices.push(new NcPropertyRestoreNotice( + propertyData.id, + propertyData.name, + NcPropertyRestoreNoticeType.Warning, + "Property cannot be changed and will be left untouched")); + else if(applyChanges) + { + //Perform further validation + this.Set(this.oid, propertyData.id, propertyData.value, 0); + } + }); + + validationEntries.push(new NcObjectPropertiesSetValidation(this.GetRolePath(), NcRestoreValidationStatus.Ok, myNotices, myNotices.length > 0 ? 'Some properties have notices' : null)); + } + + return validationEntries; + } +} + +export class NcBulkPropertiesManager extends NcManager +{ + public static staticClassID: number[] = [ 1, 3, 3 ]; + + @myIdDecorator('1p1') + public override classID: number[] = NcBulkPropertiesManager.staticClassID; + + public static staticRole: string = "BulkPropertiesManager"; + + public constructor( + oid: number, + constantOid: boolean, + ownerObject: NcObject | null, + userLabel: string, + touchpoints: NcTouchpoint[] | null, + runtimePropertyConstraints: NcPropertyConstraints[] | null, + description: string, + notificationContext: INotificationContext) + { + super(oid, constantOid, ownerObject, NcBulkPropertiesManager.staticRole, userLabel, touchpoints, runtimePropertyConstraints, description, notificationContext); + } + + //'1m1' + public override Get(oid: number, propertyId: NcElementId, handle: number) : CommandResponseNoValue + { + if(oid == this.oid) + { + let key: string = `${propertyId.level}p${propertyId.index}`; + + return super.Get(oid, propertyId, handle); + } + + return new CommandResponseError(handle, NcMethodStatus.BadOid, 'OID could not be found'); + } + + //'1m2' + public override Set(oid: number, id: NcElementId, value: any, handle: number) : CommandResponseNoValue + { + if(oid == this.oid) + { + let key: string = `${id.level}p${id.index}`; + + return super.Set(oid, id, value, handle); + } + + return new CommandResponseError(handle, NcMethodStatus.BadOid, 'OID could not be found'); + } + + public static override GetClassDescriptor(includeInherited: boolean): NcClassDescriptor + { + let currentClassDescriptor = new NcClassDescriptor(`${NcBulkPropertiesManager.name} class descriptor`, + NcBulkPropertiesManager.staticClassID, NcBulkPropertiesManager.name, NcBulkPropertiesManager.staticRole, + [], + [ + new NcMethodDescriptor(new NcElementId(3, 1), "GetPropertiesByPath", "NcMethodResultBulkValuesHolder", [ + new NcParameterDescriptor("path", "NcRolePath", false, false, null, "The target role path"), + new NcParameterDescriptor("recurse", "NcBoolean", false, false, null, "If true will return properties on specified path and all the nested paths") + ], "Get bulk object properties by given path"), + new NcMethodDescriptor(new NcElementId(3, 2), "ValidateSetPropertiesByPath", "NcMethodResultObjectPropertiesSetValidation", [ + new NcParameterDescriptor("dataSet", "NcBulkValuesHolder", false, false, null, "The values offered (this may include read-only values and also paths which are not the target role path)"), + new NcParameterDescriptor("path", "NcRolePath", false, false, null, "The target role path"), + new NcParameterDescriptor("recurse", "NcBoolean", false, false, null, "If true will validate properties on target path and all the nested paths"), + new NcParameterDescriptor("restoreMode", "NcRestoreMode", false, false, null, "Defines the restore mode to be applied") + ], "Validate bulk properties for setting by given paths"), + new NcMethodDescriptor(new NcElementId(3, 3), "SetPropertiesByPath", "NcMethodResultObjectPropertiesSetValidation", [ + new NcParameterDescriptor("dataSet", "NcBulkValuesHolder", false, false, null, "The values offered (this may include read-only values and also paths which are not the target role path)"), + new NcParameterDescriptor("path", "NcRolePath", false, false, null, "The target role path"), + new NcParameterDescriptor("recurse", "NcBoolean", false, false, null, "If true will set properties on target path and all the nested paths"), + new NcParameterDescriptor("restoreMode", "NcRestoreMode", false, false, null, "Defines the restore mode to be applied") + ], "Set bulk properties by given paths"), + ], + [] + ); + + if(includeInherited) + { + let baseDescriptor = super.GetClassDescriptor(includeInherited); + + currentClassDescriptor.properties = currentClassDescriptor.properties.concat(baseDescriptor.properties); + currentClassDescriptor.methods = currentClassDescriptor.methods.concat(baseDescriptor.methods); + currentClassDescriptor.events = currentClassDescriptor.events.concat(baseDescriptor.events); + } + + return currentClassDescriptor; + } + + public override GetAllProperties(recurse: boolean) : NcObjectPropertiesHolder[] + { + let properties = [ + new NcObjectPropertiesHolder(this.GetRolePath(), [], [], [], this.isRebuildable) + ]; + + properties[0].values = properties[0].values.concat(super.GetAllProperties(recurse)[0].values); + + return properties; + } + + public override Restore(restoreArguments: RestoreArguments, applyChanges: Boolean) : NcObjectPropertiesSetValidation[] + { + let validationEntries = new Array(); + + let myRestoreData = restoreArguments.dataSet.values.find(f => f.path.join('.') == this.GetRolePath().join('.')) + if(myRestoreData) + { + let myNotices = new Array(); + + myRestoreData.values.forEach(propertyData => { + let propertyId = NcElementId.ToPropertyString(propertyData.id); + if(propertyId != '1p6') + myNotices.push(new NcPropertyRestoreNotice( + propertyData.id, + propertyData.name, + NcPropertyRestoreNoticeType.Warning, + "Property cannot be changed and will be left untouched")); + else if(applyChanges) + { + //Perform further validation + this.Set(this.oid, propertyData.id, propertyData.value, 0); + } + }); + + validationEntries.push(new NcObjectPropertiesSetValidation(this.GetRolePath(), NcRestoreValidationStatus.Ok, myNotices, myNotices.length > 0 ? 'Some properties have notices' : null)); + } + + return validationEntries; + } } diff --git a/code/src/NCProtocol/Commands.ts b/code/src/NCProtocol/Commands.ts index d755054..d3295fc 100644 --- a/code/src/NCProtocol/Commands.ts +++ b/code/src/NCProtocol/Commands.ts @@ -166,4 +166,4 @@ export class ProtocolError extends ProtocolWrapper { return JSON.stringify(this, jsonIgnoreReplacer); } -} \ No newline at end of file +} diff --git a/code/src/NmosDevice.ts b/code/src/NmosDevice.ts index bdc1192..6133da2 100644 --- a/code/src/NmosDevice.ts +++ b/code/src/NmosDevice.ts @@ -71,7 +71,8 @@ export class NmosDevice extends NmosResource this.controls = [ new NmosControl(`http://${address}:${port}/x-nmos/connection/v1.1/`, 'urn:x-nmos:control:sr-ctrl/v1.1'), new NmosControl(`http://${address}:${port}/x-nmos/connection/v1.0/`, 'urn:x-nmos:control:sr-ctrl/v1.0'), - new NmosControl(`ws://${address}:${port}/x-nmos/ncp/v1.0/connect`, 'urn:x-nmos:control:ncp/v1.0') + new NmosControl(`ws://${address}:${port}/x-nmos/ncp/v1.0/connect`, 'urn:x-nmos:control:ncp/v1.0'), + new NmosControl(`http://${address}:${port}/x-nmos/configuration/v1.0/`, 'urn:x-nmos:control:configuration/v1.0'), ]; this.type = 'urn:x-nmos:device:generic'; diff --git a/code/src/Server.ts b/code/src/Server.ts index ee4f175..888fe9c 100644 --- a/code/src/Server.ts +++ b/code/src/Server.ts @@ -12,9 +12,9 @@ import { RegistrationClient } from './RegistrationClient'; import { NmosReceiverVideo } from './NmosReceiverVideo'; import { NmosReceiverActiveRtp } from './NmosReceiverActiveRtp'; import { SessionManager } from './SessionManager'; -import { NcBlock, RootBlock } from './NCModel/Blocks'; +import { ExampleControlsBlock, NcBlock, RootBlock } from './NCModel/Blocks'; import { NcClassManager, NcDeviceManager } from './NCModel/Managers'; -import { NcMethodStatus, NcTouchpointNmos, NcTouchpointResourceNmos } from './NCModel/Core'; +import { ConfigApiArguments, ConfigApiValue, NcBulkValuesHolder, NcMethodResultBulkValuesHolder, NcMethodResultObjectPropertiesSetValidation, NcMethodStatus, NcTouchpointNmos, NcTouchpointResourceNmos, RestoreBody } from './NCModel/Core'; import { ExampleControl, GainControl, NcIdentBeacon, NcReceiverMonitor, NcSenderMonitor } from './NCModel/Features'; import { ProtocolError, ProtocolSubscription } from './NCProtocol/Commands'; import { MessageType, ProtocolWrapper } from './NCProtocol/Core'; @@ -132,7 +132,6 @@ try const sessionManager = new SessionManager(config.notify_without_subscriptions); const rootBlock = new RootBlock( - 1, true, null, 'root', @@ -145,7 +144,7 @@ try sessionManager); const deviceManager = new NcDeviceManager( - 2, + rootBlock.AllocateOid(rootBlock.GetRolePathForMember(NcDeviceManager.staticRole).join('.')), true, rootBlock, 'Device manager', @@ -155,7 +154,7 @@ try sessionManager); const classManager = new NcClassManager( - 3, + rootBlock.AllocateOid(rootBlock.GetRolePathForMember(NcClassManager.staticRole).join('.')), true, rootBlock, 'Class manager', @@ -164,20 +163,8 @@ try "The class manager offers access to control class and data type descriptors", sessionManager); - const exampleControl = new ExampleControl( - 111, - true, - rootBlock, - 'ExampleControl', - 'Example control worker', - [], - null, - true, - "Example control worker", - sessionManager); - const stereoGainBlock = new NcBlock( - 131, + rootBlock.AllocateOid(rootBlock.GetRolePathForMember('stereo-gain').join('.')), true, rootBlock, 'stereo-gain', @@ -187,10 +174,11 @@ try true, [], "Stereo gain block", - sessionManager); + sessionManager, + rootBlock); const channelGainBlock = new NcBlock( - 121, + rootBlock.AllocateOid(stereoGainBlock.GetRolePathForMember('channel-gain').join('.')), true, stereoGainBlock, 'channel-gain', @@ -200,19 +188,20 @@ try true, [], "Channel gain block", - sessionManager); + sessionManager, + rootBlock); - let leftGain = new GainControl(22, true, channelGainBlock, "left-gain", "Left gain", [], null, true, 0, "Left channel gain", sessionManager); - let rightGain = new GainControl(23, true, channelGainBlock, "right-gain", "Right gain", [], null, true, 0, "Right channel gain", sessionManager); + let leftGain = new GainControl(rootBlock.AllocateOid(channelGainBlock.GetRolePathForMember('left-gain').join('.')), true, channelGainBlock, 'left-gain', 'Left gain', [], null, true, 0, 'Left channel gain', sessionManager); + let rightGain = new GainControl(rootBlock.AllocateOid(channelGainBlock.GetRolePathForMember('right-gain').join('.')), true, channelGainBlock, 'right-gain', 'Right gain', [], null, true, 0, 'Right channel gain', sessionManager); channelGainBlock.UpdateMembers([ leftGain, rightGain ]); - let masterGain = new GainControl(24, true, stereoGainBlock, "master-gain", "Master gain", [], null, true, 0, "Master gain", sessionManager); + let masterGain = new GainControl(rootBlock.AllocateOid(stereoGainBlock.GetRolePathForMember('master-gain').join('.')), true, stereoGainBlock, 'master-gain', 'Master gain', [], null, true, 0, 'Master gain', sessionManager); stereoGainBlock.UpdateMembers([ channelGainBlock, masterGain ]); const receiversBlock = new NcBlock( - 10, + rootBlock.AllocateOid(rootBlock.GetRolePathForMember('receivers').join('.')), true, rootBlock, 'receivers', @@ -222,10 +211,11 @@ try true, [], "Receivers block", - sessionManager); + sessionManager, + rootBlock); const receiverMonitor = new NcReceiverMonitor( - 11, + rootBlock.AllocateOid(receiversBlock.GetRolePathForMember('monitor-01').join('.')), true, receiversBlock, 'monitor-01', @@ -241,7 +231,7 @@ try receiversBlock.UpdateMembers([ receiverMonitor ]); const sendersBlock = new NcBlock( - 20, + rootBlock.AllocateOid(rootBlock.GetRolePathForMember('senders').join('.')), true, rootBlock, 'senders', @@ -250,11 +240,12 @@ try null, true, [], - "Receivers block", - sessionManager); + "Senders block", + sessionManager, + rootBlock); const senderMonitor = new NcSenderMonitor( - 21, + rootBlock.AllocateOid(sendersBlock.GetRolePathForMember('monitor-01').join('.')), true, sendersBlock, 'monitor-01', @@ -269,9 +260,40 @@ try sendersBlock.UpdateMembers([ senderMonitor ]); - const identBeacon = new NcIdentBeacon(51, true, rootBlock, "IdentBeacon", "Identification beacon", [], null, true, false, "Identification beacon", sessionManager); + const identBeacon = new NcIdentBeacon(rootBlock.AllocateOid(rootBlock.GetRolePathForMember('IdentBeacon').join('.')), true, rootBlock, 'IdentBeacon', 'Identification beacon', [], null, true, false, 'Identification beacon', sessionManager); + + const exampleControlsBlock = new ExampleControlsBlock( + rootBlock.AllocateOid(rootBlock.GetRolePathForMember('example-controls').join('.')), + true, + rootBlock, + 'example-controls', + 'Example controls', + null, + null, + true, + [], + "Example controls block", + sessionManager, + rootBlock, + 2, + true); + + const exampleControl = new ExampleControl( + rootBlock.AllocateOid(exampleControlsBlock.GetRolePathForMember('example-control-01').join('.')), + true, + exampleControlsBlock, + 'example-control-01', + 'Example control worker 01', + [], + null, + true, + "Example control worker", + sessionManager, + true); + + exampleControlsBlock.UpdateMembers([ exampleControl ]); - rootBlock.UpdateMembers([ deviceManager, classManager, receiversBlock, sendersBlock, stereoGainBlock, exampleControl, identBeacon ]); + rootBlock.UpdateMembers([ deviceManager, classManager, receiversBlock, sendersBlock, stereoGainBlock, exampleControlsBlock, identBeacon ]); async function doAsync () { await registrationClient.RegisterOrUpdateResource('node', myNode); @@ -287,7 +309,10 @@ try //initialize the Express HTTP listener const app = application(); - app.use(application.json()); + var cors = require('cors'); + app.use(cors()); + app.use(application.json({ limit: '50mb' })); + app.use(application.urlencoded({ extended: true })); //initialize server const server = http.createServer(application); @@ -423,7 +448,8 @@ try res.setHeader('Content-Type', 'application/json'); res.send(JSON.stringify([ 'node/', - 'connection/' + 'connection/', + 'configuration/' ])); }) @@ -707,6 +733,263 @@ try else res.sendStatus(404); }) + + //IS-14 paths + + app.get('/x-nmos/configuration', function (req, res) { + res.setHeader('Content-Type', 'application/json'); + res.send(JSON.stringify([ 'v1.0/' ])); + }) + + app.get('/x-nmos/configuration/:version', function (req, res) { + res.setHeader('Content-Type', 'application/json'); + res.send(JSON.stringify([ + 'rolePaths/' + ])); + }) + + app.get('/x-nmos/configuration/:version/rolePaths', function (req, res) { + res.setHeader('Content-Type', 'application/json'); + + let response = rootBlock.GetRolePathUrls(); + + res.send(JSON.stringify(response.sort((a, b) => (a > b ? -1 : 1)))); + }) + + app.get('/x-nmos/configuration/:version/rolePaths/:rolePath', function (req, res) { + res.setHeader('Content-Type', 'application/json'); + + let rolePath = req.params.rolePath.split('.'); + + let member = rootBlock.FindMemberByRolePath(rolePath); + if(member) + { + res.send(JSON.stringify([ + 'bulkProperties/', + 'descriptor/', + 'methods/', + 'properties/' + ])); + } + else + res.sendStatus(404); + }) + + app.get('/x-nmos/configuration/:version/rolePaths/:rolePath/descriptor/', function (req, res) { + res.setHeader('Content-Type', 'application/json'); + + let rolePath = req.params.rolePath.split('.'); + + let member = rootBlock.FindMemberByRolePath(rolePath); + if(member) + { + res.send(JSON.stringify(classManager.GetClassDescriptor(member.classID, true), jsonIgnoreReplacer)); + } + else + res.sendStatus(404); + }) + + app.get('/x-nmos/configuration/:version/rolePaths/:rolePath/methods/', function (req, res) { + res.setHeader('Content-Type', 'application/json'); + + let rolePath = req.params.rolePath.split('.'); + + let member = rootBlock.FindMemberByRolePath(rolePath); + if(member) + { + res.send(JSON.stringify(classManager.GetClassDescriptor(member.classID, true)?.methods.map(({ id }) => `${id.level}m${id.index}/`).sort((a, b) => (a > b ? 1 : -1)), jsonIgnoreReplacer)); + } + else + res.sendStatus(404); + }) + + app.get('/x-nmos/configuration/:version/rolePaths/:rolePath/properties/', function (req, res) { + res.setHeader('Content-Type', 'application/json'); + + let rolePath = req.params.rolePath.split('.'); + + let member = rootBlock.FindMemberByRolePath(rolePath); + if(member) + { + res.send(JSON.stringify(classManager.GetClassDescriptor(member.classID, true)?.properties.map(({ id }) => `${id.level}p${id.index}/`).sort((a, b) => (a > b ? 1 : -1)), jsonIgnoreReplacer)); + } + else + res.sendStatus(404); + }) + + app.get('/x-nmos/configuration/:version/rolePaths/:rolePath/properties/:propertyId', function (req, res) { + res.setHeader('Content-Type', 'application/json'); + + let rolePath = req.params.rolePath.split('.'); + + let member = rootBlock.FindMemberByRolePath(rolePath); + if(member) + { + let property = classManager.GetClassDescriptor(member.classID, true)?.properties.find(f => req.params.propertyId == `${f.id.level}p${f.id.index}`); + if(property) + res.send(JSON.stringify([ + 'descriptor/', + 'value/' + ])); + else + res.sendStatus(404); + } + else + res.sendStatus(404); + }) + + app.get('/x-nmos/configuration/:version/rolePaths/:rolePath/properties/:propertyId/descriptor', function (req, res) { + res.setHeader('Content-Type', 'application/json'); + + let rolePath = req.params.rolePath.split('.'); + + let member = rootBlock.FindMemberByRolePath(rolePath); + if(member) + { + let property = classManager.GetClassDescriptor(member.classID, true)?.properties.find(f => req.params.propertyId == `${f.id.level}p${f.id.index}`); + if(property?.typeName) + res.send(JSON.stringify(classManager.GetTypeDescriptor(property.typeName, true), jsonIgnoreReplacer)); + else + res.sendStatus(404); + } + else + res.sendStatus(404); + }) + + app.get('/x-nmos/configuration/:version/rolePaths/:rolePath/properties/:propertyId/value', function (req, res) { + res.setHeader('Content-Type', 'application/json'); + + let rolePath = req.params.rolePath.split('.'); + + let member = rootBlock.FindMemberByRolePath(rolePath); + if(member) + { + let property = classManager.GetClassDescriptor(member.classID, true)?.properties.find(f => req.params.propertyId == `${f.id.level}p${f.id.index}`); + if(property) + res.send(JSON.stringify(member.Get(member.oid, property.id, 0).result, jsonIgnoreReplacer)); + else + res.sendStatus(404); + } + else + res.sendStatus(404); + }) + + app.put('/x-nmos/configuration/:version/rolePaths/:rolePath/properties/:propertyId/value', function (req, res) { + res.setHeader('Content-Type', 'application/json'); + + let apiValue = req.body as ConfigApiValue; + + console.log(`Property PUT ${req.url}`); + + let rolePath = req.params.rolePath.split('.'); + + let member = rootBlock.FindMemberByRolePath(rolePath); + if(member) + { + let property = classManager.GetClassDescriptor(member.classID, true)?.properties.find(f => req.params.propertyId == `${f.id.level}p${f.id.index}`); + if(property) + res.send(JSON.stringify(member.Set(member.oid, property.id, apiValue.value, 0).result, jsonIgnoreReplacer)); + else + res.sendStatus(404); + } + else + res.sendStatus(404); + }); + + app.patch('/x-nmos/configuration/:version/rolePaths/:rolePath/methods/:methodId', function (req, res) { + res.setHeader('Content-Type', 'application/json'); + + let apiArguments = req.body as ConfigApiArguments; + + console.log(`Method PATCH ${req.url}`); + + let rolePath = req.params.rolePath.split('.'); + + let member = rootBlock.FindMemberByRolePath(rolePath); + if(member) + { + let method = classManager.GetClassDescriptor(member.classID, true)?.methods.find(f => req.params.methodId == `${f.id.level}m${f.id.index}`); + if(method) + res.send(JSON.stringify(member.InvokeMethod(member.oid, method.id, apiArguments.arguments, 0).result, jsonIgnoreReplacer)); + else + res.sendStatus(404); + } + else + res.sendStatus(404); + }) + + app.get('/x-nmos/configuration/:version/rolePaths/:rolePath/bulkProperties', function (req, res) { + res.setHeader('Content-Type', 'application/json'); + + let recurse: boolean = req.query.recurse === 'false' ? false : true; + + console.log(`BulkProperties GET ${req.url}, recurse: ${recurse}`); + + let rolePath = req.params.rolePath.split('.'); + + let member = rootBlock.FindMemberByRolePath(rolePath); + if(member) + { + let response = new NcMethodResultBulkValuesHolder( + NcMethodStatus.OK, new NcBulkValuesHolder("AMWA NMOS Device Control Mock Application|v1.0", + member.GetAllProperties(recurse))); + + res.send(JSON.stringify(response, jsonIgnoreReplacer)); + } + else + res.sendStatus(404); + }) + + app.patch('/x-nmos/configuration/:version/rolePaths/:rolePath/bulkProperties', function (req, res) { + res.setHeader('Content-Type', 'application/json'); + + let restore = req.body as RestoreBody; + + console.log(`BulkProperties PATCH ${req.url}`); + + let rolePath = req.params.rolePath.split('.'); + + let member = rootBlock.FindMemberByRolePath(rolePath); + if(member) + { + let response = new NcMethodResultObjectPropertiesSetValidation( + NcMethodStatus.OK, + member.Restore(restore.arguments, false)); + + res.send(JSON.stringify(response, jsonIgnoreReplacer)); + } + else + res.sendStatus(404); + }) + + app.put('/x-nmos/configuration/:version/rolePaths/:rolePath/bulkProperties', function (req, res) { + res.setHeader('Content-Type', 'application/json'); + + let restore = req.body as RestoreBody; + + console.log(`BulkProperties PUT ${req.url}`); + + let rolePath = req.params.rolePath.split('.'); + + let member = rootBlock.FindMemberByRolePath(rolePath); + if(member) + { + let response = new NcMethodResultObjectPropertiesSetValidation( + NcMethodStatus.OK, + member.Restore(restore.arguments, true)); + + res.send(JSON.stringify(response, jsonIgnoreReplacer)); + } + else + res.sendStatus(404); + }) + + app.use((req, res, next) => { + //This applied to any invalid path + + res.set({ 'content-type': 'application/json; charset=utf-8' }); + res.status(404).send(''); + }) //start our server server.listen(config.port, () => {