Skip to content

Commit

Permalink
fix(menu/importieds): allow import to new projects
Browse files Browse the repository at this point in the history
  • Loading branch information
JakobVogelsang committed Sep 26, 2022
1 parent 6e0ccf1 commit 5e34cb2
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 35 deletions.
76 changes: 41 additions & 35 deletions src/menu/ImportIEDs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,10 @@ function hasConnectionToIed(type: Element, ied: Element): boolean {
function addEnumType(
ied: Element,
enumType: Element,
doc: Document
parent: Element
): SimpleAction | undefined {
const existEnumType = doc.querySelector(
`:root > DataTypeTemplates > EnumType[id="${enumType.getAttribute('id')}"]`
const existEnumType = parent.querySelector(
`EnumType[id="${enumType.getAttribute('id')}"]`
);

if (existEnumType && enumType.isEqualNode(existEnumType)) return;
Expand All @@ -204,7 +204,7 @@ function addEnumType(

return {
new: {
parent: doc.querySelector(':root > DataTypeTemplates')!,
parent,
element: enumType,
},
};
Expand All @@ -213,10 +213,10 @@ function addEnumType(
function addDAType(
ied: Element,
daType: Element,
doc: Document
parent: Element
): SimpleAction | undefined {
const existDAType = doc.querySelector(
`:root > DataTypeTemplates > DAType[id="${daType.getAttribute('id')}"]`
const existDAType = parent.querySelector(
`DAType[id="${daType.getAttribute('id')}"]`
);

if (existDAType && daType.isEqualNode(existDAType)) return;
Expand All @@ -239,7 +239,7 @@ function addDAType(

return {
new: {
parent: doc.querySelector(':root > DataTypeTemplates')!,
parent,
element: daType,
},
};
Expand All @@ -248,10 +248,10 @@ function addDAType(
function addDOType(
ied: Element,
doType: Element,
doc: Document
parent: Element
): SimpleAction | undefined {
const existDOType = doc.querySelector(
`:root > DataTypeTemplates > DOType[id="${doType.getAttribute('id')}"]`
const existDOType = parent.querySelector(
`DOType[id="${doType.getAttribute('id')}"]`
);

if (existDOType && doType.isEqualNode(existDOType)) return;
Expand All @@ -274,7 +274,7 @@ function addDOType(

return {
new: {
parent: doc.querySelector(':root > DataTypeTemplates')!,
parent,
element: doType,
},
};
Expand All @@ -283,12 +283,10 @@ function addDOType(
function addLNodeType(
ied: Element,
lNodeType: Element,
doc: Document
parent: Element
): SimpleAction | undefined {
const existLNodeType = doc.querySelector(
`:root > DataTypeTemplates > LNodeType[id="${lNodeType.getAttribute(
'id'
)}"]`
const existLNodeType = parent.querySelector(
`LNodeType[id="${lNodeType.getAttribute('id')}"]`
);

if (existLNodeType && lNodeType.isEqualNode(existLNodeType)) return;
Expand All @@ -310,7 +308,7 @@ function addLNodeType(

return {
new: {
parent: doc.querySelector(':root > DataTypeTemplates')!,
parent,
element: lNodeType,
},
};
Expand All @@ -319,21 +317,42 @@ function addLNodeType(
function addDataTypeTemplates(ied: Element, doc: XMLDocument): SimpleAction[] {
const actions: (SimpleAction | undefined)[] = [];

const dataTypeTemplates = doc.querySelector(':root > DataTypeTemplates')
? doc.querySelector(':root > DataTypeTemplates')!
: createElement(doc, 'DataTypeTemplates', {});

if (!dataTypeTemplates.parentElement) {
actions.push({
new: {
parent: doc.querySelector('SCL')!,
element: dataTypeTemplates,
},
});
}

ied.ownerDocument
.querySelectorAll(':root > DataTypeTemplates > LNodeType')
.forEach(lNodeType => actions.push(addLNodeType(ied, lNodeType, doc)));
.forEach(lNodeType =>
actions.push(addLNodeType(ied, lNodeType, dataTypeTemplates!))
);

ied.ownerDocument
.querySelectorAll(':root > DataTypeTemplates > DOType')
.forEach(doType => actions.push(addDOType(ied, doType, doc)));
.forEach(doType =>
actions.push(addDOType(ied, doType, dataTypeTemplates!))
);

ied.ownerDocument
.querySelectorAll(':root > DataTypeTemplates > DAType')
.forEach(daType => actions.push(addDAType(ied, daType, doc)));
.forEach(daType =>
actions.push(addDAType(ied, daType, dataTypeTemplates!))
);

ied.ownerDocument
.querySelectorAll(':root > DataTypeTemplates > EnumType')
.forEach(enumType => actions.push(addEnumType(ied, enumType, doc)));
.forEach(enumType =>
actions.push(addEnumType(ied, enumType, dataTypeTemplates!))
);

return <SimpleAction[]>actions.filter(item => item !== undefined);
}
Expand Down Expand Up @@ -459,19 +478,6 @@ export default class ImportingIedPlugin extends LitElement {
return;
}

if (!doc.querySelector(':root > DataTypeTemplates')) {
const element = createElement(doc, 'DataTypeTemplates', {});

this.parent.dispatchEvent(
newActionEvent({
new: {
parent: doc.documentElement,
element,
},
})
);
}

if (ieds.length === 1) {
importIED(ieds[0], doc, this.parent);
return;
Expand Down
92 changes: 92 additions & 0 deletions test/integration/editors/triggered/ImportIedsPlugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,92 @@ import ImportingIedPlugin from '../../../../src/menu/ImportIEDs.js';
describe('ImportIedsPlugin', () => {
customElements.define('import-ieds-plugin', ImportingIedPlugin);

describe('imports valid ied elements to empty projects', () => {
let doc: XMLDocument;
let importDoc: XMLDocument;

let parent: MockWizardEditor;
let element: ImportingIedPlugin;

beforeEach(async () => {
parent = await fixture(
html`<mock-wizard-editor
><import-ieds-plugin></import-ieds-plugin
></mock-wizard-editor>`
);

element = <ImportingIedPlugin>parent.querySelector('import-ieds-plugin')!;

doc = await fetch('/test/testfiles/importieds/emptyproject.scd')
.then(response => response.text())
.then(str => new DOMParser().parseFromString(str, 'application/xml'));
element.doc = doc;
await element.updateComplete;

importDoc = await fetch('/test/testfiles/importieds/valid.iid')
.then(response => response.text())
.then(str => new DOMParser().parseFromString(str, 'application/xml'));
});

it('loads ied element to the project', async () => {
expect(element.doc?.querySelector(':root > IED[name="TestImportIED"]')).to
.not.exist;
element.prepareImport(importDoc, doc);
await element.updateComplete;
expect(element.doc?.querySelector(':root > IED[name="TestImportIED"]')).to
.exist;
});

it('adds the connectedap of the imported ied', async () => {
element.prepareImport(importDoc, doc);
await element.updateComplete;
expect(
element.doc.querySelector(
'SubNetwork[name="NewSubNetwork"] > ConnectedAP[iedName="TestImportIED"]'
)
).to.exist;
});

it('creates new subnetwork if not present in the doc', () => {
expect(element.doc.querySelector('SubNetwork[name="NewSubNetwork"]')).to
.not.exist;
element.prepareImport(importDoc, doc);
expect(element.doc.querySelector('SubNetwork[name="NewSubNetwork"]')).to
.exist;
});

it('allows multiple import of TEMPLATE IEDs', async () => {
const templateIED1 = await fetch(
'/test/testfiles/importieds/template.icd'
)
.then(response => response.text())
.then(str => new DOMParser().parseFromString(str, 'application/xml'));
element.prepareImport(templateIED1, doc);

const templateIED2 = await fetch(
'/test/testfiles/importieds/template.icd'
)
.then(response => response.text())
.then(str => new DOMParser().parseFromString(str, 'application/xml'));
element.prepareImport(templateIED2, doc);

expect(element.doc.querySelector('IED[name="TEMPLATE_IED1"]')).to.exist;
expect(element.doc.querySelector('IED[name="TEMPLATE_IED2"]')).to.exist;
});

it('loads unique lnodetypes to the project', () => {
expect(
element.doc?.querySelectorAll(':root > DataTypeTemplates > LNodeType')
.length
).to.equal(0);
element.prepareImport(importDoc, doc);
expect(
element.doc?.querySelectorAll(':root > DataTypeTemplates > LNodeType')
.length
).to.equal(5);
});
});

describe('imports valid ied elements', () => {
let doc: XMLDocument;
let importDoc: XMLDocument;
Expand Down Expand Up @@ -83,6 +169,7 @@ describe('ImportIedsPlugin', () => {
.length
).to.equal(11);
});

it('loads unique enumtypes to the project', () => {
expect(
element.doc?.querySelectorAll(':root > DataTypeTemplates > EnumType')
Expand All @@ -94,6 +181,7 @@ describe('ImportIedsPlugin', () => {
.length
).to.equal(10);
});

it('adds the connectedap of the imported ied', () => {
expect(element.doc.querySelector('ConnectedAP[iedName="TestImportIED"]'))
.to.not.exist;
Expand All @@ -105,13 +193,15 @@ describe('ImportIedsPlugin', () => {
?.parentElement
).to.equal(element.doc.querySelector('SubNetwork[name="NewSubNetwork"]'));
});

it('creates new subnetwork if not present in the doc', () => {
expect(element.doc.querySelector('SubNetwork[name="NewSubNetwork"]')).to
.not.exist;
element.prepareImport(importDoc, doc);
expect(element.doc.querySelector('SubNetwork[name="NewSubNetwork"]')).to
.exist;
});

it('allows multiple import of TEMPLATE IEDs', async () => {
expect(element.doc.querySelectorAll('IED').length).to.equal(3);

Expand All @@ -132,6 +222,7 @@ describe('ImportIedsPlugin', () => {
expect(element.doc.querySelector('IED[name="TEMPLATE_IED1"]')).to.exist;
expect(element.doc.querySelector('IED[name="TEMPLATE_IED2"]')).to.exist;
});

it('renders wizard for files containing more than one IED', async () => {
const multipleIedDoc = await fetch(
'/test/testfiles/importieds/multipleied.scd'
Expand All @@ -147,6 +238,7 @@ describe('ImportIedsPlugin', () => {
parent.wizardUI.dialog?.querySelectorAll('mwc-check-list-item').length
).to.equal(3);
});

it('imports selected IED from Import IED wizard', async () => {
const multipleIedDoc = await fetch(
'/test/testfiles/importieds/multipleied.scd'
Expand Down
4 changes: 4 additions & 0 deletions test/testfiles/importieds/emptyproject.scd
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<SCL xmlns="http://www.iec.ch/61850/2003/SCL" version="2007" revision="B" release="4">
<Header id="TestIID"/>
</SCL>

0 comments on commit 5e34cb2

Please sign in to comment.