Skip to content
Open
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
11 changes: 5 additions & 6 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
export {
TreeSelection,
} from "./foundation/utils.js";
export { TreeSelection } from "./foundation/utils.js";

export { updateBay } from "./tBay/updateBay.js";
export { updateVoltageLevel } from "./tVoltageLevel/updateVoltageLevel.js";
Expand Down Expand Up @@ -69,8 +67,6 @@ export {
export { sourceControlBlock } from "./tExtRef/sourceControlBlock.js";
export { isSubscribed } from "./tExtRef/isSubscribed.js";



export {
LNodeDescription,
NameSpaceDescription,
Expand All @@ -79,7 +75,10 @@ export {

export { insertSelectedLNodeType } from "./tDataTypeTemplates/insertSelectedLNodeType.js";

export { removeDataType, RemoveDataTypeOptions } from "./tDataTypeTemplates/removeDataType.js"
export {
removeDataType,
RemoveDataTypeOptions,
} from "./tDataTypeTemplates/removeDataType.js";
export { importLNodeType } from "./tDataTypeTemplates/importLNodeType.js";
export { updateLNodeType } from "./tDataTypeTemplates/updateLNodeType.js";
export { lNodeTypeToSelection } from "./tDataTypeTemplates/lNodeTypeToSelection.js";
Expand Down
2 changes: 1 addition & 1 deletion tAddress/changeGseOrSmvAddress.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Insert, Remove } from "@openscd/oscd-api"
import { Insert, Remove } from "@openscd/oscd-api";

import { createElement } from "../foundation/utils.js";
import { getReference } from "../tBaseElement/getReference.js";
Expand Down
6 changes: 4 additions & 2 deletions tBay/updateBay.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,17 @@ describe("update Bay element", () => {
.filter(isSetAttributes)
.filter(
({ attributes }) =>
attributes?.connectivityNode === "AA1/E1/Q03/L1" && attributes?.bayName === "Q03",
attributes?.connectivityNode === "AA1/E1/Q03/L1" &&
attributes?.bayName === "Q03",
),
).to.have.lengthOf(2);
expect(
edits
.filter(isSetAttributes)
.find(
({ attributes }) =>
attributes?.connectivityNode === "AA1/E1/Q03/L2" && attributes?.bayName === "Q03",
attributes?.connectivityNode === "AA1/E1/Q03/L2" &&
attributes?.bayName === "Q03",
),
).to.exist;
});
Expand Down
4 changes: 3 additions & 1 deletion tControl/removeControlBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ export function removeControlBlock(remove: Remove): (Remove | SetAttributes)[] {

const controlBlock = remove.node as Element;

const ctrlBlockRemoveAction: (Remove | SetAttributes)[] = [{ node: controlBlock }];
const ctrlBlockRemoveAction: (Remove | SetAttributes)[] = [
{ node: controlBlock },
];

const dataSet = controlBlock.parentElement?.querySelector(
`DataSet[name="${controlBlock.getAttribute("datSet")}"]`,
Expand Down
6 changes: 4 additions & 2 deletions tControl/updateDatSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { SetAttributes } from "@openscd/oscd-api";

const controlBlockTags = ["ReportControl", "GSEControl", "SampledValueControl"];

/**
/**
* On update the `datSet` attribute of a given control block this function will change
* `DataSet.name` attribute as well if the `DataSet` is only used by this control block.
*/
export function updateDatSet(setAttributes: SetAttributes): SetAttributes | null {
export function updateDatSet(
setAttributes: SetAttributes,
): SetAttributes | null {
const newDatSet = setAttributes?.attributes?.datSet;
const controlBlock = setAttributes.element;
const oldDatSet = controlBlock.getAttribute("datSet");
Expand Down
12 changes: 6 additions & 6 deletions tDataSet/removeDataSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ export function removeDataSet(remove: Remove): (Remove | SetAttributes)[] {
const extRefEdits: (Remove | SetAttributes)[] = [];
extRefEdits.push(...unsubscribe(extRefs));

const ctrlBlockUpdates: (Remove | SetAttributes)[] = controlBlocks(dataSet).map(
(ctrlBlock) => ({
element: ctrlBlock,
attributes: { datSet: null, confRev: updatedConfRev(ctrlBlock) },
}),
);
const ctrlBlockUpdates: (Remove | SetAttributes)[] = controlBlocks(
dataSet,
).map((ctrlBlock) => ({
element: ctrlBlock,
attributes: { datSet: null, confRev: updatedConfRev(ctrlBlock) },
}));

return dataSetRemove.concat(extRefEdits, ctrlBlockUpdates);
}
225 changes: 113 additions & 112 deletions tDataTypeTemplates/foundation.ts
Original file line number Diff line number Diff line change
@@ -1,142 +1,143 @@


function describeEnumType(element: Element): { vals: Record<string, string> } {
const vals: Record<string, string> = {};
const vals: Record<string, string> = {};

const sortedEnumVals = Array.from(element.children)
.filter((child) => child.tagName === "EnumVal")
.sort(
(v1, v2) =>
parseInt(v1.getAttribute("ord")!, 10) -
parseInt(v2.getAttribute("ord")!, 10),
);
for (const val of sortedEnumVals)
vals[val.getAttribute("ord")!] = val.textContent ?? "";
const sortedEnumVals = Array.from(element.children)
.filter((child) => child.tagName === "EnumVal")
.sort(
(v1, v2) =>
parseInt(v1.getAttribute("ord")!, 10) -
parseInt(v2.getAttribute("ord")!, 10),
);
for (const val of sortedEnumVals)
vals[val.getAttribute("ord")!] = val.textContent ?? "";

return { vals };
return { vals };
}

function describeDAType(element: Element): {
bdas: Record<string, Record<string, string | null>>;
bdas: Record<string, Record<string, string | null>>;
} {
const bdas: Record<string, Record<string, string | null>> = {};
for (const bda of Array.from(element.children)
.filter((child) => child.tagName === "BDA")
.sort((c1, c2) => c1.outerHTML.localeCompare(c2.outerHTML))) {
const [bType, type, dchg, dupd, qchg] = [
"bType",
"type",
"dchg",
"dupd",
"qchg",
].map((attr) => bda.getAttribute(attr));
bdas[bda.getAttribute("name")!] = { bType, type, dchg, dupd, qchg };
}
return { bdas };
const bdas: Record<string, Record<string, string | null>> = {};
for (const bda of Array.from(element.children)
.filter((child) => child.tagName === "BDA")
.sort((c1, c2) => c1.outerHTML.localeCompare(c2.outerHTML))) {
const [bType, type, dchg, dupd, qchg] = [
"bType",
"type",
"dchg",
"dupd",
"qchg",
].map((attr) => bda.getAttribute(attr));
bdas[bda.getAttribute("name")!] = { bType, type, dchg, dupd, qchg };
}
return { bdas };
}

function describeDOType(element: Element) {
const sdos: Record<string, Record<string, string | null>> = {};
for (const sdo of Array.from(element.children)
.filter((child) => child.tagName === "SDO")
.sort((c1, c2) => c1.outerHTML.localeCompare(c2.outerHTML))) {
const [name, type, transient] = ["name", "type", "transient"].map((attr) =>
sdo.getAttribute(attr),
);
sdos[name!] = { type, transient };
}
const das: Record<string, Record<string, string | null>> = {};
for (const da of Array.from(element.children)
.filter((child) => child.tagName === "DA")
.sort((c1, c2) => c1.outerHTML.localeCompare(c2.outerHTML))) {
const [name, fc, bType, type, dchg, dupd, qchg] = [
"name",
"fc",
"bType",
"type",
"dchg",
"dupd",
"qchg",
].map((attr) => da.getAttribute(attr));
das[name!] = {
fc,
bType,
type,
dchg,
dupd,
qchg,
};
}
return {
sdos,
das,
cdc: element.getAttribute("cdc"),
const sdos: Record<string, Record<string, string | null>> = {};
for (const sdo of Array.from(element.children)
.filter((child) => child.tagName === "SDO")
.sort((c1, c2) => c1.outerHTML.localeCompare(c2.outerHTML))) {
const [name, type, transient] = ["name", "type", "transient"].map((attr) =>
sdo.getAttribute(attr),
);
sdos[name!] = { type, transient };
}
const das: Record<string, Record<string, string | null>> = {};
for (const da of Array.from(element.children)
.filter((child) => child.tagName === "DA")
.sort((c1, c2) => c1.outerHTML.localeCompare(c2.outerHTML))) {
const [name, fc, bType, type, dchg, dupd, qchg] = [
"name",
"fc",
"bType",
"type",
"dchg",
"dupd",
"qchg",
].map((attr) => da.getAttribute(attr));
das[name!] = {
fc,
bType,
type,
dchg,
dupd,
qchg,
};
}
return {
sdos,
das,
cdc: element.getAttribute("cdc"),
};
}

function describeLNodeType(element: Element) {
const dos: Record<string, Record<string, string | null>> = {};
for (const doElement of Array.from(element.children)
.filter((child) => child.tagName === "DO")
.sort((c1, c2) => c1.outerHTML.localeCompare(c2.outerHTML))) {
const [name, type, transient] = ["name", "type", "transient"].map((attr) =>
doElement.getAttribute(attr),
);
dos[name!] = { type, transient };
}
return {
dos,
lnClass: element.getAttribute("lnClass"),
};
const dos: Record<string, Record<string, string | null>> = {};
for (const doElement of Array.from(element.children)
.filter((child) => child.tagName === "DO")
.sort((c1, c2) => c1.outerHTML.localeCompare(c2.outerHTML))) {
const [name, type, transient] = ["name", "type", "transient"].map((attr) =>
doElement.getAttribute(attr),
);
dos[name!] = { type, transient };
}
return {
dos,
lnClass: element.getAttribute("lnClass"),
};
}

const typeDescriptions = {
EnumType: describeEnumType,
DAType: describeDAType,
DOType: describeDOType,
LNodeType: describeLNodeType,
EnumType: describeEnumType,
DAType: describeDAType,
DOType: describeDOType,
LNodeType: describeLNodeType,
} as Partial<Record<string, (e: Element) => object>>;

function describeElement(element: Element): object {
const describe = typeDescriptions[element.tagName]!;
const describe = typeDescriptions[element.tagName]!;

return describe(element);
return describe(element);
}

export function hashElement(element: Element): string {
/** A direct copy from www.github.com/openscd/open-scd-core/foundation/cyrb64.ts */
/** A direct copy from www.github.com/openscd/open-scd-core/foundation/cyrb64.ts */

/**
* Hashes `str` using the cyrb64 variant of
* https://github.com/bryc/code/blob/master/jshash/experimental/cyrb53.js
* @returns digest - a rather insecure hash, very quickly
*/
function cyrb64(str: string): string {
/* eslint-disable no-bitwise */
let h1 = 0xdeadbeef;
let h2 = 0x41c6ce57;
/* eslint-disable-next-line no-plusplus */
for (let i = 0, ch; i < str.length; i++) {
ch = str.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
}
h1 =
Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^
Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 =
Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^
Math.imul(h1 ^ (h1 >>> 13), 3266489909);
return (
(h2 >>> 0).toString(16).padStart(8, "0") +
(h1 >>> 0).toString(16).padStart(8, "0")
);
/* eslint-enable no-bitwise */
/**
* Hashes `str` using the cyrb64 variant of
* https://github.com/bryc/code/blob/master/jshash/experimental/cyrb53.js
* @returns digest - a rather insecure hash, very quickly
*/
function cyrb64(str: string): string {
/* eslint-disable no-bitwise */
let h1 = 0xdeadbeef;
let h2 = 0x41c6ce57;
/* eslint-disable-next-line no-plusplus */
for (let i = 0, ch; i < str.length; i++) {
ch = str.charCodeAt(i);
h1 = Math.imul(h1 ^ ch, 2654435761);
h2 = Math.imul(h2 ^ ch, 1597334677);
}
h1 =
Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^
Math.imul(h2 ^ (h2 >>> 13), 3266489909);
h2 =
Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^
Math.imul(h1 ^ (h1 >>> 13), 3266489909);
return (
(h2 >>> 0).toString(16).padStart(8, "0") +
(h1 >>> 0).toString(16).padStart(8, "0")
);
/* eslint-enable no-bitwise */
}

return cyrb64(JSON.stringify(describeElement(element)));
return cyrb64(JSON.stringify(describeElement(element)));
}

export function isEqualNode(ours: Element, theirs: Element): boolean {
return JSON.stringify(describeElement(ours)) === JSON.stringify(describeElement(theirs));
}
return (
JSON.stringify(describeElement(ours)) ===
JSON.stringify(describeElement(theirs))
);
}
14 changes: 9 additions & 5 deletions tDataTypeTemplates/importLNodeType.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ const completeTemplate = findElement(competeBayTemplate) as XMLDocument;
const invalidTemplate = findElement(invalidBayTemplate) as XMLDocument;
const tctrLNodeType = findElement(
baseDataTypes,
'LNodeType[id="Dummy.TCTR"]'
'LNodeType[id="Dummy.TCTR"]',
) as Element;
const mmxuLNodeType = findElement(
baseDataTypes,
'LNodeType[id="Dummy.MMXU"]'
'LNodeType[id="Dummy.MMXU"]',
) as Element;
const tctrHardUpdate = findElement(
hardUpdate,
'LNodeType[id="Dummy.TCTR"]'
'LNodeType[id="Dummy.TCTR"]',
) as Element;

describe("Function to import LNodeType with its sub data", () => {
Expand Down Expand Up @@ -76,14 +76,18 @@ describe("Function to import LNodeType with its sub data", () => {
});

it("allows to overwrite existing LNodeType", () => {
const edits1 = importLNodeType(tctrHardUpdate, completeTemplate, { overwrite: true }) as Insert[];
const edits1 = importLNodeType(tctrHardUpdate, completeTemplate, {
overwrite: true,
}) as Insert[];

expect(edits1.length).to.equal(2);

expect(edits1[0]).to.satisfies(isInsert);
expect(edits1[1]).to.satisfies(isRemove);

const edits2 = importLNodeType(tctrHardUpdate, completeTemplate, { overwrite: false }) as Insert[];
const edits2 = importLNodeType(tctrHardUpdate, completeTemplate, {
overwrite: false,
}) as Insert[];

expect(edits2.length).to.equal(1);

Expand Down
Loading
Loading