Skip to content

Commit

Permalink
feat(tSampledValueControl): update confRev on datSet update only
Browse files Browse the repository at this point in the history
  • Loading branch information
JakobVogelsang committed Jul 23, 2024
1 parent 15215e1 commit 6888b99
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 44 deletions.
72 changes: 39 additions & 33 deletions tSampledValueControl/updateSampledValueControl.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function findElement(str: string, selector: string): Element | null {
}

function buildAttr(
type: "name" | "datSet" | "none" = "none",
type: "name" | "datSet" | "datSetOnly" | "none" = "none"
): Record<string, string | null> {
if (type === "name")
return {
Expand All @@ -42,6 +42,11 @@ function buildAttr(
nofASDU: "3",
};

if (type === "datSetOnly")
return {
datSet: "someNewDatSet",
};

return {
desc: "someDesc",
smpMod: "SmpPerSec",
Expand All @@ -61,32 +66,11 @@ describe("Utility function to update SampledValueControl attributes", () => {
expect(actions.length).to.equal(0);
});

it("always updates confRev attribute +10000", () => {
const smvControl = findElement(
smvControlDoc,
'SampledValueControl[name="someSmv"]',
)!;
const actions = updateSampledValueControl({
element: smvControl,
attributes: buildAttr(),
});

expect(actions.length).to.equal(1);
expect(actions[0]).to.satisfy(isUpdate);
expect((actions[0] as Update).element).to.equal(smvControl);
expect((actions[0] as Update).attributes).to.deep.equal({
desc: "someDesc",
confRev: "20001",
smpMod: "SmpPerSec",
securityEnabled: "SignatureAndEncryption",
});
});

describe("with intention to change name attributes", () => {
it("also updates subscribed ExtRefs", () => {
const smvControl = findElement(
smvControlDoc,
'SampledValueControl[name="someSmv3"]',
'SampledValueControl[name="someSmv3"]'
)!;
const actions = updateSampledValueControl({
element: smvControl,
Expand All @@ -101,7 +85,6 @@ describe("Utility function to update SampledValueControl attributes", () => {
desc: "someDesc",
multicast: "true",
smvID: "someSmvID",
confRev: "30001",
smpRate: "90",
nofASDU: "2",
});
Expand All @@ -116,7 +99,7 @@ describe("Utility function to update SampledValueControl attributes", () => {
it("also updates subscriber supervision Val element", () => {
const smvControl = findElement(
smvControlDoc,
'SampledValueControl[name="someSmv"]',
'SampledValueControl[name="someSmv"]'
)!;
const actions = updateSampledValueControl({
element: smvControl,
Expand All @@ -129,7 +112,6 @@ describe("Utility function to update SampledValueControl attributes", () => {
expect((actions[0] as Update).attributes).to.deep.equal({
name: "someNewSmvName",
desc: "someDesc",
confRev: "20001",
multicast: "true",
smpRate: "90",
nofASDU: "2",
Expand All @@ -144,23 +126,23 @@ describe("Utility function to update SampledValueControl attributes", () => {

const oldText = Array.from(
smvControl.ownerDocument.querySelector(
'LN[lnClass="LSVS"][inst="1"] Val',
)?.childNodes ?? [],
'LN[lnClass="LSVS"][inst="1"] Val'
)?.childNodes ?? []
).find((node) => node.nodeType === Node.TEXT_NODE)!;
expect(actions[2]).to.satisfy(isRemove);
expect((actions[2] as Remove).node).to.equal(oldText);

expect(actions[3]).to.satisfy(isInsert);
expect((actions[3] as Insert).parent).to.equal(oldText.parentElement);
expect((actions[3] as Insert).node.textContent).to.equal(
"srcIEDsomeLDInst/LLN0.someNewSmvName",
"srcIEDsomeLDInst/LLN0.someNewSmvName"
);
});

it("also updates SMV cbName attribute", () => {
const smvControl = findElement(
smvControlDoc,
'SampledValueControl[name="someSmv2"]',
'SampledValueControl[name="someSmv2"]'
)!;
const actions = updateSampledValueControl({
element: smvControl,
Expand All @@ -173,7 +155,6 @@ describe("Utility function to update SampledValueControl attributes", () => {
expect((actions[0] as Update).attributes).to.deep.equal({
name: "someNewSmvName",
desc: "someDesc",
confRev: "10001",
multicast: "true",
smpRate: "90",
nofASDU: "2",
Expand All @@ -192,7 +173,7 @@ describe("Utility function to update SampledValueControl attributes", () => {
it("also updates DataSet.name with DataSet being single used", () => {
const smvControl = findElement(
smvControlDoc,
'SampledValueControl[name="someSmv3"]',
'SampledValueControl[name="someSmv3"]'
)!;
const actions = updateSampledValueControl({
element: smvControl,
Expand Down Expand Up @@ -222,7 +203,7 @@ describe("Utility function to update SampledValueControl attributes", () => {
it("also updates DataSet.name with DataSet being single used", () => {
const smvControl = findElement(
smvControlDoc,
'SampledValueControl[name="someSmv2"]',
'SampledValueControl[name="someSmv2"]'
)!;
const actions = updateSampledValueControl({
element: smvControl,
Expand All @@ -241,5 +222,30 @@ describe("Utility function to update SampledValueControl attributes", () => {
smvID: "someSmvID",
});
});

it("also updates SampledValueControl.confRev on datSet change", () => {
const smvControl = findElement(
smvControlDoc,
'SampledValueControl[name="someSmv3"]'
)!;
const actions = updateSampledValueControl({
element: smvControl,
attributes: buildAttr("datSetOnly"),
});

expect(actions.length).to.equal(2);
expect(actions[0]).to.satisfy(isUpdate);
expect((actions[0] as Update).element).to.equal(smvControl);
expect((actions[0] as Update).attributes).to.deep.equal({
datSet: "someNewDatSet",
confRev: "30001",
});

expect(actions[1]).to.satisfy(isUpdate);
expect((actions[1] as Update).element.tagName).to.equal("DataSet");
expect((actions[1] as Update).attributes).to.deep.equal({
name: "someNewDatSet",
});
});
});
});
24 changes: 13 additions & 11 deletions tSampledValueControl/updateSampledValueControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,42 +9,42 @@ import { updateDatSet } from "../tControl/updateDatSet.js";
/**
* Utility function to update SampledValueControl element attributes.
* ```md
* These attributes trigger addition edits
* These attributes trigger additional edits such as
* - name: also updates SMV.cbName and supervision references
* - datSet: update reference DataSet.name - when DataSet is single use
*
* >NOTE: confRev attribute is updated +10000 every time this function is called
* >NOTE: confRev attribute is updated +10000 on each data set change
* ```
* @param update - diff holding the `SampledValueControl` as element
* @returns action array to update all `SampledValueControl` attributes
*/
export function updateSampledValueControl(
update: Update,
update: Update
): (Update | Remove | Insert)[] {
if (update.element.tagName !== "SampledValueControl") return [];

const updates: (Update | Remove | Insert)[] = [];
if (update.attributes.name) {
const extRefUpdates: Update[] = findControlBlockSubscription(
update.element,
update.element
).map((extRef) => ({
element: extRef,
attributes: { srcCBName: update.attributes.name },
}));

const supervisionUpdates: (Remove | Insert)[] = Array.from(
update.element.ownerDocument.querySelectorAll(
':root > IED > AccessPoint > Server > LDevice > LN[lnClass="LSVS"] > DOI[name="SvCBRef"] > DAI[name="setSrcRef"] > Val',
),
':root > IED > AccessPoint > Server > LDevice > LN[lnClass="LSVS"] > DOI[name="SvCBRef"] > DAI[name="setSrcRef"] > Val'
)
)
.filter((val) => val.textContent === controlBlockObjRef(update.element))
.flatMap((val) => {
const [path] = controlBlockObjRef(update.element)!.split(".");
const oldValContent = Array.from(val.childNodes).find(
(node) => node.nodeType === Node.TEXT_NODE,
(node) => node.nodeType === Node.TEXT_NODE
)!;
const newValContent = update.element.ownerDocument.createTextNode(
`${path}.${update.attributes.name}`,
`${path}.${update.attributes.name}`
) as Text;

return [
Expand All @@ -69,10 +69,12 @@ export function updateSampledValueControl(
const updateDataSet = updateDatSet(update);

if (updateDataSet) updates.push(updateDataSet);
else delete update.attributes.datSet; // remove datSet from the update to avoid schema invalidity
}
// remove datSet from the update to avoid schema invalidity
else delete update.attributes.datSet;

update.attributes.confRev = updatedConfRev(update.element); // +10000 for update
// +10000 for update
update.attributes.confRev = updatedConfRev(update.element);
}

return [update, ...updates];
}

0 comments on commit 6888b99

Please sign in to comment.