diff --git a/describe.spec.ts b/describe.spec.ts index 6b0e7b9c..8222f419 100644 --- a/describe.spec.ts +++ b/describe.spec.ts @@ -14,15 +14,15 @@ const testScl = new DOMParser().parseFromString( - - - + + - + + @@ -114,13 +114,9 @@ const baseEnumType = testScl.querySelector("#someID")!; const diffEnumType = testScl.querySelector("#someDiffID")!; const equalEnumType = testScl.querySelector("#someOtherID")!; -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`)!; +const baseLDevice = testScl.querySelector(`LDevice[inst="ldInst1"]`)!; +const equalLDevice = testScl.querySelector(`LDevice[inst="ldInst2"]`)!; +const diffLDevice = testScl.querySelector(`LDevice[inst="ldInst3"]`)!; describe("Describe SCL elements function", () => { it("returns undefined with missing describe function", () => @@ -139,23 +135,13 @@ 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)), + it("returns same description with semantically equal LDevice's", () => + expect(JSON.stringify(describeSclElement(baseLDevice))).to.equal( + JSON.stringify(describeSclElement(equalLDevice)), )); - it("returns different description with unequal LN elements", () => - expect(JSON.stringify(describeSclElement(baseLN))).to.not.equal( - JSON.stringify(describeSclElement(diffLN)), + it("returns different description with unequal LDevice elements", () => + expect(JSON.stringify(describeSclElement(baseLDevice))).to.not.equal( + JSON.stringify(describeSclElement(diffLDevice)), )); }); diff --git a/describe.ts b/describe.ts index f65f066d..c5da5b88 100644 --- a/describe.ts +++ b/describe.ts @@ -3,6 +3,7 @@ import { Text, TextDescription } from "./describe/Text.js"; import { EnumType, EnumTypeDescription } from "./describe/EnumType.js"; import { DAType, DATypeDescription } from "./describe/DAType.js"; import { DOType, DOTypeDescription } from "./describe/DOType.js"; +import { LDevice, LDeviceDescription } from "./describe/LDevice.js"; import { LNodeType, LNodeTypeDescription } from "./describe/LNodeType.js"; import { LN, LNDescription } from "./describe/LN.js"; import { LN0, LN0Description } from "./describe/LN0.js"; @@ -16,7 +17,8 @@ export type Description = | DOTypeDescription | LNodeTypeDescription | LNDescription - | LN0Description; + | LN0Description + | LDeviceDescription; const sclElementDescriptors: Partial< Record Description | undefined> > = { @@ -28,6 +30,7 @@ const sclElementDescriptors: Partial< LNodeType, LN, LN0, + LDevice, }; export function describe(element: Element): Description | undefined { diff --git a/describe/LDevice.spec.ts b/describe/LDevice.spec.ts new file mode 100644 index 00000000..9604560d --- /dev/null +++ b/describe/LDevice.spec.ts @@ -0,0 +1,294 @@ +import { expect } from "chai"; +import { LDevice } from "./LDevice.js"; + +const scl = new DOMParser().parseFromString( + ` + + + + + + + + + + + + + + on + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10.10 + 30.30 + 20.20 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + test + + + + + + + + + + 10.10 + 30.30 + 20.20 + + + + Some AccessControl Own Namespace thingy! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + off + + + + + on + + + + + on + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 60.60 + 10.10 + 40.10 + + + + + 30.30 + 10.10 + 20.20 + + + + on + test + off + + + on + test + + + `, + "application/xml", +); + +/* +const missingLnType = scl.querySelector('*[desc="missingLnType"]')!; +const invalidLnType = scl.querySelector('*[desc="invalidLnType"]')!; +const invalidLnTypeDescription = scl.querySelector( + '*[desc="invalidLnTypeDescription"]' +)!; */ + +const baseLDevice = scl.querySelector(`LDevice[inst="lDevice1"]`)!; +const equalLDevice = scl.querySelector('LDevice[inst="lDevice2"]')!; +const diffLDevice = scl.querySelector('LDevice[inst="lDevice3"]')!; +const missingLLN0 = scl.querySelector('LDevice[inst="lDevice4"]')!; +const missingLnType = scl.querySelector('LDevice[inst="lDevice5"]')!; +const invalidLnType = scl.querySelector('LDevice[inst="lDevice6"]')!; +const missingInst = scl.querySelector('LDevice[inst="lDevice7"]')!; +const undefinedLN = scl.querySelector('LDevice[inst="lDevice8"]')!; + +describe("Description for SCL schema type LDevice", () => { + it("returns undefined with missing lnType attribute", () => + expect(LDevice(missingLLN0)).to.be.undefined); + + it("returns undefined with missing lnType attribute", () => + expect(LDevice(missingLnType)).to.be.undefined); + + it("returns undefined with invalid lnType attribute", () => + expect(LDevice(invalidLnType)).to.be.undefined); + + it("returns undefined with missing lnType attribute", () => + expect(LDevice(missingInst)).to.be.undefined); + + it("returns undefined with at least one undefined LNDescription", () => + expect(LDevice(undefinedLN)).to.be.undefined); + + it("return ldName attribute if present", () => + expect(LDevice(diffLDevice)?.ldName).to.equal("ldName")); + + it("return AccessControl child as a string if present", () => + expect(LDevice(diffLDevice)?.accessControl).to.equal( + `Some AccessControl Own Namespace thingy!`, + )); + + it("returns same description with semantically equal LDevice's", () => + expect(JSON.stringify(LDevice(baseLDevice))).to.equal( + JSON.stringify(LDevice(equalLDevice)), + )); + + it("returns different description with unequal LDevice elements", () => + expect(JSON.stringify(LDevice(baseLDevice))).to.not.equal( + JSON.stringify(LDevice(diffLDevice)), + )); +}); diff --git a/describe/LDevice.ts b/describe/LDevice.ts new file mode 100644 index 00000000..48b99b0b --- /dev/null +++ b/describe/LDevice.ts @@ -0,0 +1,63 @@ +import { LN0, LN0Description } from "./LN0.js"; +import { LN, LNDescription } from "./LN.js"; +import { NamingDescription, describeNaming } from "./Naming.js"; +import { sortRecord } from "../utils.js"; + +export interface LDeviceDescription extends NamingDescription { + /** LDevice attribute ldName */ + ldName?: string; + /** LDevice child LN0 */ + ln0: LN0Description; + /** LDevice children LN */ + lns: Record; + /** LDevice child AccessControl */ + accessControl?: string; +} + +export function LDevice(element: Element): LDeviceDescription | undefined { + const ln0 = element.querySelector(":scope > LN0"); + if (!ln0) return; + + const ln0Description = LN0(ln0); + if (!ln0Description) return; + + const lns: Record = {}; + let existUndefinedLns = false; + Array.from(element.children) + .filter((child) => child.tagName === "LN") + .forEach((ln) => { + const prefix = ln.getAttribute("prefix"); + const lnClass = ln.getAttribute("lnClass"); + const inst = ln.getAttribute("inst"); + if (!lnClass || !inst) { + existUndefinedLns = true; + return; + } + + const id = `${prefix ? prefix : ""}${lnClass}${inst}`; + + const lnDescription = LN(ln); + if (!lnDescription) { + existUndefinedLns = true; + return; + } + + lns[id] = lnDescription; + }); + + if (existUndefinedLns) return; + + const lDeviceDescription: LDeviceDescription = { + ...describeNaming(element), + ln0: ln0Description, + lns: sortRecord(lns) as Record, + }; + + const ldName = element.getAttribute("ldName"); + if (ldName) lDeviceDescription.ldName = ldName; + + const accessControl = element.querySelector(":scope > AccessControl"); + if (accessControl) lDeviceDescription.accessControl = accessControl.outerHTML; + + return lDeviceDescription; +} diff --git a/utils.ts b/utils.ts index 2ab18c85..407f14ae 100644 --- a/utils.ts +++ b/utils.ts @@ -1,6 +1,7 @@ import { DADescription } from "./describe/DADescription.js"; import { DODescription } from "./describe/DODescription.js"; import { GSEControlDescription } from "./describe/GSEControl.js"; +import { LNDescription } from "./describe/LN.js"; import { LogControlDescription } from "./describe/LogControl.js"; import { NamingDescription } from "./describe/Naming.js"; import { ReportControlDescription } from "./describe/ReportControl.js"; @@ -10,6 +11,7 @@ import { SampledValueControlDescription } from "./describe/SampledValueControl.j type SortedObjects = | DADescription | GSEControlDescription + | LNDescription | LogControlDescription | NamingDescription | SampledValueControlDescription