Skip to content

feat: add describe LN0 #42

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: describeSampledValueControl
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 34 additions & 4 deletions describe.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,31 @@ const testScl = new DOMParser().parseFromString(
<IED name="IED">
<AccessPoint name="AP1">
<Server>
<LDevice inst="ldInst">
<LN lnClass="XCBR" inst="" lnType="baseXCBR"/>
<LN lnClass="XCBR" inst="" lnType="equalXCBR"/>
<LN lnClass="XCBR" inst="" lnType="diffXCBR"/>
<LDevice inst="ldInst1">
<LN0 lnClass="LLN0" inst="" lnType="baseLLN0"/>
<LN lnClass="XCBR" inst="1" lnType="baseXCBR"/>
<LN lnClass="XCBR" inst="1" lnType="equalXCBR"/>
<LN lnClass="XCBR" inst="1" lnType="diffXCBR"/>
</LDevice>
<LDevice inst="ldInst2">
<LN0 lnClass="LLN0" inst="" lnType="equalLLN0"/>
</LDevice>
<LDevice inst="ldInst3">
<LN0 lnClass="LLN0" inst="" lnType="diffLLN0"/>
</LDevice>
</Server>
</AccessPoint>
</IED>
<DataTypeTemplates>
<LNodeType lnClass="LLN0" id="baseLLN0" >
<DO name="Beh" type="someEqualDOType" />
</LNodeType>
<LNodeType lnClass="LLN0" id="equalLLN0" >
<DO name="Beh" type="someEqualDOType" />
</LNodeType>
<LNodeType lnClass="LLN0" id="diffLLN0" >
<DO name="Beh" type="someDiffDOType" />
</LNodeType>
<LNodeType lnClass="XCBR" id="equalXCBR" >
<DO name="Beh" type="someEqualDOType" />
</LNodeType>
Expand Down Expand Up @@ -102,6 +118,10 @@ const baseLN = testScl.querySelector(`LN[lnType="baseXCBR"]`)!;
const equalLN = testScl.querySelector(`LN[lnType="equalXCBR"]`)!;
const diffLN = testScl.querySelector(`LN[lnType="diffXCBR"]`)!;

const baseLN0 = testScl.querySelector(`LDevice[inst="ldInst1"]>LN0`)!;
const equalLN0 = testScl.querySelector(`LDevice[inst="ldInst2"]>LN0`)!;
const diffLN0 = testScl.querySelector(`LDevice[inst="ldInst3"]>LN0`)!;

describe("Describe SCL elements function", () => {
it("returns undefined with missing describe function", () =>
expect(describeSclElement(oneNonSCLElement)).to.be.undefined);
Expand All @@ -119,6 +139,16 @@ describe("Describe SCL elements function", () => {
JSON.stringify(describeSclElement(equalEnumType)),
));

it("returns same description with semantically equal LN0's", () =>
expect(JSON.stringify(describeSclElement(baseLN0))).to.equal(
JSON.stringify(describeSclElement(equalLN0)),
));

it("returns different description with unequal LN0 elements", () =>
expect(JSON.stringify(describeSclElement(baseLN0))).to.not.equal(
JSON.stringify(describeSclElement(diffLN0)),
));

it("returns same description with semantically equal LN's", () =>
expect(JSON.stringify(describeSclElement(baseLN))).to.equal(
JSON.stringify(describeSclElement(equalLN)),
Expand Down
5 changes: 4 additions & 1 deletion describe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { DAType, DATypeDescription } from "./describe/DAType.js";
import { DOType, DOTypeDescription } from "./describe/DOType.js";
import { LNodeType, LNodeTypeDescription } from "./describe/LNodeType.js";
import { LN, LNDescription } from "./describe/LN.js";
import { LN0, LN0Description } from "./describe/LN0.js";

export type Description =
| PrivateDescription
Expand All @@ -14,7 +15,8 @@ export type Description =
| DATypeDescription
| DOTypeDescription
| LNodeTypeDescription
| LNDescription;
| LNDescription
| LN0Description;
const sclElementDescriptors: Partial<
Record<string, (element: Element) => Description | undefined>
> = {
Expand All @@ -25,6 +27,7 @@ const sclElementDescriptors: Partial<
DOType,
LNodeType,
LN,
LN0,
};

export function describe(element: Element): Description | undefined {
Expand Down
6 changes: 3 additions & 3 deletions describe/ControlWithIEDName.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const scl = new DOMParser().parseFromString(
</AccessPoint>
</IED>
</SCL>`,
"application/xml"
"application/xml",
);

const baseGSEControl = scl.querySelector(`*[datSet="baseDataSet"]`)!;
Expand All @@ -70,11 +70,11 @@ describe("Description for SCL schema type tControlWithIEDName", () => {

it("returns same description with semantically equal ControlWithIEDName's", () =>
expect(JSON.stringify(describeControlWithIEDName(baseGSEControl))).to.equal(
JSON.stringify(describeControlWithIEDName(equalGSEControl))
JSON.stringify(describeControlWithIEDName(equalGSEControl)),
));

it("returns different description with unequal ControlWithIEDName elements", () =>
expect(
JSON.stringify(describeControlWithIEDName(baseGSEControl))
JSON.stringify(describeControlWithIEDName(baseGSEControl)),
).to.not.equal(JSON.stringify(describeControlWithIEDName(diffGSEControl))));
});
8 changes: 4 additions & 4 deletions describe/ControlWithIEDName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ function compareIEDNameDescription(a: IEDName, b: IEDName): number {
return 0;
}

type IEDName = {
interface IEDName {
/** IEDName attribute apRef*/
apRef?: string;
/** IEDName attribute ldInst*/
Expand All @@ -22,7 +22,7 @@ type IEDName = {
lnInst?: string;
/** IEDName child text content */
val?: string;
};
}

function describeIEDName(element: Element): IEDName {
const iedName: IEDName = {};
Expand Down Expand Up @@ -55,7 +55,7 @@ export interface ControlWithIEDNameDescription extends ControlDescription {
}

export function describeControlWithIEDName(
element: Element
element: Element,
): ControlWithIEDNameDescription | undefined {
const controlDescription = describeControl(element);
if (!controlDescription) return;
Expand All @@ -69,7 +69,7 @@ export function describeControlWithIEDName(
...Array.from(element.children)
.filter((child) => child.tagName === "IEDName")
.map((iedName) => describeIEDName(iedName))
.sort(compareIEDNameDescription)
.sort(compareIEDNameDescription),
);

const confRev = element.getAttribute("confRev");
Expand Down
6 changes: 3 additions & 3 deletions describe/GSEControl.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const scl = new DOMParser().parseFromString(
</AccessPoint>
</IED>
</SCL>`,
"application/xml"
"application/xml",
);

const baseGSEControl = scl.querySelector(`*[datSet="baseDataSet"]`)!;
Expand All @@ -72,11 +72,11 @@ describe("Description for SCL schema type tControlWithIEDName", () => {

it("returns same description with semantically equal GSEControl's", () =>
expect(JSON.stringify(describeGSEControl(baseGSEControl))).to.equal(
JSON.stringify(describeGSEControl(equalGSEControl))
JSON.stringify(describeGSEControl(equalGSEControl)),
));

it("returns different description with unequal GSEControl elements", () =>
expect(JSON.stringify(describeGSEControl(baseGSEControl))).to.not.equal(
JSON.stringify(describeGSEControl(diffGSEControl))
JSON.stringify(describeGSEControl(diffGSEControl)),
));
});
4 changes: 2 additions & 2 deletions describe/GSEControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export interface GSEControlDescription extends ControlWithIEDNameDescription {
}

export function describeGSEControl(
element: Element
element: Element,
): GSEControlDescription | undefined {
const controlWithTriggerOptDesc = describeControlWithIEDName(element);
if (!controlWithTriggerOptDesc) return;
Expand All @@ -35,7 +35,7 @@ export function describeGSEControl(
};

const protocol = Array.from(element.children).find(
(child) => child.tagName === "Protocol"
(child) => child.tagName === "Protocol",
);
if (protocol)
gseControlDescription.protocol = { mustUnderstand: true, val: "R-GOOSE" };
Expand Down
184 changes: 184 additions & 0 deletions describe/LN0.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import { expect } from "chai";
import { LN } from "./LN.js";
import { LNodeType, isLNodeTypeDescription } from "./LNodeType.js";
import { LN0 } from "./LN0.js";

const scl = new DOMParser().parseFromString(
`<SCL xmlns="http://www.iec.ch/61850/2003/SCL" >
<IED name="IED1">
<AccessPoint name="AP1">
<Server>
<LDevice inst="lDevice1">
<LN0 lnClass="LLN0" inst="" lnType="LLN0">
<DataSet name="baseDataSet" >
<FCDA iedName="IED1" ldInst="lDevice" lnClass="XCBR" lnInst="1" doName="Pos" daName="stVal" fc="ST" />
<FCDA iedName="IED1" ldInst="lDevice" prefix="" lnClass="XCBR" lnInst="1" doName="Pos" daName="q" fc="ST" />
<FCDA iedName="IED1" ldInst="lDevice" lnClass="LLN0" doName="Beh" daName="stVal" fc="ST" />
<FCDA iedName="IED1" ldInst="lDevice" prefix="" lnClass="LLN0" lnInst="" doName="Beh" fc="ST" />
</DataSet>
<DOI name="Beh" >
<DAI name="stVal">
<Val>on</Val>
</DAI>
</DOI>
<GSEControl name="gse1" datSet="baseDataSet" />
<GSEControl name="gse2" />
<SampledValueControl name="smv1" datSet="baseDataSet" smpRate="80" nofASDU="1" >
<SmvOpts />
</SampledValueControl>
<SampledValueControl name="smv2" smpRate="80" nofASDU="1" >
<SmvOpts />
</SampledValueControl>
<LogControl name="anotherLog" logName="logName" />
<LogControl name="log" dataSet="baseDataSet" logName="logName" reasonCode="true" logEna="true" intgPd="0" bufTime="0" >
<TrgOps dchg="false" qchg="false" dupd="false" period="false" gi="false" />
</LogControl>
<ReportControl name="report" datSet="baseDataSet" intgPd="0" indexed="true" buffered="true" bufTime="0" confRev="0" >
<TrgOps dchg="false" qchg="false" dupd="false" period="false" gi="false" />
<OptFields seqNum="false" timeStamp="false" dataSet="false" reasonCode="false" dataRef="false" entryID="false" configRef="false" bufOvfl="false" />
<RptEnabled max="0" />
</ReportControl>
<ReportControl name="anotherReport" />
<Log name="someLog" />
<Log name="someOtherLog" />
<Inputs>
<ExtRef intAddr="Beh.t" pLN="LLN0" pDO="Beh" pDA="t" pServT="GOOSE" />
</Inputs>
<SettingControl numOfSGs="4" />
</LN0>
</LDevice>
<LDevice inst="lDevice2">
<LN0 lnClass="LLN0" inst="" lnType="LLN02" >
<DataSet name="equalDataSet" >
<FCDA iedName="IED1" ldInst="lDevice" prefix="" lnClass="XCBR" lnInst="1" doName="Pos" daName="stVal" fc="ST" />
<FCDA iedName="IED1" ldInst="lDevice" lnClass="XCBR" lnInst="1" doName="Pos" daName="q" fc="ST" />
<FCDA iedName="IED1" ldInst="lDevice" prefix="" lnClass="LLN0" lnInst="" doName="Beh" daName="stVal" fc="ST" />
<FCDA iedName="IED1" ldInst="lDevice" lnClass="LLN0" doName="Beh" fc="ST" />
</DataSet>
<GSEControl name="gse2" />
<GSEControl name="gse1" datSet="equalDataSet" />
<SampledValueControl name="smv2" smpRate="80" nofASDU="1" >
<SmvOpts />
</SampledValueControl>
<SampledValueControl name="smv1" datSet="equalDataSet" smpRate="80" nofASDU="1" >
<SmvOpts />
</SampledValueControl>
<LogControl name="log" dataSet="equalDataSet" logName="logName" />
<LogControl name="anotherLog" logName="logName" />
<ReportControl name="anotherReport" />
<ReportControl name="report" datSet="equalDataSet" />
<Log name="someOtherLog" />
<Log name="someLog" />
<Inputs>
<ExtRef intAddr="Beh.t" pLN="LLN0" pDO="Beh" pDA="t" pServT="GOOSE" />
</Inputs>
<SettingControl numOfSGs="4" />
</LN0>
</LDevice>
<LDevice inst="lDevice3">
<LN0 lnClass="LLN0" inst="" lnType="LLN0" >
<DOI name="Beh" >
<DAI name="stVal">
<Val>test</Val>
</DAI>
</DOI>
<Inputs>
<ExtRef intAddr="A.phsA" pLN="MMXU" pDO="A.phsA" pDA="cVal.mag.f" pServT="SMV" />
</Inputs>
</LN0>
</LDevice>
<LDevice inst="lDevice4" >
<LN0 lnClass="LLN0" inst="" lnType="diffLLN0" />
</LDevice>
<LDevice inst="lDevice5" >
<LN0 lnClass="LLN0" />
</LDevice>
<LDevice inst="lDevice6" >
<LN0 lnClass="LLN0" lnType="invalidReference" />
</LDevice>
<LDevice inst="lDevice7" >
<LN0 lnClass="LLN0" lnType="invalidLLN0" />
</LDevice>
</Server>
</AccessPoint>
</IED>
<DataTypeTemplates>
<LNodeType id="invalidLLN0" desc="desc" lnType="lnType" />
<LNodeType id="LLN0" desc="desc" lnClass="LLN0">
<DO name="Beh" type="BehENS"/>
</LNodeType>
<LNodeType id="LLN02" desc="desc" lnClass="LLN0">
<DO name="Beh" type="BehENS2"/>
</LNodeType>
<LNodeType id="diffLLN0" desc="desc" lnClass="LLN0">
<DO name="Beh" type="diffBehENS"/>
</LNodeType>
<DOType cdc="ENS" id="BehENS" >
<DA name="stVal" bType="Enum" type="BehModKind" fc="ST" >
<Val>off</Val>
</DA>
</DOType>
<DOType cdc="ENS" id="BehENS2" >
<DA name="stVal" bType="Enum" type="BehModKind" fc="ST" >
<Val>on</Val>
</DA>
</DOType>
<DOType id="diffBehENS" cdc="ENS" >
<DA name="stVal" bType="Enum" type="BehModKind" fc="ST" >
<Val>on</Val>
</DA>
<DA name="q" bType="Quality" fc="ST" />
</DOType>
<EnumType id="BehModKind" >
<EnumVal ord="1">on</EnumVal>
<EnumVal ord="3">test</EnumVal>
<EnumVal ord="5">off</EnumVal>
</EnumType>
<EnumType id="diffBehModKind" >
<EnumVal ord="1">on</EnumVal>
<EnumVal ord="3">test</EnumVal>
</EnumType>
</DataTypeTemplates>
</SCL>`,
"application/xml",
);

const missingLnType = scl.querySelector('LDevice[inst="lDevice5"] > LN0')!;
const invalidLnType = scl.querySelector('LDevice[inst="lDevice6"] > LN0')!;
const invalidLnTypeDescription = scl.querySelector(
'LDevice[inst="lDevice7"] > LN0',
)!;

const baseLLN0 = scl.querySelector(`LDevice[inst="lDevice1"] > LN0`)!;
const equalLLN0 = scl.querySelector('LDevice[inst="lDevice2"] > LN0')!;
const diffLLN0 = scl.querySelector('LDevice[inst="lDevice3"] > LN0')!;
const diffEnumType = scl.querySelector('LDevice[inst="lDevice4"] > LN0')!;

describe("Description for SCL schema type LN0", () => {
it("returns undefined with missing lnType attribute", () =>
expect(LN0(missingLnType)).to.be.undefined);

it("returns undefined with invalid lnType attribute", () =>
expect(LN0(invalidLnType)).to.be.undefined);

it("returns undefined with invalid LNodeType description", () =>
expect(LN0(invalidLnTypeDescription)).to.be.undefined);

it("return logical node structure in lnType key", () =>
expect(LN0(baseLLN0)?.lnType).to.satisfy(isLNodeTypeDescription));

it("returns same description with semantically equal LN0's", () => {
expect(JSON.stringify(LN0(baseLLN0))).to.equal(
JSON.stringify(LN0(equalLLN0)),
);
});

it("returns different description with unequal LN0 elements", () => {
expect(JSON.stringify(LN0(baseLLN0))).to.not.equal(
JSON.stringify(LN0(diffLLN0)),
);
expect(JSON.stringify(LN0(baseLLN0))).to.not.equal(
JSON.stringify(LN0(diffEnumType)),
);
});
});
Loading