Skip to content
Merged
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
18 changes: 13 additions & 5 deletions tIED/removeIED.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ describe("Function to an remove the IED and its referenced elements", () => {
expect(numberRemoves(edits, "LNode")).to.equal(1);
});

it("returns empty LNode edits for an IED without substation LNodes with preserveLNodes option", () => {
const edits = removeIED({ node: client }, { preserveLNodes: true });
expect(numberRemoves(edits, "LNode")).to.equal(0);
expect(numberUpdates(edits, "LNode")).to.equal(0);
});

it("removes ConnectedAPs as well", () => {
const edits = removeIED({ node: publisher });

Expand Down Expand Up @@ -149,7 +155,7 @@ describe("Function to an remove the IED and its referenced elements", () => {
const edits = removeIED({ node: iedA });
handleEdit(edits);
const after_iedA_lNodes = Array.from(
sclDom.querySelectorAll(`${scope} LNode[iedName="IED_A"]`),
sclDom.querySelectorAll(`${scope} > LNode[iedName="IED_A"]`),
).length;
const after_spec_LNodeCount = (
sclDom.querySelectorAll(`${scope} > LNode[iedName='None']`) ?? []
Expand All @@ -158,18 +164,20 @@ describe("Function to an remove the IED and its referenced elements", () => {
// The number of LNodes set to None should not have changed.
expect(after_spec_LNodeCount).to.equal(beforeSpec_LNodeCount);

const privateLNodeCount = sclDom.querySelectorAll(`${scope} > Private > LNode[iedName='IED_A']`).length;
expect(privateLNodeCount).to.equal(1, 'Private LNode count is wrong');
const privateLNodeCount = sclDom.querySelectorAll(
`${scope} > Private > LNode[iedName='IED_A']`,
).length;
expect(privateLNodeCount).to.equal(1, "Private LNode count is wrong");
});
});
});

describe.only("with preserveLNodes set", () => {
describe("with preserveLNodes set", () => {
// Broke this into 3 separate tests, so the scope of the failure "might" be narrower.
// Do keep in mind however, the subject SCL has 2 of everything. E.g. S1 & S2
["Bay", "VoltageLevel", "Substation"].forEach((scope) => {
it(`Within a ${scope}, it sets all bound LNodes to None`, () => {
//We're using the "duplicates" test file, but by only deleting 1 IED, no duplicates occur (yet).
// We're using the "duplicates" test file, but by only deleting 1 IED, no duplicates occur (yet).
const sclDom = new DOMParser().parseFromString(
sclDuplicateLNodes,
"application/xml",
Expand Down
6 changes: 6 additions & 0 deletions tIED/removeIED.testfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,9 @@ export const scl = `<SCL xmlns="http://www.iec.ch/61850/2003/SCL" xmlns:esld="ht
export const sclDuplicateLNodes = `<SCL xmlns="http://www.iec.ch/61850/2003/SCL" version="2007" revision="B" release="4">
<Header id="DuplicateLNodes"/>
<Substation name="S1">
<Private type="eieio">
<LNode iedName="IED_A" ldInst="CBSW" lnClass="XCBR" lnInst="1" prefix=""/>
</Private>
<LNode iedName="IED_A" ldInst="CBSW" lnClass="XCBR" lnInst="1" prefix=""/>
<LNode iedName="IED_B" ldInst="CBSW" lnClass="XCBR" lnInst="1" prefix=""/>
<VoltageLevel name="V1">
Expand All @@ -591,6 +594,9 @@ export const sclDuplicateLNodes = `<SCL xmlns="http://www.iec.ch/61850/2003/SCL"
<LNode iedName="IED_A" ldInst="CBSW" lnClass="XCBR" lnInst="1" prefix=""/>
<LNode iedName="IED_B" ldInst="CBSW" lnClass="XCBR" lnInst="1" prefix=""/>
<Bay name="B1">
<Private type="eieio">
<LNode iedName="IED_A" ldInst="CBSW" lnClass="XCBR" lnInst="1" prefix=""/>
</Private>
<LNode iedName="IED_A" ldInst="CBSW" lnClass="XCBR" lnInst="1" prefix=""/>
<LNode iedName="IED_B" ldInst="CBSW" lnClass="XCBR" lnInst="1" prefix=""/>
<ConductingEquipment name="QA1" type="CBR">
Expand Down
27 changes: 9 additions & 18 deletions tIED/removeIED.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,8 @@ function removeIedSubscriptionsAndSupervisions(
const lNodeKey = (ln: Element): string =>
["lnClass", "lnInst", "ldInst", "prefix"].map((a) => ln.getAttribute(a) ?? "").join("|");

const getLNodeScopeElement = (ln: Element): Element | null => {
if (ln.closest("Private") === null) {
return ln.closest("Bay, VoltageLevel, Substation");
}
return null;
const getLNodeScopeElement = (ln: Element): Element => {
return ln.closest("Bay, VoltageLevel, Substation")!;
};

const createRemoveEdit = (ln: Element): Remove => {
Expand All @@ -68,15 +65,15 @@ const setLNodeToNone = (ln: Element): SetAttributes => {

const getLNodesByIedName = (doc: XMLDocument, name: string): Element[] => {
return Array.from(
doc.querySelectorAll(`Substation LNode[iedName=${name}]`) ?? [],
doc.querySelectorAll(`Substation LNode[iedName=${name}]`),
).filter(isPublic);
};

/**
* Default handling for LNodes - find any (public) matching LNodes and create a Remove edit for them.
*/
function removeBoundLNodes(ied: Element, name: string): Remove[] {
return (getLNodesByIedName(ied.ownerDocument, name) ?? []).map(createRemoveEdit);
return (getLNodesByIedName(ied.ownerDocument, name)).map(createRemoveEdit);
}

/**
Expand All @@ -101,23 +98,17 @@ function detachLNodeBindings(
const unboundLNodesByScope = new Map<Element, Set<string>>();
getLNodesByIedName(doc, "None").forEach((ln) => {
const scope = getLNodeScopeElement(ln);
if (scope !== null) {
let keys = unboundLNodesByScope.get(scope);
if (!keys) {
keys = new Set<string>();
unboundLNodesByScope.set(scope, keys);
}
keys.add(lNodeKey(ln));
let keys = unboundLNodesByScope.get(scope);
if (!keys) {
keys = new Set<string>();
unboundLNodesByScope.set(scope, keys);
}
keys.add(lNodeKey(ln));
});

return boundNodes
.map((ln) => {
const scope = getLNodeScopeElement(ln);
if (!scope) {
return;
}

const keys = unboundLNodesByScope.get(scope);

const key = lNodeKey(ln);
Expand Down
Loading