Skip to content

Commit ca428ac

Browse files
authored
Merge pull request #117 from com-pas/locamation-plugin
Added Locamation plugin
2 parents 67c06d6 + d7d2f2b commit ca428ac

File tree

62 files changed

+3316
-1213
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+3316
-1213
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.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ export function newActionEvent<T extends EditorAction>(
134134
}
135135

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

src/locamation/LocamationLNEdit.ts

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import {css, customElement, html, LitElement, property, TemplateResult} from 'lit-element';
2+
import {get, translate} from "lit-translate";
3+
4+
import {patterns} from "../wizards/foundation/limits.js";
5+
import {checkValidity, ComplexAction, Wizard, WizardAction, WizardInput, wizardInputSelector} from '../foundation.js';
6+
import {Nsdoc} from "../foundation/nsdoc.js";
7+
8+
import '../wizard-textfield.js';
9+
10+
import {
11+
createEditorAction,
12+
getInputFieldValue,
13+
getPrivate,
14+
getPrivateTextValue,
15+
hasPrivateElement,
16+
iedHeader,
17+
inputFieldChanged,
18+
lDeviceHeader,
19+
lnHeader,
20+
} from "./foundation.js";
21+
22+
@customElement('locamation-ln-edit')
23+
export class LocamationVMUEditElement extends LitElement {
24+
@property({type: Element})
25+
logicalNode!: Element;
26+
@property()
27+
nsdoc!: Nsdoc;
28+
29+
get inputs(): WizardInput[] {
30+
return Array.from(this.shadowRoot!.querySelectorAll(wizardInputSelector));
31+
}
32+
33+
save(): WizardAction[] {
34+
const locamationPrivate = getPrivate(this.logicalNode);
35+
36+
if (!this.fieldsChanged(locamationPrivate, this.inputs) || !this.checkValidityInputs(this.inputs)) {
37+
return [];
38+
}
39+
40+
const complexAction: ComplexAction = {
41+
actions: [],
42+
title: get('locamation.vmu.updateAction', {lnName: lnHeader(this.logicalNode, this.nsdoc)}),
43+
};
44+
45+
complexAction.actions.push(...createEditorAction(locamationPrivate, 'IDENTIFIER', getInputFieldValue(this.inputs, 'identifier')));
46+
if (hasPrivateElement(this.logicalNode, 'SUM')) {
47+
complexAction.actions.push(...createEditorAction(locamationPrivate, 'SUM', getInputFieldValue(this.inputs, 'sum')));
48+
} else {
49+
complexAction.actions.push(...createEditorAction(locamationPrivate, 'CHANNEL', getInputFieldValue(this.inputs, 'channel')));
50+
}
51+
complexAction.actions.push(...createEditorAction(locamationPrivate, 'TRANSFORM-PRIMARY', getInputFieldValue(this.inputs, 'transformPrimary')));
52+
complexAction.actions.push(...createEditorAction(locamationPrivate, 'TRANSFORM-SECONDARY', getInputFieldValue(this.inputs, 'transformSecondary')));
53+
54+
return complexAction.actions.length ? [complexAction] : [];
55+
}
56+
57+
private fieldsChanged(locamationPrivate: Element | null, inputs: WizardInput[]): boolean {
58+
const oldIdentifier= getPrivateTextValue(locamationPrivate, 'IDENTIFIER');
59+
const oldChannel = getPrivateTextValue(locamationPrivate, 'CHANNEL');
60+
const oldSum = getPrivateTextValue(locamationPrivate, 'SUM');
61+
const oldTransformPrimary = getPrivateTextValue(locamationPrivate, 'TRANSFORM-PRIMARY');
62+
const oldTransformSecondary = getPrivateTextValue(locamationPrivate, 'TRANSFORM-SECONDARY');
63+
64+
return inputFieldChanged(inputs, 'identifier', oldIdentifier)
65+
|| (hasPrivateElement(locamationPrivate, 'SUM') ? inputFieldChanged(inputs, 'sum', oldSum) : false)
66+
|| (hasPrivateElement(locamationPrivate, 'CHANNEL') ? inputFieldChanged(inputs, 'channel', oldChannel) : false)
67+
|| inputFieldChanged(inputs, 'transformPrimary', oldTransformPrimary)
68+
|| inputFieldChanged(inputs, 'transformSecondary', oldTransformSecondary);
69+
}
70+
71+
private checkValidityInputs(inputs: WizardInput[]): boolean {
72+
return Array.from(inputs).every(checkValidity);
73+
}
74+
75+
render(): TemplateResult {
76+
const lDevice = this.logicalNode.closest('LDevice')!;
77+
const ied = lDevice.closest('IED')!;
78+
const locamationPrivate = getPrivate(this.logicalNode);
79+
80+
// Depending on the value of the class the pattern for the CIM or VIM for SUM/CHANNEL will change.
81+
let channelPattern = '[0-5]';
82+
let sumPattern = '[0-5],[0-5],[0-5]';
83+
if (this.logicalNode.getAttribute('lnClass') === 'TVTR') {
84+
channelPattern = '[0-2]';
85+
sumPattern = '[0-2],[0-2],[0-2]';
86+
}
87+
88+
return html `
89+
<wizard-textfield label="${translate('locamation.vmu.ied.name')}"
90+
.maybeValue=${iedHeader(ied)}
91+
disabled>
92+
</wizard-textfield>
93+
<wizard-textfield label="${translate('locamation.vmu.ldevice.name')}"
94+
.maybeValue=${lDeviceHeader(lDevice)}
95+
disabled>
96+
</wizard-textfield>
97+
<wizard-textfield label="${translate('locamation.vmu.ln.name')}"
98+
.maybeValue=${lnHeader(this.logicalNode, this.nsdoc)}
99+
disabled>
100+
</wizard-textfield>
101+
102+
<wizard-textfield label="${translate('locamation.vmu.version')}"
103+
.maybeValue=${getPrivateTextValue(locamationPrivate, 'VERSION')}
104+
disabled>
105+
</wizard-textfield>
106+
107+
<wizard-textfield id="identifier"
108+
label="${translate('locamation.vmu.identifier')}"
109+
.maybeValue=${getPrivateTextValue(locamationPrivate, 'IDENTIFIER')}
110+
helper="${translate('locamation.vmu.identifierHelper')}"
111+
placeholder="134.12.213"
112+
pattern="^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\\.(?!$)|$)){3}$"
113+
required
114+
dialogInitialFocus>
115+
</wizard-textfield>
116+
117+
${hasPrivateElement(locamationPrivate, 'SUM') ?
118+
html `<wizard-textfield id="sum"
119+
label="${translate('locamation.vmu.sum')}"
120+
.maybeValue=${getPrivateTextValue(locamationPrivate, 'SUM')}
121+
helper="${translate('locamation.vmu.sumHelper')}"
122+
placeholder="0,1,2"
123+
pattern="${sumPattern}"
124+
required>
125+
</wizard-textfield>` : html ``
126+
}
127+
${hasPrivateElement(locamationPrivate, 'CHANNEL') ?
128+
html `<wizard-textfield id="channel"
129+
label="${translate('locamation.vmu.channel')}"
130+
.maybeValue=${getPrivateTextValue(locamationPrivate, 'CHANNEL')}
131+
helper="${translate('locamation.vmu.channelHelper')}"
132+
pattern="${channelPattern}"
133+
required>
134+
</wizard-textfield>` : html ``
135+
}
136+
137+
<wizard-textfield id="transformPrimary"
138+
label="${translate('locamation.vmu.transformPrimary')}"
139+
.maybeValue=${getPrivateTextValue(locamationPrivate, 'TRANSFORM-PRIMARY')}
140+
helper="${translate('locamation.vmu.transformPrimaryHelper')}"
141+
pattern="${patterns.unsigned}"
142+
required>
143+
</wizard-textfield>
144+
<wizard-textfield id="transformSecondary"
145+
label="${translate('locamation.vmu.transformSecondary')}"
146+
.maybeValue=${getPrivateTextValue(locamationPrivate, 'TRANSFORM-SECONDARY')}
147+
helper="${translate('locamation.vmu.transformSecondaryHelper')}"
148+
pattern="${patterns.unsigned}"
149+
required>
150+
</wizard-textfield>
151+
`;
152+
}
153+
154+
static styles = css`
155+
:host {
156+
width: 20vw;
157+
}
158+
159+
* {
160+
display: block;
161+
margin-top: 16px;
162+
}
163+
`
164+
}
165+
166+
export function locamationLNEditWizard(logicalNode: Element, nsdoc: Nsdoc): Wizard {
167+
function save() {
168+
return function (inputs: WizardInput[], wizard: Element): WizardAction[] {
169+
const locamationVMUEditElement = <LocamationVMUEditElement>wizard.shadowRoot!.querySelector('locamation-ln-edit')
170+
return locamationVMUEditElement.save();
171+
};
172+
}
173+
174+
return [
175+
{
176+
title: get('locamation.vmu.ln.editTitle'),
177+
primary: {
178+
icon: 'save',
179+
label: get('save'),
180+
action: save(),
181+
},
182+
content: [
183+
html`<locamation-ln-edit .logicalNode="${logicalNode}" .nsdoc="${nsdoc}"></locamation-ln-edit>`,
184+
],
185+
},
186+
];
187+
}
188+

0 commit comments

Comments
 (0)