From 8e858a346ac9a236ecfb8392d0f7acd2bfdcb165 Mon Sep 17 00:00:00 2001 From: Christopher Lepski Date: Tue, 9 Jul 2024 13:29:53 +0200 Subject: [PATCH 1/5] Ignore ExternalRef srcLNClass for LN0 --- packages/plugins/src/editors/subscription/foundation.ts | 5 +++++ .../src/editors/subscription/later-binding/foundation.ts | 7 +++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/plugins/src/editors/subscription/foundation.ts b/packages/plugins/src/editors/subscription/foundation.ts index bac28d2b2a..d64efc610c 100644 --- a/packages/plugins/src/editors/subscription/foundation.ts +++ b/packages/plugins/src/editors/subscription/foundation.ts @@ -132,6 +132,11 @@ export function getExtRef( control: Element | undefined ): Element | undefined { function createCriteria(attributeName: string, value: string | null): string { + // TODO: Can we safely ignore srcLNClass? What about elements with a different value + if (attributeName === 'srcLNClass' && value === 'LLN0') { + return ''; + } + if (value) { return `[${attributeName}="${value}"]`; } diff --git a/packages/plugins/src/editors/subscription/later-binding/foundation.ts b/packages/plugins/src/editors/subscription/later-binding/foundation.ts index 4d935b212a..d6233ce69d 100644 --- a/packages/plugins/src/editors/subscription/later-binding/foundation.ts +++ b/packages/plugins/src/editors/subscription/later-binding/foundation.ts @@ -163,6 +163,9 @@ function checkEditionSpecificRequirements( const lDeviceElement = controlElement?.closest('LDevice') ?? undefined; const lnElement = controlElement?.closest('LN0') ?? undefined; + // If ExtRef is missing 'srcLNClass', it defaults to 'LLN0' as specified in the standard + const canIgnoreSrcLNClass = lnElement?.getAttribute('lnClass') === 'LLN0' && !extRefElement.hasAttribute('srcLNClass'); + // For the 2007B and 2007B4 Edition we need to check some extra attributes. return ( (extRefElement.getAttribute('serviceType') ?? '') === @@ -179,12 +182,12 @@ function checkEditionSpecificRequirements( lnElement, 'prefix' ) && - sameAttributeValueDiffName( + (canIgnoreSrcLNClass || sameAttributeValueDiffName( extRefElement, 'srcLNClass', lnElement, 'lnClass' - ) && + )) && sameAttributeValueDiffName(extRefElement, 'srcLNInst', lnElement, 'inst') && sameAttributeValueDiffName( extRefElement, From df541757696c7eba0b41065409a28fec36d580ea Mon Sep 17 00:00:00 2001 From: Christopher Lepski Date: Tue, 9 Jul 2024 16:05:42 +0200 Subject: [PATCH 2/5] Add tests --- .../src/editors/subscription/foundation.ts | 1 - .../editors/ExtRefWithoutSrcLNClass.scd | 356 ++++++++++++++++++ .../editors/subscription/foundation.test.ts | 49 +++ 3 files changed, 405 insertions(+), 1 deletion(-) create mode 100644 packages/plugins/test/testfiles/editors/ExtRefWithoutSrcLNClass.scd diff --git a/packages/plugins/src/editors/subscription/foundation.ts b/packages/plugins/src/editors/subscription/foundation.ts index d64efc610c..72b4f07c9d 100644 --- a/packages/plugins/src/editors/subscription/foundation.ts +++ b/packages/plugins/src/editors/subscription/foundation.ts @@ -132,7 +132,6 @@ export function getExtRef( control: Element | undefined ): Element | undefined { function createCriteria(attributeName: string, value: string | null): string { - // TODO: Can we safely ignore srcLNClass? What about elements with a different value if (attributeName === 'srcLNClass' && value === 'LLN0') { return ''; } diff --git a/packages/plugins/test/testfiles/editors/ExtRefWithoutSrcLNClass.scd b/packages/plugins/test/testfiles/editors/ExtRefWithoutSrcLNClass.scd new file mode 100644 index 0000000000..9ecc2e7fc2 --- /dev/null +++ b/packages/plugins/test/testfiles/editors/ExtRefWithoutSrcLNClass.scd @@ -0,0 +1,356 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GOOSE_Subscriber + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + status-only + + + + + + + + status-only + + + + + + + + + + + + + + + sbo-with-enhanced-security + + + 30000 + + + 600 + + + + + + + + + + + + + + + + + + + + + + + + + IEC 61850-7-4:2007B4 + + + + + + + + + + + + + + + + + + + sbo-with-enhanced-security + + + 30000 + + + 600 + + + + + + + + + + + + + + + + + + + + + + IEC 61850-8-1:2003 + + + + + + + + + IEC 61850-8-1:2003 + + + + + + + + + + + + + + IEC 61850-8-1:2003 + + + + + + + + + IEC 61850-8-1:2003 + + + + + + + Load Break + Disconnector + Earthing Switch + High Speed Earthing Switch + + + status-only + + + pulse + persistent + persistent-feedback + + + Ok + Warning + Alarm + + + status-only + direct-with-normal-security + sbo-with-normal-security + direct-with-enhanced-security + sbo-with-enhanced-security + + + on + blocked + test + test/blocked + off + + + not-supported + bay-control + station-control + remote-control + automatic-bay + automatic-station + automatic-remote + maintenance + process + + + diff --git a/packages/plugins/test/unit/editors/subscription/foundation.test.ts b/packages/plugins/test/unit/editors/subscription/foundation.test.ts index e4a66692f4..8d74983f6e 100644 --- a/packages/plugins/test/unit/editors/subscription/foundation.test.ts +++ b/packages/plugins/test/unit/editors/subscription/foundation.test.ts @@ -3,6 +3,7 @@ import { expect } from '@open-wc/testing'; import { createExtRefElement, getExistingSupervision, + getExtRef, getFirstSubscribedExtRef, instantiatedSupervisionsCount, updateExtRefElement, @@ -285,4 +286,52 @@ describe('foundation', () => { expect(count).to.equal(0); }); }); + + describe('use default value for ExtRef without srcLNClass attribute', () => { + beforeEach(async () => { + doc = await fetch('/test/testfiles/editors/ExtRefWithoutSrcLNClass.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + }); + + it('should correctly fetch ExtRef with srcLNClass attribute', () => { + const inputs = doc.querySelector( + 'LDevice[inst="Earth_Switch"] LN0 Inputs' + )!; + const fcda = doc.querySelector( + 'FCDA[ldInst="QB2_Disconnector"][doName="Pos"][daName="stVal"]' + )!; + const controlBlock = doc.querySelector( + 'IED[name="Publisher"] GSEControl[name="GOOSE2"]' + )!; + + const expectedExtRef = doc.querySelector( + 'ExtRef[iedName="Publisher"][ldInst="QB2_Disconnector"][doName="Pos"][daName="stVal"][srcLNClass="LLN0"]' + )!; + + const extRef = getExtRef(inputs, fcda, controlBlock); + + expect(extRef).to.equal(expectedExtRef) + }); + + it('should correctly fetch ExtRef without srcLNClass attribute', () => { + const inputs = doc.querySelector( + 'LDevice[inst="Earth_Switch"] LN0 Inputs' + )!; + const fcda = doc.querySelector( + 'FCDA[ldInst="QB2_Disconnector"][doName="Pos"][daName="q"]' + )!; + const controlBlock = doc.querySelector( + 'IED[name="Publisher"] GSEControl[name="GOOSE2"]' + )!; + + const expectedExtRef = doc.querySelector( + 'ExtRef[iedName="Publisher"][ldInst="QB2_Disconnector"][doName="Pos"][daName="q"]' + )!; + + const extRef = getExtRef(inputs, fcda, controlBlock); + + expect(extRef).to.equal(expectedExtRef) + }); + }); }); From 80a42e15405b12572084fb47df2d501dd046bb26 Mon Sep 17 00:00:00 2001 From: Christopher Lepski Date: Tue, 9 Jul 2024 16:12:38 +0200 Subject: [PATCH 3/5] Add tests --- .../later-binding/foundation.test.ts | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/packages/plugins/test/unit/editors/subscription/later-binding/foundation.test.ts b/packages/plugins/test/unit/editors/subscription/later-binding/foundation.test.ts index 415df13f60..f42879c11d 100644 --- a/packages/plugins/test/unit/editors/subscription/later-binding/foundation.test.ts +++ b/packages/plugins/test/unit/editors/subscription/later-binding/foundation.test.ts @@ -322,3 +322,44 @@ describe('FCDA specification', () => { bType: 'INT32', })); }); + +describe('use default value for ExtRef without srcLNClass attribute', () => { + let doc: XMLDocument; + beforeEach(async () => { + doc = await fetch('/test/testfiles/editors/ExtRefWithoutSrcLNClass.scd') + .then(response => response.text()) + .then(str => new DOMParser().parseFromString(str, 'application/xml')); + }); + + it('should correctly consider ExtRef with srcLNClass attribute as subscribed', () => { + const fcda = doc.querySelector( + 'FCDA[ldInst="QB2_Disconnector"][doName="Pos"][daName="stVal"]' + )!; + const controlBlock = doc.querySelector( + 'IED[name="Publisher"] GSEControl[name="GOOSE2"]' + )!; + + const extRef = doc.querySelector( + 'ExtRef[iedName="Publisher"][ldInst="QB2_Disconnector"][doName="Pos"][daName="stVal"][srcLNClass="LLN0"]' + )!; + + const isSubscribed = isSubscribedTo('GSEControl', controlBlock, fcda, extRef); + expect(isSubscribed).to.be.true; + }); + + it('should correctly consider ExtRef without srcLNClass attribute as subscribed', () => { + const fcda = doc.querySelector( + 'FCDA[ldInst="QB2_Disconnector"][doName="Pos"][daName="q"]' + )!; + const controlBlock = doc.querySelector( + 'IED[name="Publisher"] GSEControl[name="GOOSE2"]' + )!; + + const extRef = doc.querySelector( + 'ExtRef[iedName="Publisher"][ldInst="QB2_Disconnector"][doName="Pos"][daName="q"]' + )!; + + const isSubscribed = isSubscribedTo('GSEControl', controlBlock, fcda, extRef); + expect(isSubscribed).to.be.true; + }); +}); From fcccc4c6c610c652a0ef3d9e2abba2860461def2 Mon Sep 17 00:00:00 2001 From: Christopher Lepski Date: Tue, 9 Jul 2024 16:15:58 +0200 Subject: [PATCH 4/5] Add comment --- packages/plugins/src/editors/subscription/foundation.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/plugins/src/editors/subscription/foundation.ts b/packages/plugins/src/editors/subscription/foundation.ts index 72b4f07c9d..97a6f507fa 100644 --- a/packages/plugins/src/editors/subscription/foundation.ts +++ b/packages/plugins/src/editors/subscription/foundation.ts @@ -132,6 +132,7 @@ export function getExtRef( control: Element | undefined ): Element | undefined { function createCriteria(attributeName: string, value: string | null): string { + // If ExtRef is missing 'srcLNClass', it defaults to 'LLN0' as specified in the standard if (attributeName === 'srcLNClass' && value === 'LLN0') { return ''; } From 1356490b56e1d9b5c976ec042ba96de0aa7eb60d Mon Sep 17 00:00:00 2001 From: Christopher Lepski Date: Wed, 10 Jul 2024 13:48:51 +0200 Subject: [PATCH 5/5] fix: Use explanatory variable names --- packages/plugins/src/editors/subscription/foundation.ts | 6 ++++-- .../src/editors/subscription/later-binding/foundation.ts | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/plugins/src/editors/subscription/foundation.ts b/packages/plugins/src/editors/subscription/foundation.ts index 97a6f507fa..739678d5df 100644 --- a/packages/plugins/src/editors/subscription/foundation.ts +++ b/packages/plugins/src/editors/subscription/foundation.ts @@ -132,8 +132,10 @@ export function getExtRef( control: Element | undefined ): Element | undefined { function createCriteria(attributeName: string, value: string | null): string { - // If ExtRef is missing 'srcLNClass', it defaults to 'LLN0' as specified in the standard - if (attributeName === 'srcLNClass' && value === 'LLN0') { + // For ExtRef the attribute 'srcLNClass' is optional and defaults to 'LLN0', here we ignore 'srcLNClass' completely for 'LLN0' + // because otherwise we would have to extend the querySelector to multiple selector groups checking for 'LLN0' or missing 'srcLNClass' + const shouldIgnoreCriteria = attributeName === 'srcLNClass' && value === 'LLN0'; + if (shouldIgnoreCriteria) { return ''; } diff --git a/packages/plugins/src/editors/subscription/later-binding/foundation.ts b/packages/plugins/src/editors/subscription/later-binding/foundation.ts index d6233ce69d..d9fef817fd 100644 --- a/packages/plugins/src/editors/subscription/later-binding/foundation.ts +++ b/packages/plugins/src/editors/subscription/later-binding/foundation.ts @@ -164,7 +164,9 @@ function checkEditionSpecificRequirements( const lnElement = controlElement?.closest('LN0') ?? undefined; // If ExtRef is missing 'srcLNClass', it defaults to 'LLN0' as specified in the standard - const canIgnoreSrcLNClass = lnElement?.getAttribute('lnClass') === 'LLN0' && !extRefElement.hasAttribute('srcLNClass'); + const extRefIsMissingSrcLNClass = !extRefElement.hasAttribute('srcLNClass'); + const isLnClassLLN0 = lnElement?.getAttribute('lnClass') === 'LLN0'; + const canIgnoreSrcLNClass = isLnClassLLN0 && extRefIsMissingSrcLNClass; // For the 2007B and 2007B4 Edition we need to check some extra attributes. return (