Skip to content

Commit

Permalink
feat(104/values): Added List of IEDs and 104-related DAI to values sc…
Browse files Browse the repository at this point in the history
…reen.
  • Loading branch information
Dennis Labordus authored May 19, 2022
1 parent 3364eab commit adbc4a4
Show file tree
Hide file tree
Showing 21 changed files with 2,126 additions and 226 deletions.
2 changes: 1 addition & 1 deletion public/js/plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const officialPlugins = [
},
{
name: '104',
src: '/src/editors/Communication104.js',
src: '/src/editors/Protocol104.js',
icon: 'settings_ethernet',
default: false,
kind: 'editor',
Expand Down
106 changes: 0 additions & 106 deletions src/editors/Communication104.ts

This file was deleted.

97 changes: 97 additions & 0 deletions src/editors/Protocol104.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import {
css,
html,
LitElement,
property,
query,
TemplateResult
} from "lit-element";
import {translate} from "lit-translate";

import '@material/mwc-fab';
import '@material/mwc-radio';
import '@material/mwc-formfield';

import {RadioListItem} from "@material/mwc-list/mwc-radio-list-item";

import './protocol104/network-container.js'
import './protocol104/values-container.js'

import {
newViewEvent,
View,
VIEW_EVENT_NAME,
ViewEvent
} from "./protocol104/foundation/foundation.js";

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

export default class Communication104Plugin extends LitElement {
@property()
doc!: XMLDocument;

@query('#byValuesRadio')
byValuesRadio!: RadioListItem;

@query('#byNetworkRadio')
byNetworkRadio!: RadioListItem;

@query('div#containers')
listDiv!: Element;

constructor() {
super();

this.addEventListener(VIEW_EVENT_NAME, (evt: ViewEvent) => {
selectedViewProtocol104Plugin = evt.detail.view;
this.requestUpdate();
});
}

firstUpdated(): void {
selectedViewProtocol104Plugin == View.VALUES
? this.byValuesRadio.setAttribute('checked', '')
: this.byNetworkRadio.setAttribute('checked', '')
}

render(): TemplateResult {
return html `
<section>
<div>
<mwc-formfield label="${translate('protocol104.view.valuesView')}">
<mwc-radio
id="byValuesRadio"
name="view"
value="values"
@checked=${() => this.listDiv.dispatchEvent(newViewEvent(View.VALUES))}
></mwc-radio>
</mwc-formfield>
<mwc-formfield label="${translate('protocol104.view.networkView')}">
<mwc-radio
id="byNetworkRadio"
name="view"
value="network"
@checked=${() => this.listDiv.dispatchEvent(newViewEvent(View.NETWORK))}
></mwc-radio>
</mwc-formfield>
<div id="containers">
${selectedViewProtocol104Plugin == View.VALUES
? html `<values-104-container .doc=${this.doc}></values-104-container>`
: html `<network-104-container .doc=${this.doc}></network-104-container>`
}
</div>
</div>
</section>`;
}

static styles = css`
:host {
width: 100vw;
}
section {
padding: 8px 12px 16px;
}
`;
}
32 changes: 0 additions & 32 deletions src/editors/communication104/foundation/foundation.ts

This file was deleted.

13 changes: 0 additions & 13 deletions src/editors/communication104/values-container.ts

This file was deleted.

100 changes: 100 additions & 0 deletions src/editors/protocol104/foundation/foundation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import {
getInstanceAttribute,
getNameAttribute
} from "../../../foundation.js";

export const PRIVATE_TYPE_104 = "IEC_60870_5_104";

/**
* Retrieve the full path as wanted for the IED Container in the 104 Plugin, meaning we go higher in the
* hierarchy until the parent found is the IED, this element is excluded, because the containers are group per
* IED.
* From all parent between the DAI and IED the name or likely attributes are used to define a unique name.
*
* @param daiElement - The DAI Element for which the full path needs to be defined.
* @returns The full path shown to the user for a DAI Element.
*/
export function getFullPath(daiElement: Element): string {
let path = daiElement.getAttribute('name') ?? '';
let parent = daiElement.parentElement;

while (parent && parent.tagName != 'IED') {
let value: string | undefined;
switch (parent.tagName) {
case 'LN':
case 'LN0': {
const prefix = parent.getAttribute('prefix');
const inst = getInstanceAttribute(parent);
value = `${prefix ? prefix + '-' : ''}${parent.getAttribute('lnClass')}${inst ? '-' + inst : ''}`;
break;
}
case 'LDevice': {
value = getNameAttribute(parent) ?? getInstanceAttribute(parent);
break;
}
default: {
// Just add the name to the list
value = getNameAttribute(parent);
}
}
path = (value ? value + ' / ' : '') + path;
parent = parent.parentElement;
}
return path;
}

/**
* Retrieve the CDC Value that belongs to a DAI Element, meaning, using the DOI/LN Elements to
* search for a DO Element, which is again used to find the DO/DOType Element. The DOType Element
* finally holds the attribute 'cdc'.
*
* @param daiElement - The DAI Element to start the search for the CDC Value.
* @returns The CDC Value from the DOType Element.
*/
export function getCdcValue(daiElement: Element): string | null {
const lnElement = daiElement.closest('LN0, LN');
const doiElement = daiElement.closest('DOI');
if (lnElement && doiElement) {
const lnType = lnElement.getAttribute('lnType');
const doName = doiElement.getAttribute('name');

const doElement = daiElement.ownerDocument.querySelector(`LNodeType[id="${lnType}"] > DO[name="${doName}"]`)
if (doElement) {
const doType = doElement.getAttribute('type');
const doTypeElement = daiElement.ownerDocument.querySelector(`DOType[id="${doType}"]`)
return (doTypeElement ? doTypeElement.getAttribute('cdc') : null);
}
}
return null;
}

/**
* Enumeration stating the active view of the 104 plugin.
*/
export enum View {
VALUES,
NETWORK
}

export const VIEW_EVENT_NAME = 'view-change-104-plugin';

// Objects needed to register and fire the change of a view within the Communication 104 Plugin
export interface ViewDetail {
view: View;
}
export type ViewEvent = CustomEvent<ViewDetail>;
export function newViewEvent(
view: View,
): ViewEvent {
return new CustomEvent<ViewDetail>(VIEW_EVENT_NAME, {
bubbles: true,
composed: true,
detail: { view },
});
}

declare global {
interface ElementEventMap {
[VIEW_EVENT_NAME]: ViewEvent;
}
}
Loading

0 comments on commit adbc4a4

Please sign in to comment.