Skip to content

Commit

Permalink
feat(tDataTypeTemplates): remove LNodeType, DOType, DAType or EnumType
Browse files Browse the repository at this point in the history
  • Loading branch information
JakobVogelsang committed Oct 29, 2024
1 parent 2999a86 commit 453a273
Show file tree
Hide file tree
Showing 3 changed files with 253 additions and 0 deletions.
34 changes: 34 additions & 0 deletions tDataTypeTemplates/removeDataType.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { expect } from "chai";
import { findElement } from "../foundation/helpers.test.js";

import { invalidScl, mmxu } from "./removeDataType.testfiles.js";
import { removeDataType } from "./removeDataType.js";

const invalidLNodeType = findElement(invalidScl, "LNodeType") as XMLDocument;
const doType = findElement(mmxu, "DOType") as XMLDocument;
const lNodeType = findElement(mmxu, "LNodeType") as XMLDocument;

describe("Function to recursively remove data types ", () => {
it("returns empty array with SCL files", () => {
expect(removeDataType({ node: invalidLNodeType }).length).equals(0);
});

it("does not remove linked data types per default", () => {
const removes = removeDataType({ node: doType });
expect(removes.length).equals(0);
});

it("removes linked data types with force option set", () => {
const removes = removeDataType({ node: doType }, { force: true });

expect(removes.length).equals(6);
expect(removes[0].node).equal(doType);
});

it("does not remove linked data types with force option set", () => {
const removes = removeDataType({ node: lNodeType });

expect(removes.length).equals(15);
expect(removes[0].node).equal(lNodeType);
});
});
150 changes: 150 additions & 0 deletions tDataTypeTemplates/removeDataType.testfiles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
export const invalidScl = `<?xml version="1.0" encoding="UTF-8"?>
<SCL xmlns="http://www.iec.ch/61850/2003/SCL" version="2007" revision="B" release="4">
<LNodeType id="someFakeIds" >
<DO name="Beh" type="someType"
</LNodeType>
</SCL>`;

export const mmxu = `<?xml version="1.0" encoding="UTF-8"?>
<SCL xmlns="http://www.iec.ch/61850/2003/SCL" version="2007" revision="B" release="4">
<Header id="project"/>
<DataTypeTemplates>
<LNodeType lnClass="MMXU" id="MMXU$oscd$_c83c0b5963558681">
<DO name="Beh" type="Beh$oscd$_f1a426046ceedd46"/>
<DO name="A" type="A$oscd$_67cc94e14f712b32"/>
<DO name="Hz" type="Hz$oscd$_979742e3ddefd568"/>
<DO name="Mod" type="Mod$oscd$_f595ae60fd1bdba5"/>
</LNodeType>
<LNodeType lnClass="PTOC" id="PTOC$oscd$_c83c0b59635asdd">
<DO name="Beh" type="Beh$oscd$_f1a426046ceedd46"/>
<DO name="Mod" type="Mod$oscd$_f595ae60fd1bdba5"/>
</LNodeType>
<DOType cdc="ENC" id="Mod$oscd$_f595ae60fd1bdba5">
<DA name="stVal" fc="ST" dchg="true" bType="Enum" type="stVal$oscd$_48ba16345b8e7f5b"/>
<DA name="q" fc="ST" qchg="true" bType="Quality"/>
<DA name="t" fc="ST" bType="Timestamp"/>
<DA name="ctlModel" fc="CF" dchg="true" bType="Enum" type="ctlModel$oscd$_d3bc74b2690f022e"/>
<DA name="SBOw" fc="CO" bType="Struct" type="SBOw$oscd$_e57fac30c1740d87"/>
<DA name="Oper" fc="CO" bType="Struct" type="Oper$oscd$_e57fac30c1740d87"/>
<DA name="Cancel" fc="CO" bType="Struct" type="Cancel$oscd$_7f4258185194f976"/>
</DOType>
<DOType cdc="ENC" id="Mod$oscd$_f595ae60fd1bdasdf">
<DA name="stVal" fc="ST" dchg="true" bType="Enum" type="stVal$oscd$_48ba16345b8e7f5b"/>
<DA name="q" fc="ST" qchg="true" bType="Quality"/>
<DA name="t" fc="ST" bType="Timestamp"/>
<DA name="ctlModel" fc="CF" dchg="true" bType="Enum" type="ctlModel$oscd$_d3bc74b2690f022e"/>
</DOType>
<DOType cdc="MV" id="Hz$oscd$_979742e3ddefd568">
<DA name="mag" fc="MX" dchg="true" dupd="true" bType="Struct" type="mag$oscd$_ed49c2f7a55ad05a"/>
<DA name="q" fc="MX" qchg="true" bType="Quality"/>
<DA name="t" fc="MX" bType="Timestamp"/>
<DA name="units" fc="CF" dchg="true" bType="Struct" type="units$oscd$_18b92a11f3e0b360"/>
<DA name="sVC" fc="CF" dchg="true" bType="Struct" type="sVC$oscd$_df6488ea078bf55c"/>
</DOType>
<DOType cdc="WYE" id="A$oscd$_67cc94e14f712b32">
<SDO name="phsC" type="phsC$oscd$_16d33d83a36d07b3"/>
<SDO name="phsB" type="phsB$oscd$_0b99219f396fac0f"/>
<SDO name="phsA" type="phsA$oscd$_16d33d83a36d07b3"/>
</DOType>
<DOType cdc="CMV" id="phsC$oscd$_16d33d83a36d07b3">
<DA name="cVal" fc="MX" dchg="true" dupd="true" bType="Struct" type="cVal$oscd$_21f679e08734a896"/>
<DA name="q" fc="MX" qchg="true" bType="Quality"/>
<DA name="t" fc="MX" bType="Timestamp"/>
<DA name="units" fc="CF" dchg="true" bType="Struct" type="units$oscd$_18b92a11f3e0b360"/>
</DOType>
<DOType cdc="CMV" id="phsB$oscd$_0b99219f396fac0f">
<DA name="cVal" fc="MX" dchg="true" dupd="true" bType="Struct" type="cVal$oscd$_b2a7664de2216dae"/>
<DA name="q" fc="MX" qchg="true" bType="Quality"/>
<DA name="t" fc="MX" bType="Timestamp"/>
</DOType>
<DOType cdc="CMV" id="phsA$oscd$_16d33d83a36d07b3">
<DA name="cVal" fc="MX" dchg="true" dupd="true" bType="Struct" type="cVal$oscd$_21f679e08734a896"/>
<DA name="q" fc="MX" qchg="true" bType="Quality"/>
<DA name="t" fc="MX" bType="Timestamp"/>
<DA name="units" fc="CF" dchg="true" bType="Struct" type="units$oscd$_18b92a11f3e0b360"/>
</DOType>
<DOType cdc="ENS" id="Beh$oscd$_f1a426046ceedd46">
<DA name="stVal" fc="ST" dchg="true" dupd="true" bType="Enum" type="stVal$oscd$_3bbb381e9c7a72b0"/>
<DA name="q" fc="ST" qchg="true" bType="Quality"/>
<DA name="t" fc="ST" bType="Timestamp"/>
</DOType>
<DAType id="Cancel$oscd$_7f4258185194f976">
<BDA name="ctlVal" bType="Enum" type="stVal$oscd$_48ba16345b8e7f5b"/>
<BDA name="origin" bType="Struct" type="origin$oscd$_7268d0f278193d66"/>
<BDA name="ctlNum" bType="INT8U"/>
<BDA name="T" bType="Timestamp"/>
<BDA name="Test" bType="BOOLEAN"/>
</DAType>
<DAType id="Oper$oscd$_e57fac30c1740d87">
<BDA name="ctlVal" bType="Enum" type="stVal$oscd$_48ba16345b8e7f5b"/>
<BDA name="origin" bType="Struct" type="origin$oscd$_7268d0f278193d66"/>
<BDA name="ctlNum" bType="INT8U"/>
<BDA name="T" bType="Timestamp"/>
<BDA name="Test" bType="BOOLEAN"/>
<BDA name="Check" bType="Check"/>
</DAType>
<DAType id="SBOw$oscd$_e57fac30c1740d87">
<BDA name="ctlVal" bType="Enum" type="stVal$oscd$_48ba16345b8e7f5b"/>
<BDA name="origin" bType="Struct" type="origin$oscd$_7268d0f278193d66"/>
<BDA name="ctlNum" bType="INT8U"/>
<BDA name="T" bType="Timestamp"/>
<BDA name="Test" bType="BOOLEAN"/>
<BDA name="Check" bType="Check"/>
</DAType>
<DAType id="origin$oscd$_7268d0f278193d66">
<BDA name="orCat" bType="Enum" type="orCat$oscd$_929ee017c8f9feb5"/>
<BDA name="orIdent" bType="Octet64"/>
</DAType>
<DAType id="sVC$oscd$_df6488ea078bf55c">
<BDA name="scaleFactor" bType="FLOAT32"/>
<BDA name="offset" bType="FLOAT32"/>
</DAType>
<DAType id="cVal$oscd$_b2a7664de2216dae">
<BDA name="mag" bType="Struct" type="mag$oscd$_5a5af9e249dc7f84"/>
<BDA name="ang" bType="Struct" type="ang$oscd$_5a5af9e249dc7f84"/>
</DAType>
<DAType id="ang$oscd$_5a5af9e249dc7f84">
<BDA name="i" bType="INT32"/>
</DAType>
<DAType id="mag$oscd$_5a5af9e249dc7f84">
<BDA name="i" bType="INT32"/>
</DAType>
<DAType id="units$oscd$_18b92a11f3e0b360">
<BDA name="SIUnit" bType="Enum" type="SIUnit$oscd$_4ee8604931a723d7"/>
</DAType>
<DAType id="cVal$oscd$_21f679e08734a896">
<BDA name="mag" bType="Struct" type="mag$oscd$_ed49c2f7a55ad05a"/>
<BDA name="ang" bType="Struct" type="ang$oscd$_ed49c2f7a55ad05a"/>
</DAType>
<DAType id="ang$oscd$_ed49c2f7a55ad05a">
<BDA name="f" bType="FLOAT32"/>
</DAType>
<DAType id="mag$oscd$_ed49c2f7a55ad05a">
<BDA name="f" bType="FLOAT32"/>
</DAType>
<EnumType id="orCat$oscd$_929ee017c8f9feb5">
<EnumVal ord="4">automatic-bay</EnumVal>
<EnumVal ord="6">automatic-remote</EnumVal>
</EnumType>
<EnumType id="ctlModel$oscd$_d3bc74b2690f022e">
<EnumVal ord="1">direct-with-normal-security</EnumVal>
<EnumVal ord="4">sbo-with-enhanced-security</EnumVal>
</EnumType>
<EnumType id="stVal$oscd$_48ba16345b8e7f5b">
<EnumVal ord="1">on</EnumVal>
<EnumVal ord="2">blocked</EnumVal>
<EnumVal ord="3">test</EnumVal>
<EnumVal ord="4">test/blocked</EnumVal>
<EnumVal ord="5">off</EnumVal>
</EnumType>
<EnumType id="SIUnit$oscd$_4ee8604931a723d7">
<EnumVal ord="5">A</EnumVal>
<EnumVal ord="29">V</EnumVal>
<EnumVal ord="33">Hz</EnumVal>
</EnumType>
<EnumType id="stVal$oscd$_3bbb381e9c7a72b0">
<EnumVal ord="1">on</EnumVal>
<EnumVal ord="5">off</EnumVal>
</EnumType>
</DataTypeTemplates>
</SCL>`;
69 changes: 69 additions & 0 deletions tDataTypeTemplates/removeDataType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Remove } from "../foundation/utils.js";

export type RemoveDataTypeOptions = {
/** Whether a linked data type will be removed */
force: boolean;
};

function orphanRemoves(dtt: Element, orphans: Element[]): Remove[] {
return orphans
.map((orphan) => {
const id = orphan.getAttribute("id");
const btRemovedDataType = dtt.querySelector(`:scope > *[id="${id}"]`)!;

return { node: btRemovedDataType };
})
.filter((remove) => remove) as Remove[];
}

function removeOrphans(orphans: Element[]): void {
orphans.forEach((orphan) => orphan.remove());
}

function isLinked(dataType: Element): boolean {
const dtt = dataType.closest("DataTypeTemplates");

const id = dataType.getAttribute("id");

const linkedData = dtt?.ownerDocument.querySelector(
`:root > DataTypeTemplates *[type="${id}"], :root > IED *[lnType="${id}"]`
);
return !!linkedData;
}

function getOrphans(ddt: Element, saveOrphans: Element[] = []): Element[] {
return Array.from(ddt.querySelectorAll(":scope > *"))
.filter((dataType) => !isLinked(dataType))
.filter((orphan) => !saveOrphans.includes(orphan));
}

/**
* Utility function to remove data types LNodeType, DOType, DAType or EnumType.
* The function makes sure to not leave new unlinked data types .
* @param remove - Remove edit the data type
* @returns Remove array containing all to be removed data types
*/
export function removeDataType(
dtRemove: Remove,
options: RemoveDataTypeOptions = { force: false }
): Remove[] {
const dataType = dtRemove.node as Element;

const dtt = dataType.closest("DataTypeTemplates");
const dttClone = dataType.closest("DataTypeTemplates")?.cloneNode(true);
if (!dttClone) return [];

if (isLinked(dataType) && !options.force) return [];

const saveOrphans = getOrphans(dtt!);

const removes: Remove[] = [];
let orphans = [dataType];
while (orphans.length > 0) {
removes.push(...orphanRemoves(dtt!, orphans));
removeOrphans(orphans);
orphans = getOrphans(dtt!, saveOrphans);
}

return removes;
}

0 comments on commit 453a273

Please sign in to comment.