Skip to content

Commit

Permalink
Add supervision indicator, closes openscd#1037
Browse files Browse the repository at this point in the history
  • Loading branch information
danyill committed Nov 3, 2022
1 parent 36ded24 commit d63a830
Show file tree
Hide file tree
Showing 13 changed files with 1,994 additions and 1,572 deletions.
53 changes: 53 additions & 0 deletions src/editors/subscription/foundation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Create,
createElement,
Delete,
findControlBlocks,
getSclSchemaVersion,
minAvailableLnInst,
} from '../../foundation.js';
Expand Down Expand Up @@ -343,6 +344,58 @@ export function findOrCreateAvailableLNInst(
return newElement;
}

export function getFirstSubscribedExtRef(
publishedControlBlock: Element,
subscribingIed: Element
): Element | null {
const publishingIed = publishedControlBlock.closest('IED')!;
const dataSet = publishingIed.querySelector(
`DataSet[name="${publishedControlBlock.getAttribute('datSet')}"]`
);
let extRef: Element | undefined = undefined;
Array.from(
subscribingIed?.querySelectorAll('LN0 > Inputs, LN > Inputs')
).some(inputs => {
Array.from(dataSet!.querySelectorAll('FCDA')).some(fcda => {
const anExtRef = getExtRef(inputs, fcda, publishedControlBlock);
if (anExtRef) {
extRef = anExtRef;
return true;
}
return false;
});
return extRef !== undefined;
});
return extRef !== undefined ? extRef : null;
}

/** Returns the subscriber's supervision LN for a given control block and extRef element
*
* @param extRef - The extRef SCL element in the subscribing IED.
* @param controlBlock - The publishing IED control block.
* @returns The supervision LN instance or null if not found
*/
export function getExistingSupervision(
extRef: Element | null,
controlBlock: Element
): Element | null {
if (extRef === null) return null;
const subscriberIED = extRef.closest('IED')!;
const supervisionType =
controlBlock.tagName === 'GSEControl' ? 'LGOS' : 'LSVS';
const refSelector =
supervisionType === 'LGOS' ? 'DOI[name="GoCBRef"]' : 'DOI[name="SvCBRef"]';
const candidateLns = Array.from(
subscriberIED.querySelectorAll(
`LN[lnClass="${supervisionType}"]>${refSelector}>DAI[name="setSrcRef"]>Val`
)
);
const foundLn = candidateLns.find(
supLn => supLn.textContent === controlBlockReference(controlBlock)
);
return foundLn !== undefined ? foundLn : null;
}

/**
* Counts the number of LN instances with proper supervision for the given control block set up.
*
Expand Down
27 changes: 26 additions & 1 deletion src/editors/subscription/goose/subscriber-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
property,
TemplateResult,
} from 'lit-element';
import { nothing } from 'lit-html';
import { get, translate } from 'lit-translate';

import '@material/mwc-icon';
Expand All @@ -29,7 +30,9 @@ import {
canCreateValidExtRef,
createExtRefElement,
existExtRef,
getExistingSupervision,
getExtRef,
getFirstSubscribedExtRef,
IEDSelectEvent,
instantiateSubscriptionSupervision,
ListElement,
Expand Down Expand Up @@ -310,14 +313,31 @@ export class SubscriberList extends SubscriberListContainer {
}

renderSubscriber(status: SubscribeStatus, element: Element): TemplateResult {
let firstSubscribedExtRef: Element | null = null;
let supervisionNode: Element | null = null;
if (status !== SubscribeStatus.None) {
if (view === View.PUBLISHER) {
firstSubscribedExtRef = getFirstSubscribedExtRef(
this.currentSelectedGseControl!,
element
);
supervisionNode = getExistingSupervision(firstSubscribedExtRef!, this.currentSelectedGseControl!)
} else {
firstSubscribedExtRef = getFirstSubscribedExtRef(
element,
this.currentSelectedIed!
);
supervisionNode = getExistingSupervision(firstSubscribedExtRef!, element)
}
}
return html` <mwc-list-item
@click=${() => {
this.dispatchEvent(
newGooseSubscriptionEvent(element, status ?? SubscribeStatus.None)
);
}}
graphic="avatar"
hasMeta
?hasMeta=${supervisionNode !== null}
>
<span
>${view == View.PUBLISHER
Expand All @@ -328,6 +348,11 @@ export class SubscriberList extends SubscriberListContainer {
<mwc-icon slot="graphic"
>${status == SubscribeStatus.Full ? html`clear` : html`add`}</mwc-icon
>
${supervisionNode !== null
? html`<mwc-icon title="${identity(supervisionNode)}" slot="meta"
>monitor_heart</mwc-icon
>`
: nothing}
</mwc-list-item>`;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
} from '../../../foundation.js';

import {
getExistingSupervision,
styles,
updateExtRefElement,
instantiateSubscriptionSupervision,
Expand Down Expand Up @@ -213,6 +214,34 @@ export class ExtRefLaterBindingList extends LitElement {
</h1>`;
}

private renderExtRefElement(extRefElement: Element): TemplateResult {
const supervisionNode = getExistingSupervision(
extRefElement,
<Element>this.currentSelectedControlElement,
);
return html` <mwc-list-item
graphic="large"
?hasMeta=${supervisionNode !== null}
twoline
@click=${() => this.unsubscribe(extRefElement)}
value="${identity(extRefElement)}"
>
<span>
${extRefElement.getAttribute('intAddr')}
${getDescriptionAttribute(extRefElement)
? html` (${getDescriptionAttribute(extRefElement)})`
: nothing}
</span>
<span slot="secondary">${identity(extRefElement)}</span>
<mwc-icon slot="graphic">swap_horiz</mwc-icon>
${supervisionNode !== null
? html`<mwc-icon title="${identity(supervisionNode)}" slot="meta"
>monitor_heart</mwc-icon
>`
: nothing}
</mwc-list-item>`;
}

private renderSubscribedExtRefs(): TemplateResult {
const subscribedExtRefs = this.getSubscribedExtRefElements();
return html`
Expand All @@ -231,22 +260,8 @@ export class ExtRefLaterBindingList extends LitElement {
</mwc-list-item>
<li divider role="separator"></li>
${subscribedExtRefs.length > 0
? html`${subscribedExtRefs.map(
extRefElement => html` <mwc-list-item
graphic="large"
twoline
@click=${() => this.unsubscribe(extRefElement)}
value="${identity(extRefElement)}"
>
<span>
${extRefElement.getAttribute('intAddr')}
${getDescriptionAttribute(extRefElement)
? html` (${getDescriptionAttribute(extRefElement)})`
: nothing}
</span>
<span slot="secondary">${identity(extRefElement)}</span>
<mwc-icon slot="graphic">swap_horiz</mwc-icon>
</mwc-list-item>`
? html`${subscribedExtRefs.map(extRefElement =>
this.renderExtRefElement(extRefElement)
)}`
: html`<mwc-list-item graphic="large" noninteractive>
${translate(
Expand Down
72 changes: 46 additions & 26 deletions src/editors/subscription/later-binding/ext-ref-ln-binding-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
state,
TemplateResult,
} from 'lit-element';
import { nothing } from 'lit-html';
import { get, translate } from 'lit-translate';

import {
Expand All @@ -25,6 +26,7 @@ import {
existExtRef,
FcdaSelectEvent,
getExtRef,
getExistingSupervision,
newSubscriptionChangedEvent,
removeSubscriptionSupervision,
instantiateSubscriptionSupervision,
Expand Down Expand Up @@ -220,6 +222,49 @@ export class ExtRefLnBindingList extends LitElement {
return html`<h1>${translate(`subscription.binding.extRefList.title`)}</h1>`;
}

private renderSubscribedLN(lnElement: Element): TemplateResult {
const extRefs = getSubscribedExtRefElements(
lnElement,
this.controlTag,
this.currentSelectedFcdaElement,
this.currentSelectedControlElement,
false
);
// can we always assume only one extRef?
const supervisionNode = getExistingSupervision(
extRefs[0],
<Element>this.currentSelectedControlElement
);
return html`<mwc-list-item
graphic="large"
?hasMeta=${supervisionNode !== null}
?disabled=${this.bindingNotSupported(lnElement)}
twoline
value="${identity(lnElement)}"
@click=${() => {
const replaceAction = this.unsubscribe(lnElement);
if (replaceAction) {
this.dispatchEvent(newActionEvent(replaceAction));
this.dispatchEvent(
newSubscriptionChangedEvent(
this.currentSelectedControlElement,
this.currentSelectedFcdaElement
)
);
}
}}
>
<span>${this.buildLNTitle(lnElement)}</span>
<span slot="secondary"> ${identity(lnElement.closest('LDevice'))} </span>
<mwc-icon slot="graphic">close</mwc-icon>
${supervisionNode !== null
? html`<mwc-icon title="${identity(supervisionNode)}" slot="meta"
>monitor_heart</mwc-icon
>`
: nothing}</mwc-list-item
>`;
}

private renderSubscribedLNs(): TemplateResult {
const subscribedLNs = this.getSubscribedLNElements();
return html`
Expand All @@ -238,32 +283,7 @@ export class ExtRefLnBindingList extends LitElement {
</mwc-list-item>
<li divider role="separator"></li>
${subscribedLNs.length > 0
? html`${subscribedLNs.map(
lnElement => html` <mwc-list-item
graphic="large"
?disabled=${this.bindingNotSupported(lnElement)}
twoline
value="${identity(lnElement)}"
@click=${() => {
const replaceAction = this.unsubscribe(lnElement);
if (replaceAction) {
this.dispatchEvent(newActionEvent(replaceAction));
this.dispatchEvent(
newSubscriptionChangedEvent(
this.currentSelectedControlElement,
this.currentSelectedFcdaElement
)
);
}
}}
>
<span>${this.buildLNTitle(lnElement)}</span>
<span slot="secondary">
${identity(lnElement.closest('LDevice'))}
</span>
<mwc-icon slot="graphic">close</mwc-icon>
</mwc-list-item>`
)}`
? html`${subscribedLNs.map(lN => this.renderSubscribedLN(lN))}`
: html`<mwc-list-item graphic="large" noninteractive>
${translate('subscription.binding.extRefList.noSubscribedLNs')}
</mwc-list-item>`}
Expand Down
29 changes: 27 additions & 2 deletions src/editors/subscription/sampledvalues/subscriber-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
property,
TemplateResult,
} from 'lit-element';
import { nothing } from 'lit-html';
import { get, translate } from 'lit-translate';

import '@material/mwc-icon';
Expand All @@ -29,7 +30,9 @@ import {
canCreateValidExtRef,
createExtRefElement,
existExtRef,
getExistingSupervision,
getExtRef,
getFirstSubscribedExtRef,
IEDSelectEvent,
instantiateSubscriptionSupervision,
ListElement,
Expand Down Expand Up @@ -258,7 +261,7 @@ export class SubscriberList extends SubscriberListContainer {
}
});

// we need to extend the actions array with the actions for the instation of the LSVS
// we need to extend the actions array with the actions for the instantiation of the LSVS
const supervisionActions: Create[] = instantiateSubscriptionSupervision(
this.currentSelectedSmvControl,
ied
Expand Down Expand Up @@ -312,14 +315,31 @@ export class SubscriberList extends SubscriberListContainer {
}

renderSubscriber(status: SubscribeStatus, element: Element): TemplateResult {
let firstSubscribedExtRef: Element | null = null;
let supervisionNode: Element | null = null;
if (status !== SubscribeStatus.None) {
if (view === View.PUBLISHER) {
firstSubscribedExtRef = getFirstSubscribedExtRef(
this.currentSelectedSmvControl!,
element
);
supervisionNode = getExistingSupervision(firstSubscribedExtRef!, this.currentSelectedSmvControl!)
} else {
firstSubscribedExtRef = getFirstSubscribedExtRef(
element,
this.currentSelectedIed!
);
supervisionNode = getExistingSupervision(firstSubscribedExtRef!, element)
}
}
return html` <mwc-list-item
@click=${() => {
this.dispatchEvent(
newSmvSubscriptionEvent(element, status ?? SubscribeStatus.None)
);
}}
graphic="avatar"
hasMeta
?hasMeta=${supervisionNode !== null}
>
<span
>${view == View.PUBLISHER
Expand All @@ -330,6 +350,11 @@ export class SubscriberList extends SubscriberListContainer {
<mwc-icon slot="graphic"
>${status == SubscribeStatus.Full ? html`clear` : html`add`}</mwc-icon
>
${supervisionNode !== null
? html`<mwc-icon title="${identity(supervisionNode)}" slot="meta"
>monitor_heart</mwc-icon
>`
: nothing}
</mwc-list-item>`;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describe('GOOSE subscriber plugin', () => {
let doc: XMLDocument;

beforeEach(async () => {
doc = await fetch('/test/testfiles/editors/MessageBindinGOOSE2007B4.scd')
doc = await fetch('/test/testfiles/editors/MessageBindingGOOSE2007B4.scd')
.then(response => response.text())
.then(str => new DOMParser().parseFromString(str, 'application/xml'));

Expand Down
Loading

0 comments on commit d63a830

Please sign in to comment.