Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(editors): Changed selectors in Substation and IED Editors and updated IED and Substation Wizards. #671

Merged
merged 2 commits into from
Apr 11, 2022
Merged
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
46 changes: 24 additions & 22 deletions src/Editing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,30 +319,32 @@ export function Editing<TBase extends LitElementConstructor>(Base: TBase) {
private checkUpdateValidity(update: Update): boolean {
if (update.checkValidity !== undefined) return update.checkValidity();

const invalidNaming = Array.from(
update.element.parentElement?.children ?? []
).some(
elm =>
elm.tagName === update.element.tagName &&
elm.getAttribute('name') === update.newAttributes['name']
);

if (invalidNaming) {
this.dispatchEvent(
newLogEvent({
kind: 'error',
title: get('editing.error.update', {
name: update.element.tagName,
}),
message: get('editing.error.nameClash', {
parent: update.element.parentElement!.tagName,
child: update.element.tagName,
name: update.newAttributes['name']!,
}),
})
if (update.oldAttributes['name'] !== update.newAttributes['name']) {
const invalidNaming = Array.from(
update.element.parentElement?.children ?? []
).some(
elm =>
elm.tagName === update.element.tagName &&
elm.getAttribute('name') === update.newAttributes['name']
);

return false;
if (invalidNaming) {
this.dispatchEvent(
newLogEvent({
kind: 'error',
title: get('editing.error.update', {
name: update.element.tagName,
}),
message: get('editing.error.nameClash', {
parent: update.element.parentElement!.tagName,
child: update.element.tagName,
name: update.newAttributes['name']!,
}),
})
);

return false;
}
}

const invalidId =
Expand Down
26 changes: 12 additions & 14 deletions src/editors/IED.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ import { Nsdoc } from '../foundation/nsdoc.js';
* We need a variable outside the plugin to save the selected IED, because the Plugin is created
* more than once during working with the IED Editor, for instance when opening a Wizard to edit the IED.
*/
let iedEditorSelectedIedName: string | undefined;
let iedEditorSelectedIed: Element | undefined;
/*
* We will also add an Event Listener when a new document is opened. We then want to reset the selection
* so setting it to undefined will set the selected IED again on the first in the list.
*/
function onOpenDocResetSelectedIed() {
iedEditorSelectedIedName = undefined;
iedEditorSelectedIed = undefined;
}
addEventListener('open-doc', onOpenDocResetSelectedIed);

Expand All @@ -45,14 +45,17 @@ export default class IedPlugin extends LitElement {

@state()
private set selectedIed(element: Element | undefined) {
iedEditorSelectedIedName = (element) ? getNameAttribute(element) : undefined;
iedEditorSelectedIed = element;
}

private get selectedIed(): Element {
if (iedEditorSelectedIedName === undefined) {
iedEditorSelectedIedName = getNameAttribute(this.alphabeticOrderedIeds[0]);
private get selectedIed(): Element | undefined {
if (iedEditorSelectedIed === undefined) {
const iedList = this.alphabeticOrderedIeds;
if (iedList.length > 0) {
iedEditorSelectedIed = iedList[0];
}
}
return this.doc.querySelector(`:root > IED[name="${iedEditorSelectedIedName}"]`)!;
return iedEditorSelectedIed;
}

private onSelect(event: SingleSelectedEvent): void {
Expand All @@ -63,11 +66,6 @@ export default class IedPlugin extends LitElement {
render(): TemplateResult {
const iedList = this.alphabeticOrderedIeds;
if (iedList.length > 0) {
let selectedIedElement = this.selectedIed;
if (!selectedIedElement) {
// Fix: If the selected IED can't be found, because the name is changed, will select the first one again.
selectedIedElement = iedList[0];
}
return html `
<section>
<mwc-select
Expand All @@ -78,7 +76,7 @@ export default class IedPlugin extends LitElement {
ied =>
html`
<mwc-list-item
?selected=${ied == selectedIedElement}
?selected=${ied == this.selectedIed}
value="${getNameAttribute(ied)}"
>${getNameAttribute(ied)} ${ied.hasAttribute('desc') ? translate('iededitor.searchHelperDesc', {
description: getDescriptionAttribute(ied)!,
Expand All @@ -87,7 +85,7 @@ export default class IedPlugin extends LitElement {
)}
</mwc-select>
<ied-container
.element=${selectedIedElement}
.element=${this.selectedIed}
.nsdoc=${this.nsdoc}
></ied-container>
</section>`;
Expand Down
14 changes: 6 additions & 8 deletions src/editors/SingleLineDiagram.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ import '@material/mwc-textfield';
* We need a variable outside the plugin to save the selected substation, because the Plugin is created
* more than once during working with the SLD, for instance when opening a Wizard to edit equipment.
*/
let sldEditorSelectedSubstationName: string | undefined;
let sldEditorSelectedSubstation: Element | undefined;
/*
* We will also add an Event Listener when a new document is opened. We then want to reset the selection
* so setting it to undefined will set the selected Substation again on the first in the list.
*/
function onOpenDocResetSelectedSubstation() {
sldEditorSelectedSubstationName = undefined;
sldEditorSelectedSubstation = undefined;
}
addEventListener('open-doc', onOpenDocResetSelectedSubstation);

Expand All @@ -90,19 +90,17 @@ export default class SingleLineDiagramPlugin extends LitElement {

@state()
private set selectedSubstation(element: Element | undefined) {
sldEditorSelectedSubstationName = (element) ? getNameAttribute(element) : undefined;
sldEditorSelectedSubstation = element;
}

private get selectedSubstation(): Element | undefined {
if (sldEditorSelectedSubstationName === undefined) {
if (sldEditorSelectedSubstation === undefined) {
const substationList = this.substations;
if (substationList.length > 0) {
sldEditorSelectedSubstationName = getNameAttribute(substationList[0]);
sldEditorSelectedSubstation = substationList[0];
}
}
return (sldEditorSelectedSubstationName)
? this.doc.querySelector(`:root > Substation[name="${sldEditorSelectedSubstationName}"]`) ?? undefined
: undefined;
return sldEditorSelectedSubstation;
}

/**
Expand Down
3 changes: 2 additions & 1 deletion src/translations/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ export const de: Translations = {
},
action: {
addvoltagelevel: 'Spannungsebene hinzufügen',
updatesubstation: 'Schaltanlage "{{name}}" bearbeitet',
},
},
iededitor: {
Expand All @@ -209,7 +210,7 @@ export const de: Translations = {
},
},
action: {
updateied: 'IED "{{iedName}}" bearbeitet',
updateied: 'IED "{{name}}" bearbeitet',
},
},
powertransformer: {
Expand Down
3 changes: 2 additions & 1 deletion src/translations/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ export const en = {
},
action: {
addvoltagelevel: 'Add voltage level',
updatesubstation: 'Edited substation "{{name}}"',
},
},
iededitor: {
Expand All @@ -206,7 +207,7 @@ export const en = {
},
},
action: {
updateied: 'Edited IED "{{iedName}}"',
updateied: 'Edited IED "{{name}}"',
},
},
powertransformer: {
Expand Down
65 changes: 64 additions & 1 deletion src/wizards/foundation/actions.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import {
cloneElement,
ComplexAction,
createUpdateAction,
EditorAction,
getValue,
WizardActor,
WizardInputElement,
} from '../../foundation.js';
import { get } from "lit-translate";
import { updateReferences } from "./references.js";

export function updateNamingAction(element: Element): WizardActor {
return (inputs: WizardInputElement[]): EditorAction[] => {
Expand All @@ -14,11 +18,70 @@ export function updateNamingAction(element: Element): WizardActor {
if (
name === element.getAttribute('name') &&
desc === element.getAttribute('desc')
)
) {
return [];
}

const newElement = cloneElement(element, { name, desc });

return [{ old: { element }, new: { element: newElement } }];
};
}

export function updateNamingAttributeWithReferencesAction(
element: Element,
messageTitleKey: string
): WizardActor {
return (inputs: WizardInputElement[]): EditorAction[] => {
const newAttributes: Record<string, string | null> = {};
processNamingAttributes(newAttributes, element, inputs);
if (Object.keys(newAttributes).length == 0) {
return [];
}
addMissingAttributes(element, newAttributes)

const name = getValue(inputs.find(i => i.label === 'name')!)!;
const complexAction: ComplexAction = {
actions: [],
title: get(messageTitleKey, {name}),
};
complexAction.actions.push(createUpdateAction(
element,
newAttributes));
complexAction.actions.push(...updateReferences(
element,
element.getAttribute('name'),
name));
return complexAction.actions.length ? [complexAction] : [];
};
}

export function processNamingAttributes(
newAttributes: Record<string, string | null>,
element: Element,
inputs: WizardInputElement[]
): void {
const name = getValue(inputs.find(i => i.label === 'name')!)!;
const desc = getValue(inputs.find(i => i.label === 'desc')!);

if (name !== element.getAttribute('name')) {
newAttributes['name'] = name;
}
if (desc !== element.getAttribute('desc')) {
newAttributes['desc'] = desc;
}
}

export function addMissingAttributes(
element: Element,
newAttributes: Record<string, string | null>
): Record<string, string | null> {
const newAttributeKeys = Object.keys(newAttributes);
Array.from(element.attributes)
.filter(attr => !newAttributeKeys.includes(attr.name))
.forEach(attr => {
newAttributes[attr.name] = attr.value;
});

return newAttributes;
}
7 changes: 6 additions & 1 deletion src/wizards/foundation/references.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {isPublic, SimpleAction} from "../../foundation.js";

const referenceInfoTags = ['IED'] as const;
const referenceInfoTags = ['IED', 'Substation'] as const;
type ReferencesInfoTag = typeof referenceInfoTags[number];

/*
Expand Down Expand Up @@ -41,6 +41,11 @@ const referenceInfos: Record<
}, {
elementQuery: `SampledValueControl > IEDName`,
attribute: null
}],
Substation:
[{
elementQuery: `Terminal`,
attribute: 'substationName'
}]
}

Expand Down
45 changes: 7 additions & 38 deletions src/wizards/ied.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
import {html, TemplateResult} from 'lit-element';
import {get, translate} from 'lit-translate';
import { html, TemplateResult } from 'lit-element';
import { get, translate } from 'lit-translate';

import {
cloneElement,
ComplexAction,
EditorAction,
getValue,
isPublic,
Wizard,
WizardActor,
WizardInputElement,
} from '../foundation.js';
import {patterns} from "./foundation/limits.js";
import {updateReferences} from "./foundation/references.js";
import '../wizard-textfield.js';
import { isPublic, Wizard } from '../foundation.js';
import { patterns } from "./foundation/limits.js";
import { updateNamingAttributeWithReferencesAction } from "./foundation/actions.js";

const iedNamePattern = "[A-Za-z][0-9A-Za-z_]{0,2}|" +
"[A-Za-z][0-9A-Za-z_]{4,63}|" +
Expand All @@ -21,29 +13,6 @@ const iedNamePattern = "[A-Za-z][0-9A-Za-z_]{0,2}|" +
"No[0-9A-Za-mo-z_][0-9A-Za-z_]|" +
"Non[0-9A-Za-df-z_]";

export function updateIED(element: Element): WizardActor {
return (inputs: WizardInputElement[]): EditorAction[] => {
const name = getValue(inputs.find(i => i.label === 'name')!)!;
const oldName = element.getAttribute('name');
const desc = getValue(inputs.find(i => i.label === 'desc')!);

if ( name === oldName &&
desc === element.getAttribute('desc')) {
return [];
}

const complexAction: ComplexAction = {
actions: [],
title: get('ied.action.updateied', {iedName: name}),
};

const newElement = cloneElement(element, { name, desc });
complexAction.actions.push({ old: { element }, new: { element: newElement } });
complexAction.actions.push(...updateReferences(element, oldName, name));
return complexAction.actions.length ? [complexAction] : [];
};
}

export function renderIEDWizard(
name: string | null,
desc: string | null,
Expand Down Expand Up @@ -87,7 +56,7 @@ export function editIEDWizard(element: Element): Wizard {
primary: {
icon: 'edit',
label: get('save'),
action: updateIED(element),
action: updateNamingAttributeWithReferencesAction(element, 'ied.action.updateied'),
},
content: renderIEDWizard(
element.getAttribute('name'),
Expand Down
4 changes: 2 additions & 2 deletions src/wizards/substation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import {
WizardActor,
WizardInputElement,
} from '../foundation.js';
import { updateNamingAction } from './foundation/actions.js';
import { guessVoltageLevel } from '../editors/substation/guess-wizard.js';
import { updateNamingAttributeWithReferencesAction } from "./foundation/actions.js";

function render(
name: string,
Expand Down Expand Up @@ -97,7 +97,7 @@ export function substationEditWizard(element: Element): Wizard {
primary: {
icon: 'edit',
label: get('save'),
action: updateNamingAction(element),
action: updateNamingAttributeWithReferencesAction(element, 'substation.action.updatesubstation'),
},
content: render(
element.getAttribute('name') ?? '',
Expand Down
Loading