Skip to content

Commit

Permalink
feat(editor/ied): Add wizard/action to remove IED including references (
Browse files Browse the repository at this point in the history
#732)

* Add action to remove IED, including references.
* Updated German translations.
* Fixed small issues.
* Added remove button to IED Editor.
* Refactor cleanup Inputs and add logic to removing IED.
* Updated comments.
  • Loading branch information
Dennis Labordus authored May 16, 2022
1 parent 5c19822 commit bba9b3c
Show file tree
Hide file tree
Showing 19 changed files with 677 additions and 236 deletions.
4 changes: 3 additions & 1 deletion src/editors/IED.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ export default class IedPlugin extends LitElement {
}

private get selectedIed(): Element | undefined {
if (iedEditorSelectedIed === undefined) {
// When there is no IED selected, or the selected IED has no parent (IED has been removed)
// select the first IED from the List.
if (iedEditorSelectedIed === undefined || iedEditorSelectedIed.parentElement === null) {
const iedList = this.alphabeticOrderedIeds;
if (iedList.length > 0) {
iedEditorSelectedIed = iedList[0];
Expand Down
64 changes: 46 additions & 18 deletions src/editors/ied/ied-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@ import {
import { nothing } from 'lit-html';
import { translate } from "lit-translate";

import {wizards} from "../../wizards/wizard-library.js";
import '../../action-pane.js';
import './access-point-container.js';

import { wizards } from "../../wizards/wizard-library.js";
import { Nsdoc } from '../../foundation/nsdoc.js';
import { getDescriptionAttribute, getNameAttribute, newWizardEvent } from '../../foundation.js';
import {
getDescriptionAttribute,
getNameAttribute,
newActionEvent,
newWizardEvent} from '../../foundation.js';
import { removeIEDWizard } from "../../wizards/ied.js";

/** [[`IED`]] plugin subeditor for editing `IED` element. */
@customElement('ied-container')
Expand All @@ -24,12 +30,22 @@ export class IedContainer extends LitElement {

@property()
nsdoc!: Nsdoc;

private openEditWizard(): void {
const wizard = wizards['IED'].edit(this.element);
if (wizard) this.dispatchEvent(newWizardEvent(wizard));
}

private removeIED(): void {
const wizard = removeIEDWizard(this.element);
if (wizard) {
this.dispatchEvent(newWizardEvent(() => wizard));
} else {
// If no Wizard is needed, just remove the element.
this.dispatchEvent(newActionEvent({ old: { parent: this.element.parentElement!, element: this.element } }));
}
}

private header(): TemplateResult {
const name = getNameAttribute(this.element);
const desc = getDescriptionAttribute(this.element);
Expand All @@ -38,22 +54,34 @@ export class IedContainer extends LitElement {
}

render(): TemplateResult {
return html`<action-pane .label="${this.header()}">
<mwc-icon slot="icon">developer_board</mwc-icon>
<abbr slot="action" title="${translate('edit')}">
<mwc-icon-button
icon="edit"
@click=${() => this.openEditWizard()}
></mwc-icon-button>
</abbr>
${Array.from(this.element.querySelectorAll(':scope > AccessPoint')).map(
ap => html`<access-point-container
.element=${ap}
.nsdoc=${this.nsdoc}
.ancestors=${[this.element]}
></access-point-container>`)}
return html`
<action-pane .label="${this.header()}">
<mwc-icon slot="icon">developer_board</mwc-icon>
<abbr slot="action" title="${translate('remove')}">
<mwc-icon-button
icon="delete"
@click=${() => this.removeIED()}
></mwc-icon-button>
</abbr>
<abbr slot="action" title="${translate('edit')}">
<mwc-icon-button
icon="edit"
@click=${() => this.openEditWizard()}
></mwc-icon-button>
</abbr>
${Array.from(this.element.querySelectorAll(':scope > AccessPoint')).map(
ap => html`<access-point-container
.element=${ap}
.nsdoc=${this.nsdoc}
.ancestors=${[this.element]}
></access-point-container>`)}
</action-pane>`;
}

static styles = css``;
static styles = css`
abbr {
text-decoration: none;
border-bottom: none;
}
`;
}
80 changes: 8 additions & 72 deletions src/editors/sampledvalues/subscriber-ied-list-smv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,18 @@ import {
Create,
createElement,
Delete,
identity,
newActionEvent,
selector,
} from '../../foundation.js';
import {
SampledValuesSelectEvent,
IEDSampledValuesSubscriptionEvent,
styles,
SubscribeStatus,
} from './foundation.js';
import {
emptyInputsDeleteActions,
getFcdaReferences
} from "../../foundation/ied.js";

/**
* An IED within this IED list has 2 properties:
Expand All @@ -39,33 +41,6 @@ interface IED {
partial?: boolean;
}

/**
* All available FCDA references that are used to link ExtRefs.
*/
const fcdaReferences = [
'ldInst',
'lnClass',
'lnInst',
'prefix',
'doName',
'daName',
];

/**
* Get all the FCDA attributes containing values from a specific element.
* @param elementContainingFcdaReferences - The element to use
* @returns FCDA references
*/
function getFcdaReferences(elementContainingFcdaReferences: Element): string {
return fcdaReferences
.map(fcdaRef =>
elementContainingFcdaReferences.getAttribute(fcdaRef)
? `[${fcdaRef}="${elementContainingFcdaReferences.getAttribute(fcdaRef)}"]`
: ''
)
.join('');
}

/** An sub element for subscribing and unsubscribing IEDs to Sampled Values messages. */
@customElement('subscriber-ied-list-smv')
export class SubscriberIEDListSmv extends LitElement {
Expand Down Expand Up @@ -261,56 +236,17 @@ export class SubscriberIEDListSmv extends LitElement {
});
});

// Check if empty Input Element should also be removed.
actions.push(...emptyInputsDeleteActions(actions));

this.dispatchEvent(
newActionEvent({
title: 'Disconnect',
actions: this.extendDeleteActions(actions),
actions: actions,
})
);
}

/**
* Creating Delete actions in case Inputs elements are empty.
* @param extRefDeleteActions - All Delete actions for ExtRefs.
* @returns Possible delete actions for empty Inputs elements.
*/
private extendDeleteActions(extRefDeleteActions: Delete[]): Delete[] {
if (!extRefDeleteActions.length) return [];

// Initialize with the already existing ExtRef Delete actions.
const extendedDeleteActions: Delete[] = extRefDeleteActions;
const inputsMap: Record<string, Element> = {};

for (const extRefDeleteAction of extRefDeleteActions) {
const extRef = <Element>extRefDeleteAction.old.element;
const inputsElement = <Element>extRefDeleteAction.old.parent;

const id = identity(inputsElement);
if (!inputsMap[id]) inputsMap[id] = <Element>(inputsElement.cloneNode(true));

const linkedExtRef = inputsMap[id].querySelector(`ExtRef[iedName=${extRef.getAttribute('iedName')}]` +
`${getFcdaReferences(extRef)}`);

if (linkedExtRef) inputsMap[id].removeChild(linkedExtRef);
}

// create delete action for each empty inputs
Object.entries(inputsMap).forEach(([key, value]) => {
if (value.children.length ! == 0) {
const doc = extRefDeleteActions[0].old.parent.ownerDocument!;
const inputs = doc.querySelector(selector('Inputs', key));

if (inputs && inputs.parentElement) {
extendedDeleteActions.push({
old: { parent: inputs.parentElement, element: inputs },
});
}
}
});

return extendedDeleteActions;
}

protected updated(): void {
if (this.subscriberWrapper) {
this.subscriberWrapper.scrollTo(0, 0);
Expand Down
85 changes: 8 additions & 77 deletions src/editors/subscription/subscriber-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ import {
Create,
createElement,
Delete,
identity,
newActionEvent,
selector,
} from '../../foundation.js';
import {
newSubscriptionEvent,
Expand All @@ -32,6 +30,10 @@ import {
View,
ViewEvent,
} from './foundation.js';
import {
emptyInputsDeleteActions,
getFcdaReferences
} from "../../foundation/ied.js";

/**
* An element within this list has 2 properties:
Expand All @@ -43,35 +45,6 @@ interface ListElement {
partial?: boolean;
}

/**
* All available FCDA references that are used to link ExtRefs.
*/
const fcdaReferences = [
'ldInst',
'lnClass',
'lnInst',
'prefix',
'doName',
'daName',
];

/**
* Get all the FCDA attributes containing values from a specific element.
* @param elementContainingFcdaReferences - The element to use
* @returns FCDA references
*/
function getFcdaReferences(elementContainingFcdaReferences: Element): string {
return fcdaReferences
.map(fcdaRef =>
elementContainingFcdaReferences.getAttribute(fcdaRef)
? `[${fcdaRef}="${elementContainingFcdaReferences.getAttribute(
fcdaRef
)}"]`
: ''
)
.join('');
}

/** Defining view outside the class, which makes it persistent. */
let view: View = View.GOOSE_PUBLISHER;

Expand Down Expand Up @@ -351,59 +324,17 @@ export class SubscriberList extends LitElement {
});
});

// Check if empty Input Element should also be removed.
actions.push(...emptyInputsDeleteActions(actions));

this.dispatchEvent(
newActionEvent({
title: 'Disconnect',
actions: this.extendDeleteActions(actions),
actions: actions,
})
);
}

/**
* Creating Delete actions in case Inputs elements are empty.
* @param extRefDeleteActions - All Delete actions for ExtRefs.
* @returns Possible delete actions for empty Inputs elements.
*/
private extendDeleteActions(extRefDeleteActions: Delete[]): Delete[] {
if (!extRefDeleteActions.length) return [];

// Initialize with the already existing ExtRef Delete actions.
const extendedDeleteActions: Delete[] = extRefDeleteActions;
const inputsMap: Record<string, Element> = {};

for (const extRefDeleteAction of extRefDeleteActions) {
const extRef = <Element>extRefDeleteAction.old.element;
const inputsElement = <Element>extRefDeleteAction.old.parent;

const id = identity(inputsElement);
if (!inputsMap[id])
inputsMap[id] = <Element>inputsElement.cloneNode(true);

const linkedExtRef = inputsMap[id].querySelector(
`ExtRef[iedName=${extRef.getAttribute('iedName')}]` +
`${getFcdaReferences(extRef)}`
);

if (linkedExtRef) inputsMap[id].removeChild(linkedExtRef);
}

// create delete action for each empty inputs
Object.entries(inputsMap).forEach(([key, value]) => {
if (value.children.length! == 0) {
const doc = extRefDeleteActions[0].old.parent.ownerDocument!;
const inputs = doc.querySelector(selector('Inputs', key));

if (inputs && inputs.parentElement) {
extendedDeleteActions.push({
old: { parent: inputs.parentElement, element: inputs },
});
}
}
});

return extendedDeleteActions;
}

private resetElements() {
this.subscribedElements = [];
this.availableElements = [];
Expand Down
26 changes: 22 additions & 4 deletions src/editors/substation/ied-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ import '../../action-icon.js';
import { createClientLnWizard } from '../../wizards/clientln.js';
import { gooseIcon, smvIcon, reportIcon } from '../../icons/icons.js';
import { wizards } from '../../wizards/wizard-library.js';
import { newWizardEvent } from '../../foundation.js';
import { newActionEvent, newWizardEvent } from '../../foundation.js';
import { selectGseControlWizard } from '../../wizards/gsecontrol.js';
import { selectSampledValueControlWizard } from '../../wizards/sampledvaluecontrol.js';
import { selectReportControlWizard } from '../../wizards/reportcontrol.js';
import { removeIEDWizard } from "../../wizards/ied.js";

/** [[`SubstationEditor`]] subeditor for a child-less `IED` element. */
@customElement('ied-editor')
Expand Down Expand Up @@ -65,6 +66,16 @@ export class IedEditor extends LitElement {
if (wizard) this.dispatchEvent(newWizardEvent(wizard));
}

private removeIED(): void {
const wizard = removeIEDWizard(this.element);
if (wizard) {
this.dispatchEvent(newWizardEvent(() => wizard));
} else {
// If no Wizard is needed, just remove the element.
this.dispatchEvent(newActionEvent({ old: { parent: this.element.parentElement!, element: this.element } }));
}
}

render(): TemplateResult {
return html`<action-icon label="${this.name}" icon="developer_board">
<mwc-fab
Expand All @@ -76,10 +87,11 @@ export class IedEditor extends LitElement {
></mwc-fab
><mwc-fab
slot="action"
class="selectgse"
class="delete"
mini
@click="${() => this.openGseControlSelection()}"
><mwc-icon slot="icon">${gooseIcon}</mwc-icon></mwc-fab
@click="${() => this.removeIED() }"
icon="delete"
></mwc-fab
><mwc-fab
slot="action"
class="selectreport"
Expand All @@ -99,6 +111,12 @@ export class IedEditor extends LitElement {
@click="${() => this.openCommunicationMapping()}"
icon="add_link"
></mwc-fab
><mwc-fab
slot="action"
class="selectgse"
mini
@click="${() => this.openGseControlSelection()}"
><mwc-icon slot="icon">${gooseIcon}</mwc-icon></mwc-fab
></action-icon> `;
}
}
Loading

0 comments on commit bba9b3c

Please sign in to comment.