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

feat(editor/ied): Add wizard/action to remove IED including references #732

Merged
merged 8 commits into from
May 16, 2022
Merged
Show file tree
Hide file tree
Changes from 7 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
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 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 @@ -348,59 +321,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