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(plugins/Subscription): Manage GOOSE service connections #569

Merged
merged 33 commits into from
Mar 17, 2022
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
b4090e0
Added event handling
Flurb Feb 22, 2022
d7497c5
Merge with base
Flurb Feb 23, 2022
0c79473
Intermediate commit
Flurb Feb 23, 2022
9cd2c9d
Merge
Flurb Feb 23, 2022
9d9a030
Intermediate commit
Flurb Feb 23, 2022
c7085b1
Small variable fix
Flurb Feb 23, 2022
1ff327f
Merge
Flurb Feb 23, 2022
8108f50
Refactor
Flurb Feb 23, 2022
4eda379
Merge
Flurb Feb 24, 2022
d1c7784
Merge branch 'subscriber-plugin' into manage-goose-connections
Flurb Feb 25, 2022
c076189
Refactoring
Flurb Feb 25, 2022
0416268
Create subscribe/unsubscribe actions
Flurb Feb 28, 2022
c79713d
Intermediate commit
Flurb Feb 28, 2022
40efed9
Intermediate commit
Flurb Feb 28, 2022
4c287c8
Intermediate commit
Flurb Feb 28, 2022
aa5eda8
Added correct subscribing/unsubscribing
Flurb Feb 28, 2022
894a3db
merge
Flurb Mar 2, 2022
dceae3d
Refactor
Flurb Mar 2, 2022
4d3ddff
Refactoring
Flurb Mar 2, 2022
b6e26fe
Added goose-message unit tests
Flurb Mar 2, 2022
89b03f6
Added ied-element unit tests
Flurb Mar 2, 2022
2b77b1f
Added publisher goose list unit tests
Flurb Mar 2, 2022
dfb04a8
Remove console.log
Flurb Mar 2, 2022
2d68e4d
Remove serviceType
Flurb Mar 2, 2022
be3e65c
Remove unused import
Flurb Mar 3, 2022
7918ed6
Added add/clear icon toggle to ied-element
Flurb Mar 3, 2022
fd2d02e
Fixing bug
Flurb Mar 3, 2022
4c7c432
Moving newGOOSESelectEvent dispatch
Flurb Mar 3, 2022
f08a7cb
Subscribe/Unsubscribe now work on both LN0 and LN
Flurb Mar 4, 2022
3a2e2a4
Refactoring
Flurb Mar 4, 2022
ba71a55
refactor: use complex actions to subscribe and unsubscribe
JakobVogelsang Mar 7, 2022
ba943f2
Merge
Flurb Mar 7, 2022
807e4d1
fix(plugins/Subscription): Empty 'Inputs' element after unsubscribing…
Flurb Mar 16, 2022
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
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import {
css,
customElement,
html,
LitElement,
property,
TemplateResult,
} from 'lit-element';
import { newGOOSESelectEvent } from '../../foundation.js';
import { gooseIcon } from '../../icons.js';

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

import { gooseIcon } from '../../../icons.js';
import { newGOOSESelectEvent } from '../foundation.js';

@customElement('goose-message')
export class GOOSEMessage extends LitElement {
Expand All @@ -17,24 +20,16 @@ export class GOOSEMessage extends LitElement {

private onGooseSelect = () => {
const ln = this.element.parentElement;
const dataset = ln?.querySelector(`DataSet[name=${this.element.getAttribute('datSet')}]`);
this.dispatchEvent(
newGOOSESelectEvent(
this.element.closest('IED')?.getAttribute('name') ?? '',
this.element,
dataset!
)
const dataset = ln?.querySelector(
`DataSet[name=${this.element.getAttribute('datSet')}]`
);
this.dispatchEvent(newGOOSESelectEvent(this.element, dataset!));
};

render(): TemplateResult {
return html`<mwc-list-item
@click=${this.onGooseSelect}
graphic="large">
return html`<mwc-list-item @click=${this.onGooseSelect} graphic="large">
<span>${this.element.getAttribute('name')}</span>
<mwc-icon slot="graphic">${gooseIcon}</mwc-icon>
</mwc-list-item>`;
}

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

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

import { newIEDSubscriptionEvent, SubscribeStatus } from '../foundation.js';

@customElement('ied-element')
export class IEDElement extends LitElement {
/** Holding the IED element */
@property({ attribute: false })
element!: Element;

@property({ attribute: false })
status!: SubscribeStatus;

private onIedSelect = () => {
this.dispatchEvent(
newIEDSubscriptionEvent(this.element, this.status ?? SubscribeStatus.None)
);
};

render(): TemplateResult {
return html`<mwc-list-item
@click=${this.onIedSelect}
graphic="avatar"
hasMeta
>
<span>${this.element.getAttribute('name')}</span>
<mwc-icon slot="graphic"
>${this.status == SubscribeStatus.Full
? html`clear`
: html`add`}</mwc-icon
>
</mwc-list-item>`;
}
}
115 changes: 115 additions & 0 deletions src/editors/subscription/foundation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { css } from 'lit-element';

/**
* Enumeration stating the Subscribe status of a IED to a GOOSE.
*/
export enum SubscribeStatus {
Full,
Partial,
None,
}

export interface GOOSESelectDetail {
gseControl: Element;
dataset: Element;
}
export type GOOSESelectEvent = CustomEvent<GOOSESelectDetail>;
export function newGOOSESelectEvent(
gseControl: Element,
dataset: Element,
eventInitDict?: CustomEventInit<GOOSESelectDetail>
): GOOSESelectEvent {
return new CustomEvent<GOOSESelectDetail>('goose-dataset', {
bubbles: true,
composed: true,
...eventInitDict,
detail: { gseControl, dataset, ...eventInitDict?.detail },
});
}

export interface IEDSubscriptionDetail {
element: Element;
subscribeStatus: SubscribeStatus;
}
export type IEDSubscriptionEvent = CustomEvent<IEDSubscriptionDetail>;
export function newIEDSubscriptionEvent(
element: Element,
subscribeStatus: SubscribeStatus
): IEDSubscriptionEvent {
return new CustomEvent<IEDSubscriptionDetail>('ied-subscription', {
bubbles: true,
composed: true,
detail: { element, subscribeStatus },
});
}

/** Common `CSS` styles used by DataTypeTemplate subeditors */
export const styles = css`
:host(.moving) section {
opacity: 0.3;
}

section {
background-color: var(--mdc-theme-surface);
transition: all 200ms linear;
outline-color: var(--mdc-theme-primary);
outline-style: solid;
outline-width: 0px;
opacity: 1;
}

section:focus {
box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14),
0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2);
}

section:focus-within {
outline-width: 2px;
transition: all 250ms linear;
}

h1,
h2,
h3 {
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;
transition: background-color 150ms linear;
}

section:focus-within > h1,
section:focus-within > h2,
section:focus-within > h3 {
color: var(--mdc-theme-surface);
background-color: var(--mdc-theme-primary);
transition: background-color 200ms linear;
}

h1 > nav,
h2 > nav,
h3 > nav,
h1 > abbr > mwc-icon-button,
h2 > abbr > mwc-icon-button,
h3 > abbr > mwc-icon-button {
float: right;
}

abbr[title] {
border-bottom: none !important;
cursor: inherit !important;
text-decoration: none !important;
}
`;

declare global {
interface ElementEventMap {
['goose-dataset']: GOOSESelectEvent;
['ied-subscription']: IEDSubscriptionEvent;
}
}
64 changes: 36 additions & 28 deletions src/editors/subscription/publisher-goose-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,27 @@ import {
property,
TemplateResult,
} from 'lit-element';
import { translate } from 'lit-translate';

import './goose-message.js';
import '@material/mwc-icon';
import '@material/mwc-list';
import '@material/mwc-list/mwc-list-item';

import { translate } from 'lit-translate';
import './elements/goose-message.js';
import { compareNames, getNameAttribute } from '../../foundation.js';
import { styles } from '../templates/foundation.js';
import { styles } from './foundation.js';

/** An sub element for showing all published GOOSE messages per IED. */
@customElement('publisher-goose-list')
export class PublisherGOOSEList extends LitElement {
@property()
@property({ attribute: false })
doc!: XMLDocument;

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

Expand All @@ -30,37 +35,40 @@ export class PublisherGOOSEList extends LitElement {
* @param ied - The IED to search through.
* @returns All the published GOOSE messages of this specific IED.
*/
private getGSEControls(ied: Element) : Element[] {
return Array.from(ied.querySelectorAll(':scope > AccessPoint > Server > LDevice > LN0[lnClass="LLN0"] > GSEControl'));
private getGSEControls(ied: Element): Element[] {
return Array.from(
ied.querySelectorAll(
':scope > AccessPoint > Server > LDevice > LN0 > GSEControl'
)
);
}

render(): TemplateResult {
return html`
<section>
return html` <section>
<h1>${translate('subscription.publisherGoose.title')}</h1>
<mwc-list>
${this.ieds.map(ied =>
ied.querySelector('GSEControl') ?
html`
<mwc-list-item noninteractive graphic="icon">
<span class="iedListTitle">${getNameAttribute(ied)}</span>
<mwc-icon slot="graphic">developer_board</mwc-icon>
</mwc-list-item>
<li divider role="separator"></li>
${this.getGSEControls(ied).map(control =>
html`<goose-message
.element=${control}
></goose-message>`)}
` : ``
)
}
</mwc-list>
</section>`;
ied.querySelector('GSEControl')
? html`
<mwc-list-item noninteractive graphic="icon">
<span class="iedListTitle">${getNameAttribute(ied)}</span>
<mwc-icon slot="graphic">developer_board</mwc-icon>
</mwc-list-item>
<li divider role="separator"></li>
${this.getGSEControls(ied).map(
control =>
html`<goose-message .element=${control}></goose-message>`
)}
`
: ``
)}
</mwc-list>
</section>`;
}

static styles = css`
${styles}

mwc-list {
height: 100vh;
overflow-y: scroll;
Expand Down
Loading