Skip to content

Commit aa2e1ac

Browse files
author
Dennis Labordus
committed
First part of the Locamation Plugin to update private fields.
Signed-off-by: Dennis Labordus <[email protected]>
1 parent 4e2bf44 commit aa2e1ac

File tree

10 files changed

+511
-6
lines changed

10 files changed

+511
-6
lines changed

public/js/plugins.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,15 @@ export const officialPlugins = [
151151
requireDoc: true,
152152
position: 'middle'
153153
},
154+
{
155+
name: 'Locamation VMU',
156+
src: '/src/menu/LocamationVMU.js',
157+
icon: 'edit_note',
158+
default: true,
159+
kind: 'menu',
160+
requireDoc: true,
161+
position: 'middle'
162+
},
154163
{
155164
name: 'CoMPAS Settings',
156165
src: '/src/menu/CompasSettings.js',

src/foundation/nsdoc.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export async function initializeNsdoc(): Promise<Nsdoc> {
6565
}
6666
>].getDataDescription(element);
6767
}
68-
68+
6969
}
7070
}
7171

@@ -77,4 +77,4 @@ export async function initializeNsdoc(): Promise<Nsdoc> {
7777
*/
7878
function getNsdocDocumentation(nsdoc: XMLDocument, id: string) {
7979
return nsdoc?.querySelector(`NSDoc > Doc[id="${id}"]`)?.textContent;
80-
}
80+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import {css, customElement, html, LitElement, property, TemplateResult} from 'lit-element';
2+
import {get, translate} from "lit-translate";
3+
4+
import {newSubWizardEvent, newWizardEvent, Wizard, WizardInput} from '../foundation.js';
5+
import {isSCLNamespace} from "../schemas.js";
6+
import {Nsdoc} from "../foundation/nsdoc.js";
7+
import {dispatchEventOnOpenScd} from "../compas/foundation.js";
8+
9+
import {iedHeader, lDeviceHeader} from "./foundation.js";
10+
import {locamationLNListWizard} from "./LocamationLNList.js";
11+
12+
@customElement('locamation-ied-list')
13+
export class LocamationIEDListElement extends LitElement {
14+
@property({type: Document})
15+
doc!: XMLDocument;
16+
@property()
17+
nsdoc!: Nsdoc;
18+
19+
private get logicaDevices(): Element[] {
20+
return Array.from(this.doc!.querySelectorAll(
21+
'IED[manufacturer="Locamation B.V."] LDevice'))
22+
.filter(isSCLNamespace)
23+
.filter(element => element.querySelector('LN > Private[type="LCMTN_VMU_SENSOR"]') !== null);
24+
}
25+
26+
close(): void {
27+
// Close the Save Dialog.
28+
dispatchEventOnOpenScd(newWizardEvent());
29+
}
30+
31+
render(): TemplateResult {
32+
const lDevices = this.logicaDevices;
33+
if (lDevices.length > 0) {
34+
return html `
35+
<mwc-list>
36+
${lDevices.map(lDevice => {
37+
const ied = lDevice.closest('IED')!;
38+
return html`
39+
<mwc-list-item
40+
twoline
41+
@click="${(e: Event) => {
42+
e.target?.dispatchEvent(
43+
newSubWizardEvent(() => locamationLNListWizard(lDevice, this.nsdoc))
44+
);
45+
}}"
46+
>
47+
<span>${iedHeader(ied)}</span>
48+
<span slot="secondary">${lDeviceHeader(lDevice)}</span>
49+
</mwc-list-item>`
50+
})}
51+
</mwc-list>
52+
`
53+
}
54+
return html `
55+
<mwc-list>
56+
<mwc-list-item><i>${translate('locamation.vmu.ied.missing')}</i></mwc-list-item>
57+
</mwc-list>
58+
`;
59+
}
60+
61+
static styles = css`
62+
:host {
63+
width: 20vw;
64+
}
65+
`
66+
}
67+
68+
export function locamationIEDListWizard(doc: XMLDocument, nsdoc: Nsdoc): Wizard {
69+
function close() {
70+
return function (inputs: WizardInput[], wizard: Element) {
71+
const locamationIEDListElement = <LocamationIEDListElement>wizard.shadowRoot!.querySelector('locamation-ied-list')
72+
locamationIEDListElement.close();
73+
return [];
74+
};
75+
}
76+
77+
return [
78+
{
79+
title: get('locamation.vmu.ied.title'),
80+
secondary: {
81+
icon: 'close',
82+
label: get('close'),
83+
action: close(),
84+
},
85+
content: [
86+
html`<locamation-ied-list .doc="${doc}" .nsdoc="${nsdoc}"></locamation-ied-list>`,
87+
],
88+
},
89+
];
90+
}
91+

src/locamation/LocamationLNEdit.ts

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import {css, customElement, html, LitElement, property, TemplateResult} from 'lit-element';
2+
import {get, translate} from "lit-translate";
3+
4+
import {Wizard, WizardAction, WizardInput} from '../foundation.js';
5+
import {Nsdoc} from "../foundation/nsdoc.js";
6+
7+
import {
8+
getPrivateTextValue,
9+
hasPrivateElement,
10+
iedHeader,
11+
lDeviceHeader,
12+
lnHeader,
13+
LOCAMATION_PRIVATE
14+
} from "./foundation.js";
15+
import Protocol from "devtools-protocol";
16+
import integer = Protocol.integer;
17+
18+
@customElement('locamation-ln-edit')
19+
export class LocamationVMUEditElement extends LitElement {
20+
@property({type: Element})
21+
logicalNode!: Element;
22+
@property()
23+
nsdoc!: Nsdoc;
24+
25+
save(inputs: WizardInput[]): WizardAction[] {
26+
// if (!this.fieldsChanged(inputs)) {
27+
return [];
28+
// }
29+
//
30+
// cloneElement(element, { name, desc });
31+
//
32+
// const oldPrivateElement = getPrivate(oldElement, 'compas_substation');
33+
// const newPrivateElement = getOrCreatePrivate(oldElement, newElement, 'compas_substation');
34+
//
35+
// const compasName = getInputFieldValue(inputs, 'compasName');
36+
// processPrivateTextElement(newPrivateElement, EXTENSION_NAMESPACE, 'compas', 'CompasName', compasName);
37+
//
38+
// if (oldPrivateElement) {
39+
// return [{old: {element: oldPrivateElement}, new: {element: newPrivateElement}}];
40+
// }
41+
// return [{new: {parent: newElement, element: newPrivateElement}}];
42+
}
43+
//
44+
// private fieldsChanged(inputs: WizardInput[]): boolean {
45+
// const oldIdentifier= getPrivateTextValue(this.logicalNode, LOCAMATION_PRIVATE, 'IDENTIFIER');
46+
// const oldChannel = getPrivateTextValue(this.logicalNode, LOCAMATION_PRIVATE, 'CHANNEL');
47+
// const oldTransformPrimary = getPrivateTextValue(this.logicalNode, LOCAMATION_PRIVATE, 'TRANSFORM-PRIMARY');
48+
// const oldTransformSecondary = getPrivateTextValue(this.logicalNode, LOCAMATION_PRIVATE, 'TRANSFORM-SECONDARY');
49+
// return inputFieldChanged(inputs, 'Identifier', oldIdentifier)
50+
// || inputFieldChanged(inputs, 'Channel', oldChannel)
51+
// || inputFieldChanged(inputs, 'TransformPrimary', oldTransformPrimary)
52+
// || inputFieldChanged(inputs, 'TransformSecondary', oldTransformSecondary);
53+
// }
54+
55+
private getIdentifierPart(partNr: integer): string {
56+
const identifier = getPrivateTextValue(this.logicalNode, LOCAMATION_PRIVATE, 'IDENTIFIER')?? '';
57+
const parts = identifier.trim().split('.');
58+
if (parts.length < partNr) {
59+
return '';
60+
}
61+
return parts[partNr];
62+
}
63+
64+
render(): TemplateResult {
65+
const lDevice = this.logicalNode.closest('LDevice')!;
66+
const ied = lDevice.closest('IED')!;
67+
return html `
68+
<wizard-textfield label="IED"
69+
.maybeValue=${iedHeader(ied)}
70+
helper="${translate('locamation.vmu.ied.name')}"
71+
disabled>
72+
</wizard-textfield>
73+
<wizard-textfield label="Logical Device"
74+
.maybeValue=${lDeviceHeader(lDevice)}
75+
helper="${translate('locamation.vmu.ldevice.name')}"
76+
disabled>
77+
</wizard-textfield>
78+
<wizard-textfield label="Logical Node"
79+
.maybeValue=${lnHeader(this.logicalNode, this.nsdoc)}
80+
helper="${translate('locamation.vmu.ln.name')}"
81+
disabled>
82+
</wizard-textfield>
83+
84+
<div id="Identifier">
85+
<wizard-textfield label="IdentifierPart0"
86+
.maybeValue=${this.getIdentifierPart(0)}
87+
helper="Identifier"
88+
required>
89+
</wizard-textfield>
90+
<wizard-textfield label="IdentifierPart1"
91+
.maybeValue=${this.getIdentifierPart(1)}
92+
helper="Identifier"
93+
required>
94+
</wizard-textfield>
95+
<wizard-textfield label="IdentifierPart2"
96+
.maybeValue=${this.getIdentifierPart(2)}
97+
helper="Identifier"
98+
required>
99+
</wizard-textfield>
100+
</div>
101+
${hasPrivateElement(this.logicalNode, LOCAMATION_PRIVATE, 'SUM') ?
102+
html `<wizard-textfield label="Sum"
103+
.maybeValue=${getPrivateTextValue(this.logicalNode, LOCAMATION_PRIVATE, 'SUM')}
104+
helper="Sum"
105+
required>
106+
</wizard-textfield>` :
107+
html `<wizard-textfield label="Channel"
108+
.maybeValue=${getPrivateTextValue(this.logicalNode, LOCAMATION_PRIVATE, 'CHANNEL')}
109+
helper="Channel"
110+
required>
111+
</wizard-textfield>`
112+
}
113+
<wizard-textfield label="TransformPrimary"
114+
.maybeValue=${getPrivateTextValue(this.logicalNode, LOCAMATION_PRIVATE, 'TRANSFORM-PRIMARY')}
115+
helper="TransformPrimary"
116+
required>
117+
</wizard-textfield>
118+
<wizard-textfield label="TransformSecondary"
119+
.maybeValue=${getPrivateTextValue(this.logicalNode, LOCAMATION_PRIVATE, 'TRANSFORM-SECONDARY')}
120+
helper="TransformSecondary"
121+
required>
122+
</wizard-textfield>
123+
`;
124+
}
125+
126+
static styles = css`
127+
:host {
128+
width: 20vw;
129+
}
130+
131+
* {
132+
display: block;
133+
margin-top: 16px;
134+
}
135+
`
136+
}
137+
138+
export function locamationLNEditWizard(logicalNode: Element, nsdoc: Nsdoc): Wizard {
139+
function save() {
140+
return function (inputs: WizardInput[], wizard: Element): WizardAction[] {
141+
const locamationVMUEditElement = <LocamationVMUEditElement>wizard.shadowRoot!.querySelector('locamation-ln-edit')
142+
return locamationVMUEditElement.save(inputs);
143+
};
144+
}
145+
146+
return [
147+
{
148+
title: get('locamation.vmu.ln.editTitle'),
149+
primary: {
150+
icon: 'save',
151+
label: get('save'),
152+
action: save(),
153+
},
154+
content: [
155+
html`<locamation-ln-edit .logicalNode="${logicalNode}" .nsdoc="${nsdoc}"></locamation-ln-edit>`,
156+
],
157+
},
158+
];
159+
}
160+

src/locamation/LocamationLNList.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import {css, customElement, html, LitElement, property, TemplateResult} from 'lit-element';
2+
import {get, translate} from "lit-translate";
3+
4+
import {newSubWizardEvent, newWizardEvent, Wizard, WizardInput} from '../foundation.js';
5+
import {isSCLNamespace} from "../schemas.js";
6+
import {Nsdoc} from "../foundation/nsdoc.js";
7+
8+
import {dispatchEventOnOpenScd} from "../compas/foundation.js";
9+
import {locamationLNEditWizard} from "./LocamationLNEdit.js";
10+
import {iedHeader, lDeviceHeader, lnHeader} from "./foundation.js";
11+
12+
@customElement('locamation-ln-list')
13+
export class LocamationLNodeListElement extends LitElement {
14+
@property({type: Element})
15+
lDevice!: Element;
16+
@property()
17+
nsdoc!: Nsdoc;
18+
19+
private get logicaNodes(): Element[] {
20+
return Array.from(this.lDevice!.querySelectorAll('LN'))
21+
.filter(isSCLNamespace)
22+
.filter(element => element.querySelector('Private[type="LCMTN_VMU_SENSOR"]') !== null);
23+
}
24+
25+
close(): void {
26+
// Close the Save Dialog.
27+
dispatchEventOnOpenScd(newWizardEvent());
28+
}
29+
30+
render(): TemplateResult {
31+
const logicalNodes = this.logicaNodes;
32+
if (logicalNodes.length > 0) {
33+
const ied = this.lDevice.closest('IED')!;
34+
return html `
35+
<wizard-textfield label="IED"
36+
.maybeValue=${iedHeader(ied)}
37+
helper="${translate('locamation.vmu.ied.name')}"
38+
disabled>
39+
</wizard-textfield>
40+
<wizard-textfield label="Logical Device"
41+
.maybeValue=${lDeviceHeader(this.lDevice)}
42+
helper="${translate('locamation.vmu.ldevice.name')}"
43+
disabled>
44+
</wizard-textfield>
45+
<mwc-list>
46+
${logicalNodes.map(ln => {
47+
return html`
48+
<mwc-list-item
49+
@click="${(e: Event) => {
50+
e.target?.dispatchEvent(
51+
newSubWizardEvent(() => locamationLNEditWizard(ln, this.nsdoc))
52+
);
53+
}}"
54+
>
55+
<span>${lnHeader(ln, this.nsdoc)}</span>
56+
</mwc-list-item>`
57+
})}
58+
</mwc-list>
59+
`
60+
}
61+
return html `
62+
<mwc-list>
63+
<mwc-list-item><i>${translate('locamation.vmu.ied.missing')}</i></mwc-list-item>
64+
</mwc-list>
65+
`;
66+
}
67+
68+
static styles = css`
69+
:host {
70+
width: 20vw;
71+
}
72+
73+
* {
74+
display: block;
75+
margin-top: 16px;
76+
}
77+
`
78+
}
79+
80+
export function locamationLNListWizard(lDevice: Element, nsdoc: Nsdoc): Wizard {
81+
function close() {
82+
return function (inputs: WizardInput[], wizard: Element) {
83+
const locamationIEDListElement = <LocamationLNodeListElement>wizard.shadowRoot!.querySelector('locamation-ln-list')
84+
locamationIEDListElement.close();
85+
return [];
86+
};
87+
}
88+
89+
return [
90+
{
91+
title: get('locamation.vmu.ln.title'),
92+
secondary: {
93+
icon: 'close',
94+
label: get('close'),
95+
action: close(),
96+
},
97+
content: [
98+
html`<locamation-ln-list .lDevice="${lDevice}" .nsdoc="${nsdoc}"></locamation-ln-list>`,
99+
],
100+
},
101+
];
102+
}
103+

0 commit comments

Comments
 (0)