Skip to content

Commit 453a273

Browse files
feat(tDataTypeTemplates): remove LNodeType, DOType, DAType or EnumType
1 parent 2999a86 commit 453a273

File tree

3 files changed

+253
-0
lines changed

3 files changed

+253
-0
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { expect } from "chai";
2+
import { findElement } from "../foundation/helpers.test.js";
3+
4+
import { invalidScl, mmxu } from "./removeDataType.testfiles.js";
5+
import { removeDataType } from "./removeDataType.js";
6+
7+
const invalidLNodeType = findElement(invalidScl, "LNodeType") as XMLDocument;
8+
const doType = findElement(mmxu, "DOType") as XMLDocument;
9+
const lNodeType = findElement(mmxu, "LNodeType") as XMLDocument;
10+
11+
describe("Function to recursively remove data types ", () => {
12+
it("returns empty array with SCL files", () => {
13+
expect(removeDataType({ node: invalidLNodeType }).length).equals(0);
14+
});
15+
16+
it("does not remove linked data types per default", () => {
17+
const removes = removeDataType({ node: doType });
18+
expect(removes.length).equals(0);
19+
});
20+
21+
it("removes linked data types with force option set", () => {
22+
const removes = removeDataType({ node: doType }, { force: true });
23+
24+
expect(removes.length).equals(6);
25+
expect(removes[0].node).equal(doType);
26+
});
27+
28+
it("does not remove linked data types with force option set", () => {
29+
const removes = removeDataType({ node: lNodeType });
30+
31+
expect(removes.length).equals(15);
32+
expect(removes[0].node).equal(lNodeType);
33+
});
34+
});
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
export const invalidScl = `<?xml version="1.0" encoding="UTF-8"?>
2+
<SCL xmlns="http://www.iec.ch/61850/2003/SCL" version="2007" revision="B" release="4">
3+
<LNodeType id="someFakeIds" >
4+
<DO name="Beh" type="someType"
5+
</LNodeType>
6+
</SCL>`;
7+
8+
export const mmxu = `<?xml version="1.0" encoding="UTF-8"?>
9+
<SCL xmlns="http://www.iec.ch/61850/2003/SCL" version="2007" revision="B" release="4">
10+
<Header id="project"/>
11+
<DataTypeTemplates>
12+
<LNodeType lnClass="MMXU" id="MMXU$oscd$_c83c0b5963558681">
13+
<DO name="Beh" type="Beh$oscd$_f1a426046ceedd46"/>
14+
<DO name="A" type="A$oscd$_67cc94e14f712b32"/>
15+
<DO name="Hz" type="Hz$oscd$_979742e3ddefd568"/>
16+
<DO name="Mod" type="Mod$oscd$_f595ae60fd1bdba5"/>
17+
</LNodeType>
18+
<LNodeType lnClass="PTOC" id="PTOC$oscd$_c83c0b59635asdd">
19+
<DO name="Beh" type="Beh$oscd$_f1a426046ceedd46"/>
20+
<DO name="Mod" type="Mod$oscd$_f595ae60fd1bdba5"/>
21+
</LNodeType>
22+
<DOType cdc="ENC" id="Mod$oscd$_f595ae60fd1bdba5">
23+
<DA name="stVal" fc="ST" dchg="true" bType="Enum" type="stVal$oscd$_48ba16345b8e7f5b"/>
24+
<DA name="q" fc="ST" qchg="true" bType="Quality"/>
25+
<DA name="t" fc="ST" bType="Timestamp"/>
26+
<DA name="ctlModel" fc="CF" dchg="true" bType="Enum" type="ctlModel$oscd$_d3bc74b2690f022e"/>
27+
<DA name="SBOw" fc="CO" bType="Struct" type="SBOw$oscd$_e57fac30c1740d87"/>
28+
<DA name="Oper" fc="CO" bType="Struct" type="Oper$oscd$_e57fac30c1740d87"/>
29+
<DA name="Cancel" fc="CO" bType="Struct" type="Cancel$oscd$_7f4258185194f976"/>
30+
</DOType>
31+
<DOType cdc="ENC" id="Mod$oscd$_f595ae60fd1bdasdf">
32+
<DA name="stVal" fc="ST" dchg="true" bType="Enum" type="stVal$oscd$_48ba16345b8e7f5b"/>
33+
<DA name="q" fc="ST" qchg="true" bType="Quality"/>
34+
<DA name="t" fc="ST" bType="Timestamp"/>
35+
<DA name="ctlModel" fc="CF" dchg="true" bType="Enum" type="ctlModel$oscd$_d3bc74b2690f022e"/>
36+
</DOType>
37+
<DOType cdc="MV" id="Hz$oscd$_979742e3ddefd568">
38+
<DA name="mag" fc="MX" dchg="true" dupd="true" bType="Struct" type="mag$oscd$_ed49c2f7a55ad05a"/>
39+
<DA name="q" fc="MX" qchg="true" bType="Quality"/>
40+
<DA name="t" fc="MX" bType="Timestamp"/>
41+
<DA name="units" fc="CF" dchg="true" bType="Struct" type="units$oscd$_18b92a11f3e0b360"/>
42+
<DA name="sVC" fc="CF" dchg="true" bType="Struct" type="sVC$oscd$_df6488ea078bf55c"/>
43+
</DOType>
44+
<DOType cdc="WYE" id="A$oscd$_67cc94e14f712b32">
45+
<SDO name="phsC" type="phsC$oscd$_16d33d83a36d07b3"/>
46+
<SDO name="phsB" type="phsB$oscd$_0b99219f396fac0f"/>
47+
<SDO name="phsA" type="phsA$oscd$_16d33d83a36d07b3"/>
48+
</DOType>
49+
<DOType cdc="CMV" id="phsC$oscd$_16d33d83a36d07b3">
50+
<DA name="cVal" fc="MX" dchg="true" dupd="true" bType="Struct" type="cVal$oscd$_21f679e08734a896"/>
51+
<DA name="q" fc="MX" qchg="true" bType="Quality"/>
52+
<DA name="t" fc="MX" bType="Timestamp"/>
53+
<DA name="units" fc="CF" dchg="true" bType="Struct" type="units$oscd$_18b92a11f3e0b360"/>
54+
</DOType>
55+
<DOType cdc="CMV" id="phsB$oscd$_0b99219f396fac0f">
56+
<DA name="cVal" fc="MX" dchg="true" dupd="true" bType="Struct" type="cVal$oscd$_b2a7664de2216dae"/>
57+
<DA name="q" fc="MX" qchg="true" bType="Quality"/>
58+
<DA name="t" fc="MX" bType="Timestamp"/>
59+
</DOType>
60+
<DOType cdc="CMV" id="phsA$oscd$_16d33d83a36d07b3">
61+
<DA name="cVal" fc="MX" dchg="true" dupd="true" bType="Struct" type="cVal$oscd$_21f679e08734a896"/>
62+
<DA name="q" fc="MX" qchg="true" bType="Quality"/>
63+
<DA name="t" fc="MX" bType="Timestamp"/>
64+
<DA name="units" fc="CF" dchg="true" bType="Struct" type="units$oscd$_18b92a11f3e0b360"/>
65+
</DOType>
66+
<DOType cdc="ENS" id="Beh$oscd$_f1a426046ceedd46">
67+
<DA name="stVal" fc="ST" dchg="true" dupd="true" bType="Enum" type="stVal$oscd$_3bbb381e9c7a72b0"/>
68+
<DA name="q" fc="ST" qchg="true" bType="Quality"/>
69+
<DA name="t" fc="ST" bType="Timestamp"/>
70+
</DOType>
71+
<DAType id="Cancel$oscd$_7f4258185194f976">
72+
<BDA name="ctlVal" bType="Enum" type="stVal$oscd$_48ba16345b8e7f5b"/>
73+
<BDA name="origin" bType="Struct" type="origin$oscd$_7268d0f278193d66"/>
74+
<BDA name="ctlNum" bType="INT8U"/>
75+
<BDA name="T" bType="Timestamp"/>
76+
<BDA name="Test" bType="BOOLEAN"/>
77+
</DAType>
78+
<DAType id="Oper$oscd$_e57fac30c1740d87">
79+
<BDA name="ctlVal" bType="Enum" type="stVal$oscd$_48ba16345b8e7f5b"/>
80+
<BDA name="origin" bType="Struct" type="origin$oscd$_7268d0f278193d66"/>
81+
<BDA name="ctlNum" bType="INT8U"/>
82+
<BDA name="T" bType="Timestamp"/>
83+
<BDA name="Test" bType="BOOLEAN"/>
84+
<BDA name="Check" bType="Check"/>
85+
</DAType>
86+
<DAType id="SBOw$oscd$_e57fac30c1740d87">
87+
<BDA name="ctlVal" bType="Enum" type="stVal$oscd$_48ba16345b8e7f5b"/>
88+
<BDA name="origin" bType="Struct" type="origin$oscd$_7268d0f278193d66"/>
89+
<BDA name="ctlNum" bType="INT8U"/>
90+
<BDA name="T" bType="Timestamp"/>
91+
<BDA name="Test" bType="BOOLEAN"/>
92+
<BDA name="Check" bType="Check"/>
93+
</DAType>
94+
<DAType id="origin$oscd$_7268d0f278193d66">
95+
<BDA name="orCat" bType="Enum" type="orCat$oscd$_929ee017c8f9feb5"/>
96+
<BDA name="orIdent" bType="Octet64"/>
97+
</DAType>
98+
<DAType id="sVC$oscd$_df6488ea078bf55c">
99+
<BDA name="scaleFactor" bType="FLOAT32"/>
100+
<BDA name="offset" bType="FLOAT32"/>
101+
</DAType>
102+
<DAType id="cVal$oscd$_b2a7664de2216dae">
103+
<BDA name="mag" bType="Struct" type="mag$oscd$_5a5af9e249dc7f84"/>
104+
<BDA name="ang" bType="Struct" type="ang$oscd$_5a5af9e249dc7f84"/>
105+
</DAType>
106+
<DAType id="ang$oscd$_5a5af9e249dc7f84">
107+
<BDA name="i" bType="INT32"/>
108+
</DAType>
109+
<DAType id="mag$oscd$_5a5af9e249dc7f84">
110+
<BDA name="i" bType="INT32"/>
111+
</DAType>
112+
<DAType id="units$oscd$_18b92a11f3e0b360">
113+
<BDA name="SIUnit" bType="Enum" type="SIUnit$oscd$_4ee8604931a723d7"/>
114+
</DAType>
115+
<DAType id="cVal$oscd$_21f679e08734a896">
116+
<BDA name="mag" bType="Struct" type="mag$oscd$_ed49c2f7a55ad05a"/>
117+
<BDA name="ang" bType="Struct" type="ang$oscd$_ed49c2f7a55ad05a"/>
118+
</DAType>
119+
<DAType id="ang$oscd$_ed49c2f7a55ad05a">
120+
<BDA name="f" bType="FLOAT32"/>
121+
</DAType>
122+
<DAType id="mag$oscd$_ed49c2f7a55ad05a">
123+
<BDA name="f" bType="FLOAT32"/>
124+
</DAType>
125+
<EnumType id="orCat$oscd$_929ee017c8f9feb5">
126+
<EnumVal ord="4">automatic-bay</EnumVal>
127+
<EnumVal ord="6">automatic-remote</EnumVal>
128+
</EnumType>
129+
<EnumType id="ctlModel$oscd$_d3bc74b2690f022e">
130+
<EnumVal ord="1">direct-with-normal-security</EnumVal>
131+
<EnumVal ord="4">sbo-with-enhanced-security</EnumVal>
132+
</EnumType>
133+
<EnumType id="stVal$oscd$_48ba16345b8e7f5b">
134+
<EnumVal ord="1">on</EnumVal>
135+
<EnumVal ord="2">blocked</EnumVal>
136+
<EnumVal ord="3">test</EnumVal>
137+
<EnumVal ord="4">test/blocked</EnumVal>
138+
<EnumVal ord="5">off</EnumVal>
139+
</EnumType>
140+
<EnumType id="SIUnit$oscd$_4ee8604931a723d7">
141+
<EnumVal ord="5">A</EnumVal>
142+
<EnumVal ord="29">V</EnumVal>
143+
<EnumVal ord="33">Hz</EnumVal>
144+
</EnumType>
145+
<EnumType id="stVal$oscd$_3bbb381e9c7a72b0">
146+
<EnumVal ord="1">on</EnumVal>
147+
<EnumVal ord="5">off</EnumVal>
148+
</EnumType>
149+
</DataTypeTemplates>
150+
</SCL>`;
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { Remove } from "../foundation/utils.js";
2+
3+
export type RemoveDataTypeOptions = {
4+
/** Whether a linked data type will be removed */
5+
force: boolean;
6+
};
7+
8+
function orphanRemoves(dtt: Element, orphans: Element[]): Remove[] {
9+
return orphans
10+
.map((orphan) => {
11+
const id = orphan.getAttribute("id");
12+
const btRemovedDataType = dtt.querySelector(`:scope > *[id="${id}"]`)!;
13+
14+
return { node: btRemovedDataType };
15+
})
16+
.filter((remove) => remove) as Remove[];
17+
}
18+
19+
function removeOrphans(orphans: Element[]): void {
20+
orphans.forEach((orphan) => orphan.remove());
21+
}
22+
23+
function isLinked(dataType: Element): boolean {
24+
const dtt = dataType.closest("DataTypeTemplates");
25+
26+
const id = dataType.getAttribute("id");
27+
28+
const linkedData = dtt?.ownerDocument.querySelector(
29+
`:root > DataTypeTemplates *[type="${id}"], :root > IED *[lnType="${id}"]`
30+
);
31+
return !!linkedData;
32+
}
33+
34+
function getOrphans(ddt: Element, saveOrphans: Element[] = []): Element[] {
35+
return Array.from(ddt.querySelectorAll(":scope > *"))
36+
.filter((dataType) => !isLinked(dataType))
37+
.filter((orphan) => !saveOrphans.includes(orphan));
38+
}
39+
40+
/**
41+
* Utility function to remove data types LNodeType, DOType, DAType or EnumType.
42+
* The function makes sure to not leave new unlinked data types .
43+
* @param remove - Remove edit the data type
44+
* @returns Remove array containing all to be removed data types
45+
*/
46+
export function removeDataType(
47+
dtRemove: Remove,
48+
options: RemoveDataTypeOptions = { force: false }
49+
): Remove[] {
50+
const dataType = dtRemove.node as Element;
51+
52+
const dtt = dataType.closest("DataTypeTemplates");
53+
const dttClone = dataType.closest("DataTypeTemplates")?.cloneNode(true);
54+
if (!dttClone) return [];
55+
56+
if (isLinked(dataType) && !options.force) return [];
57+
58+
const saveOrphans = getOrphans(dtt!);
59+
60+
const removes: Remove[] = [];
61+
let orphans = [dataType];
62+
while (orphans.length > 0) {
63+
removes.push(...orphanRemoves(dtt!, orphans));
64+
removeOrphans(orphans);
65+
orphans = getOrphans(dtt!, saveOrphans);
66+
}
67+
68+
return removes;
69+
}

0 commit comments

Comments
 (0)