Skip to content

Commit

Permalink
feat(editors/ied): add read only data model structure (#423)
Browse files Browse the repository at this point in the history
* Added IED Container

* Added tests

* Added Access Point container

* Added Server container

* Added LDevice container

* Added LN Container

* Added DO container

* Fixing unit tests IED container

* Fixing Unit tests AccessPoint container

* Fixing Unit tests Server container

* Fix unit tests LDevice container

* Added padding

* fix broken test

* Fixing LN container unit tests

* Added IED drop down

* Small addition

* Added integration test for IED filter

* Small refactoring

* IED list is now ordered alphabetically

* Refactoring

* Added check for available IEDs

* First batch of review adjustments

* Second batch of review stuff

* Some more refactoring

* Batch review comments

* Batch review comments

* refactor(editors/IED): add german translation

* feat(editors/ied): add DO elements to IED editor (#454)

* Added DO/SDO elements

* Added DO/SDO/DOI/SDI

* Added unit tests ln-container

* Made methods private in ln-container

* Added do-container snapshot tests

* Added batch of do-container unit tests

* Review comments

Co-authored-by: Rob Tjalma <rob@tjalma.com>

* Added basic DA container for DA/DAI show

* Added BDA container

* Added value css

* Added Enum container

* Refactoring

* Refactoring + Adding Unit Tests

* Added Enum Tests

* Small refactoring

* First review comments + Added toggle action

* Bugfix when showing DA data

* Small refactoring

* Added toggle test for ln-container

* refactor(ied/da-container,do-container): code improvements

* Added consistent toggle functionality

* Added da container unit tests

* Added da container unit tests

* Small typo

* Revert browserLogs to false

Co-authored-by: Jakob Vogelsang <jakob.vogelsang@omicronenergy.com>
Co-authored-by: Jakob Vogelsang <jakob-vogelsang@posteo.de>
  • Loading branch information
3 people authored Jan 11, 2022
1 parent dfae9b0 commit fa15c7a
Show file tree
Hide file tree
Showing 38 changed files with 2,268 additions and 91 deletions.
8 changes: 7 additions & 1 deletion public/js/plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ export const officialPlugins = [
default: true,
kind: 'editor',
},
{
name: 'IED',
src: '/src/editors/IED.js',
icon: 'edit',
default: false,
kind: 'editor',
},
{
name: 'Single Line Diagram',
src: '/src/editors/SingleLineDiagram.js',
Expand All @@ -19,7 +26,6 @@ export const officialPlugins = [
icon: 'settings_ethernet',
default: true,
kind: 'editor',

},
{
name: 'Templates',
Expand Down
98 changes: 98 additions & 0 deletions src/editors/IED.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { css, html, LitElement, property, query, state, TemplateResult } from 'lit-element';

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

import '../zeroline-pane.js';
import './ied/ied-container.js'

import { translate } from 'lit-translate';
import { Select } from '@material/mwc-select';
import { SingleSelectedEvent } from '@material/mwc-list/mwc-list-foundation';
import { compareNames, getDescriptionAttribute, getNameAttribute } from '../foundation.js';

/** An editor [[`plugin`]] for editing the `IED` section. */
export default class IedPlugin extends LitElement {
/** The document being edited as provided to plugins by [[`OpenSCD`]]. */
@property()
doc!: XMLDocument;

/** Query holding the current selected IEDs. */
@state()
currentSelectedIEDs = ':root > IED';

@query('#iedSelect') iedSelector?: Select;

private get alphabeticOrderedIeds() : Element[] {
return Array.from(this.doc?.querySelectorAll(':root > IED'))
.sort((a,b) => compareNames(a,b));
}

/**
* When selecting drop down, update the search query.
* Because an event only returns an index, we need to retrieve the
* actual IED before getting the actual value (in this case the name).
*/
private onSelect(event: SingleSelectedEvent): void {
const ied = this.alphabeticOrderedIeds[event.detail.index];
this.currentSelectedIEDs = `:root > IED[name="${getNameAttribute(ied)}"]`;
}

render(): TemplateResult {
return this.doc?.querySelector(':root > IED')
? html`<section>
<mwc-select
id="iedSelect"
label="${translate("iededitor.searchHelper")}"
@selected=${this.onSelect}>
${this.alphabeticOrderedIeds.map(
ied =>
html`<mwc-list-item
?selected=${ied == this.alphabeticOrderedIeds[0]}
value="${getNameAttribute(ied)}"
>${getNameAttribute(ied)} ${ied.hasAttribute('desc') ? translate('iededitor.searchHelperDesc', {
description: getDescriptionAttribute(ied)!,
}) : ''}
</mwc-list-item>`
)}
</mwc-select>
${Array.from(this.doc?.querySelectorAll(this.currentSelectedIEDs)).map(
ied => html`<ied-container
.element=${ied}
></ied-container>`
)}</section>`
: html`<h1>
<span style="color: var(--base1)"
>${translate('iededitor.missing')}</span
>
</h1>`;
}

static styles = css`
:host {
width: 100vw;
}
section {
padding: 8px 12px 16px;
}
#iedSelect {
width: 35vw;
padding-bottom: 20px;
}
h1 {
color: var(--mdc-theme-on-surface);
font-family: 'Roboto', sans-serif;
font-weight: 300;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
margin: 0px;
line-height: 48px;
padding-left: 0.3em;
}
`;
}
9 changes: 2 additions & 7 deletions src/editors/SingleLineDiagram.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,11 @@ import {
state,
TemplateResult,
} from 'lit-element';

import panzoom from 'panzoom';

import { identity, getPathNameAttribute, newWizardEvent, SCLTag, getNameAttribute, getDescriptionAttribute } from '../foundation.js';
import {
compareNames,
identity,
newWizardEvent,
SCLTag
} from '../foundation.js';
import {
getAbsolutePosition,
Expand All @@ -37,9 +34,6 @@ import {
import {
isBusBar,
getConnectedTerminals,
getPathNameAttribute,
getNameAttribute,
getDescriptionAttribute,
getCommonParentElement,
} from './singlelinediagram/foundation.js';
import {isSCLNamespace} from "../schemas.js";
Expand Down Expand Up @@ -545,3 +539,4 @@ export default class SingleLineDiagramPlugin extends LitElement {
}
`;
}

38 changes: 38 additions & 0 deletions src/editors/ied/access-point-container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {
css,
customElement,
html,
LitElement,
property,
TemplateResult,
} from 'lit-element';

import '../../action-pane.js';
import './server-container.js'
import { nothing } from 'lit-html';
import { getDescriptionAttribute, getNameAttribute } from '../../foundation.js';

/** [[`IED`]] plugin subeditor for editing `AccessPoint` element. */
@customElement('access-point-container')
export class AccessPointContainer extends LitElement {
@property({ attribute: false })
element!: Element;

private header(): TemplateResult {
const name = getNameAttribute(this.element);
const desc = getDescriptionAttribute(this.element);

return html`${name}${desc ? html` &mdash; ${desc}` : nothing}`;
}

render(): TemplateResult {
return html`<action-pane .label="${this.header()}">
${Array.from(this.element.querySelectorAll(':scope > Server')).map(
server => html`<server-container
.element=${server}
></server-container>`)}
</action-pane>`;
}

static styles = css``;
}
116 changes: 116 additions & 0 deletions src/editors/ied/da-container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import {
css,
customElement,
html,
LitElement,
property,
query,
TemplateResult,
} from 'lit-element';
import { nothing } from 'lit-html';
import { translate } from 'lit-translate';

import '@material/mwc-icon-button-toggle';
import { IconButtonToggle } from '@material/mwc-icon-button-toggle';

import '../../action-pane.js';
import { getNameAttribute } from '../../foundation.js';

/** [[`IED`]] plugin subeditor for editing `(B)DA` element. */
@customElement('da-container')
export class DAContainer extends LitElement {
/**
* The DA itself.
*/
@property({ attribute: false })
element!: Element;

/**
* The optional DAI of this (B)DA.
*/
@property({ attribute: false })
instanceElement!: Element;

@query('#toggleButton') toggleButton: IconButtonToggle | undefined;

private header(): TemplateResult {
const name = getNameAttribute(this.element);
const bType = this.element!.getAttribute('bType') ?? nothing;

if (this.instanceElement) {
return html`<b>${name}</b> &mdash; ${bType}`;
} else {
return html`${name} &mdash; ${bType}`;
}
}

/**
* Rendering an optional value of this (B)DA container.
* If there is a DAI, it get's priority on top of (B)DA values.
* @returns TemplateResult containing the value of the instance, element or nothing.
*/
private renderValue(): TemplateResult {
if (this.instanceElement) {
return html`${this.getValueElement(this.instanceElement)?.textContent}`
}

return html`${this.getValueElement(this.element)?.textContent}`;
}

/**
* Get the 'Val' element of another element.
* @param element - The element to search for an 'Val' element.
* @returns the 'Val' element, or null if not found.
*/
private getValueElement(element: Element): Element | null {
return element.querySelector('Val') ?? null;
}

/**
* Get the nested (B)DA element(s) if available.
* @returns The nested (B)DA element(s) of this (B)DA container.
*/
private getBDAElements(): Element[] {
const type = this.element!.getAttribute('type') ?? undefined;
const doType = this.element!.closest('SCL')!.querySelector(`:root > DataTypeTemplates > DAType[id="${type}"]`);
if (doType != null) {
return Array.from(doType!.querySelectorAll(':scope > BDA'))
}
return [];
}

render(): TemplateResult {
const bType = this.element!.getAttribute('bType');

return html`<action-pane .label="${this.header()}" icon="${this.instanceElement != null ? 'done' : ''}">
${bType == 'Struct' ? html`<abbr slot="action" title="${translate('iededitor.toggleChildElements')}">
<mwc-icon-button-toggle
id="toggleButton"
onIcon="keyboard_arrow_up"
offIcon="keyboard_arrow_down"
@click=${() => this.requestUpdate()}
></mwc-icon-button-toggle>
</abbr>` : nothing}
<h6>${this.renderValue()}</h6>
${this.toggleButton?.on && bType == 'Struct' ? this.getBDAElements().map(element =>
html`<da-container
.element=${element}>
</da-container>`) : nothing}
</action-pane>
`;
}

static styles = css`
h6 {
color: var(--mdc-theme-on-surface);
font-family: 'Roboto', sans-serif;
font-weight: 500;
font-size: 0.8em;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
margin: 0px;
padding-left: 0.3em;
}
`;
}
Loading

0 comments on commit fa15c7a

Please sign in to comment.