Skip to content

Commit

Permalink
Merge pull request #117 from com-pas/locamation-plugin
Browse files Browse the repository at this point in the history
Added Locamation plugin
  • Loading branch information
dlabordus authored Feb 21, 2022
2 parents 67c06d6 + d7d2f2b commit ca428ac
Show file tree
Hide file tree
Showing 62 changed files with 3,316 additions and 1,213 deletions.
9 changes: 9 additions & 0 deletions public/js/plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,15 @@ export const officialPlugins = [
requireDoc: true,
position: 'middle'
},
{
name: 'Locamation VMU',
src: '/src/menu/LocamationVMU.js',
icon: 'edit_note',
default: true,
kind: 'menu',
requireDoc: true,
position: 'middle'
},
{
name: 'CoMPAS Settings',
src: '/src/menu/CompasSettings.js',
Expand Down
2 changes: 1 addition & 1 deletion src/foundation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export function newActionEvent<T extends EditorAction>(
}

export const wizardInputSelector =
'wizard-textfield, mwc-textfield, ace-editor, mwc-select,wizard-select, wizard-checkbox';
'wizard-textfield, mwc-textfield, ace-editor, mwc-select, wizard-select, wizard-checkbox';
export type WizardInput =
| WizardTextField
| TextField
Expand Down
92 changes: 92 additions & 0 deletions src/locamation/LocamationIEDList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import {css, customElement, html, LitElement, property, TemplateResult} from 'lit-element';
import {get, translate} from "lit-translate";

import '@material/mwc-list';
import '@material/mwc-list/mwc-list-item';

import {newSubWizardEvent, newWizardEvent, Wizard, WizardInput} from '../foundation.js';
import {isSCLNamespace} from "../schemas.js";
import {Nsdoc} from "../foundation/nsdoc.js";

import {iedHeader, lDeviceHeader, LOCAMATION_MANUFACTURER, LOCAMATION_PRIVATE} from "./foundation.js";
import {locamationLNListWizard} from "./LocamationLNList.js";

@customElement('locamation-ied-list')
export class LocamationIEDListElement extends LitElement {
@property({type: Document})
doc!: XMLDocument;
@property()
nsdoc!: Nsdoc;

private get logicaDevices(): Element[] {
return Array.from(this.doc!.querySelectorAll(`IED[manufacturer="${LOCAMATION_MANUFACTURER}"] LDevice`))
.filter(isSCLNamespace)
.filter(element => element.querySelector(`LN > Private[type="${LOCAMATION_PRIVATE}"]`) !== null);
}

close(): void {
// Close the Save Dialog.
this.dispatchEvent(newWizardEvent());
}

render(): TemplateResult {
const lDevices = this.logicaDevices;
if (lDevices.length > 0) {
return html `
<mwc-list>
${lDevices.map(lDevice => {
const ied = lDevice.closest('IED')!;
return html`
<mwc-list-item
twoline
@click="${(e: Event) => {
e.target?.dispatchEvent(
newSubWizardEvent(() => locamationLNListWizard(lDevice, this.nsdoc))
);
}}"
>
<span>${iedHeader(ied)}</span>
<span slot="secondary">${lDeviceHeader(lDevice)}</span>
</mwc-list-item>`
})}
</mwc-list>
`
}
return html `
<mwc-list>
<mwc-list-item><i>${translate('locamation.vmu.ied.missing')}</i></mwc-list-item>
</mwc-list>
`;
}

static styles = css`
:host {
width: 20vw;
}
`
}

export function locamationIEDListWizard(doc: XMLDocument, nsdoc: Nsdoc): Wizard {
function close() {
return function (inputs: WizardInput[], wizard: Element) {
const locamationIEDListElement = <LocamationIEDListElement>wizard.shadowRoot!.querySelector('locamation-ied-list')
locamationIEDListElement.close();
return [];
};
}

return [
{
title: get('locamation.vmu.ied.title'),
secondary: {
icon: 'close',
label: get('close'),
action: close(),
},
content: [
html`<locamation-ied-list .doc="${doc}" .nsdoc="${nsdoc}"></locamation-ied-list>`,
],
},
];
}

188 changes: 188 additions & 0 deletions src/locamation/LocamationLNEdit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import {css, customElement, html, LitElement, property, TemplateResult} from 'lit-element';
import {get, translate} from "lit-translate";

import {patterns} from "../wizards/foundation/limits.js";
import {checkValidity, ComplexAction, Wizard, WizardAction, WizardInput, wizardInputSelector} from '../foundation.js';
import {Nsdoc} from "../foundation/nsdoc.js";

import '../wizard-textfield.js';

import {
createEditorAction,
getInputFieldValue,
getPrivate,
getPrivateTextValue,
hasPrivateElement,
iedHeader,
inputFieldChanged,
lDeviceHeader,
lnHeader,
} from "./foundation.js";

@customElement('locamation-ln-edit')
export class LocamationVMUEditElement extends LitElement {
@property({type: Element})
logicalNode!: Element;
@property()
nsdoc!: Nsdoc;

get inputs(): WizardInput[] {
return Array.from(this.shadowRoot!.querySelectorAll(wizardInputSelector));
}

save(): WizardAction[] {
const locamationPrivate = getPrivate(this.logicalNode);

if (!this.fieldsChanged(locamationPrivate, this.inputs) || !this.checkValidityInputs(this.inputs)) {
return [];
}

const complexAction: ComplexAction = {
actions: [],
title: get('locamation.vmu.updateAction', {lnName: lnHeader(this.logicalNode, this.nsdoc)}),
};

complexAction.actions.push(...createEditorAction(locamationPrivate, 'IDENTIFIER', getInputFieldValue(this.inputs, 'identifier')));
if (hasPrivateElement(this.logicalNode, 'SUM')) {
complexAction.actions.push(...createEditorAction(locamationPrivate, 'SUM', getInputFieldValue(this.inputs, 'sum')));
} else {
complexAction.actions.push(...createEditorAction(locamationPrivate, 'CHANNEL', getInputFieldValue(this.inputs, 'channel')));
}
complexAction.actions.push(...createEditorAction(locamationPrivate, 'TRANSFORM-PRIMARY', getInputFieldValue(this.inputs, 'transformPrimary')));
complexAction.actions.push(...createEditorAction(locamationPrivate, 'TRANSFORM-SECONDARY', getInputFieldValue(this.inputs, 'transformSecondary')));

return complexAction.actions.length ? [complexAction] : [];
}

private fieldsChanged(locamationPrivate: Element | null, inputs: WizardInput[]): boolean {
const oldIdentifier= getPrivateTextValue(locamationPrivate, 'IDENTIFIER');
const oldChannel = getPrivateTextValue(locamationPrivate, 'CHANNEL');
const oldSum = getPrivateTextValue(locamationPrivate, 'SUM');
const oldTransformPrimary = getPrivateTextValue(locamationPrivate, 'TRANSFORM-PRIMARY');
const oldTransformSecondary = getPrivateTextValue(locamationPrivate, 'TRANSFORM-SECONDARY');

return inputFieldChanged(inputs, 'identifier', oldIdentifier)
|| (hasPrivateElement(locamationPrivate, 'SUM') ? inputFieldChanged(inputs, 'sum', oldSum) : false)
|| (hasPrivateElement(locamationPrivate, 'CHANNEL') ? inputFieldChanged(inputs, 'channel', oldChannel) : false)
|| inputFieldChanged(inputs, 'transformPrimary', oldTransformPrimary)
|| inputFieldChanged(inputs, 'transformSecondary', oldTransformSecondary);
}

private checkValidityInputs(inputs: WizardInput[]): boolean {
return Array.from(inputs).every(checkValidity);
}

render(): TemplateResult {
const lDevice = this.logicalNode.closest('LDevice')!;
const ied = lDevice.closest('IED')!;
const locamationPrivate = getPrivate(this.logicalNode);

// Depending on the value of the class the pattern for the CIM or VIM for SUM/CHANNEL will change.
let channelPattern = '[0-5]';
let sumPattern = '[0-5],[0-5],[0-5]';
if (this.logicalNode.getAttribute('lnClass') === 'TVTR') {
channelPattern = '[0-2]';
sumPattern = '[0-2],[0-2],[0-2]';
}

return html `
<wizard-textfield label="${translate('locamation.vmu.ied.name')}"
.maybeValue=${iedHeader(ied)}
disabled>
</wizard-textfield>
<wizard-textfield label="${translate('locamation.vmu.ldevice.name')}"
.maybeValue=${lDeviceHeader(lDevice)}
disabled>
</wizard-textfield>
<wizard-textfield label="${translate('locamation.vmu.ln.name')}"
.maybeValue=${lnHeader(this.logicalNode, this.nsdoc)}
disabled>
</wizard-textfield>
<wizard-textfield label="${translate('locamation.vmu.version')}"
.maybeValue=${getPrivateTextValue(locamationPrivate, 'VERSION')}
disabled>
</wizard-textfield>
<wizard-textfield id="identifier"
label="${translate('locamation.vmu.identifier')}"
.maybeValue=${getPrivateTextValue(locamationPrivate, 'IDENTIFIER')}
helper="${translate('locamation.vmu.identifierHelper')}"
placeholder="134.12.213"
pattern="^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\\.(?!$)|$)){3}$"
required
dialogInitialFocus>
</wizard-textfield>
${hasPrivateElement(locamationPrivate, 'SUM') ?
html `<wizard-textfield id="sum"
label="${translate('locamation.vmu.sum')}"
.maybeValue=${getPrivateTextValue(locamationPrivate, 'SUM')}
helper="${translate('locamation.vmu.sumHelper')}"
placeholder="0,1,2"
pattern="${sumPattern}"
required>
</wizard-textfield>` : html ``
}
${hasPrivateElement(locamationPrivate, 'CHANNEL') ?
html `<wizard-textfield id="channel"
label="${translate('locamation.vmu.channel')}"
.maybeValue=${getPrivateTextValue(locamationPrivate, 'CHANNEL')}
helper="${translate('locamation.vmu.channelHelper')}"
pattern="${channelPattern}"
required>
</wizard-textfield>` : html ``
}
<wizard-textfield id="transformPrimary"
label="${translate('locamation.vmu.transformPrimary')}"
.maybeValue=${getPrivateTextValue(locamationPrivate, 'TRANSFORM-PRIMARY')}
helper="${translate('locamation.vmu.transformPrimaryHelper')}"
pattern="${patterns.unsigned}"
required>
</wizard-textfield>
<wizard-textfield id="transformSecondary"
label="${translate('locamation.vmu.transformSecondary')}"
.maybeValue=${getPrivateTextValue(locamationPrivate, 'TRANSFORM-SECONDARY')}
helper="${translate('locamation.vmu.transformSecondaryHelper')}"
pattern="${patterns.unsigned}"
required>
</wizard-textfield>
`;
}

static styles = css`
:host {
width: 20vw;
}
* {
display: block;
margin-top: 16px;
}
`
}

export function locamationLNEditWizard(logicalNode: Element, nsdoc: Nsdoc): Wizard {
function save() {
return function (inputs: WizardInput[], wizard: Element): WizardAction[] {
const locamationVMUEditElement = <LocamationVMUEditElement>wizard.shadowRoot!.querySelector('locamation-ln-edit')
return locamationVMUEditElement.save();
};
}

return [
{
title: get('locamation.vmu.ln.editTitle'),
primary: {
icon: 'save',
label: get('save'),
action: save(),
},
content: [
html`<locamation-ln-edit .logicalNode="${logicalNode}" .nsdoc="${nsdoc}"></locamation-ln-edit>`,
],
},
];
}

Loading

0 comments on commit ca428ac

Please sign in to comment.