Skip to content

Commit 4ab72a8

Browse files
Merge pull request #220 from com-pas/Sitipe_Plugin
Sitipe plugin
2 parents 12be17b + 250fddf commit 4ab72a8

File tree

11 files changed

+58796
-10
lines changed

11 files changed

+58796
-10
lines changed

public/js/plugins.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,13 @@ export const officialPlugins = [
318318
default: false,
319319
kind: 'menu',
320320
requireDoc: true,
321-
position: 'middle'
322-
}
321+
position: 'middle',
322+
},
323+
{
324+
name: 'Sitipe',
325+
src: '/src/editors/Sitipe.js',
326+
icon: 'conveyor_belt',
327+
default: true,
328+
kind: 'editor',
329+
},
323330
];

src/editors/Sitipe.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { css, html, LitElement, property, TemplateResult } from 'lit-element';
2+
import { translate } from 'lit-translate';
3+
4+
import './sitipe/sitipe-substation.js';
5+
import { isPublic } from '../foundation.js';
6+
7+
/** An editor [[`plugin`]] for Sitipe based configuration */
8+
export default class SitipePlugin extends LitElement {
9+
@property({ attribute: false })
10+
doc!: XMLDocument;
11+
12+
header(): string {
13+
return 'Sitipe';
14+
}
15+
16+
private renderSubstations(): TemplateResult {
17+
return html`${this.doc?.querySelector(':root > Substation')
18+
? html`<section>
19+
${Array.from(this.doc.querySelectorAll('Substation') ?? [])
20+
.filter(isPublic)
21+
.map(
22+
substation =>
23+
html`<sitipe-substation
24+
.doc=${this.doc}
25+
.element=${substation}
26+
></sitipe-substation>`
27+
)}
28+
</section>`
29+
: html`<h1>
30+
<span style="color: var(--base1)"
31+
>${translate('substation.missing')}</span
32+
>
33+
</h1>`}`;
34+
}
35+
36+
render(): TemplateResult {
37+
return html`<div class="container">${this.renderSubstations()}</div>`;
38+
}
39+
40+
static styles = css`
41+
:host {
42+
width: 100vw;
43+
padding: 16px;
44+
}
45+
46+
.container {
47+
display: flex;
48+
padding: 8px 6px 16px;
49+
height: calc(100vh - 136px);
50+
box-sizing: border-box;
51+
width: 100%;
52+
}
53+
section {
54+
flex: 1;
55+
}
56+
`;
57+
}

src/editors/sitipe/foundation.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Substation element hierarchy
2+
const substationPath = [':root', 'Substation', 'VoltageLevel', 'Bay'];
3+
4+
export type SubstationTag = 'Substation' | 'VoltageLevel' | 'Bay';
5+
6+
/** `Private`-safeguarded selectors for `Substation` and its descendants */
7+
export const selectors = <Record<SubstationTag, string>>(
8+
Object.fromEntries(
9+
substationPath.map((e, i, a) => [e, a.slice(0, i + 1).join(' > ')])
10+
)
11+
);
12+
13+
export const SIEMENS_SITIPE_IED_REF = 'Siemens-SITIPE-IEDRef';
14+
15+
export const SIEMENS_SITIPE_BAY_TEMPLATE = 'Siemens-SITIPE-BayTemplate';
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import {
2+
css,
3+
customElement,
4+
html,
5+
LitElement,
6+
property,
7+
state,
8+
TemplateResult,
9+
} from 'lit-element';
10+
11+
import '../../action-pane.js';
12+
import '../../action-icon.js';
13+
14+
import {
15+
selectors,
16+
SIEMENS_SITIPE_IED_REF,
17+
SIEMENS_SITIPE_BAY_TEMPLATE,
18+
} from './foundation.js';
19+
20+
/** [[`Sitipe`]] plugin subeditor for editing `Sitipe` configuration. */
21+
@customElement('sitipe-substation')
22+
export class SitipeSubstation extends LitElement {
23+
/** The document being edited as provided to editor by [[`Sitipe`]]. */
24+
@property({ attribute: false })
25+
doc!: XMLDocument;
26+
/** The edited `Element`, a common property of all Sitipe subeditors. */
27+
@property({ attribute: false })
28+
element!: Element;
29+
30+
@state()
31+
get substationHeader(): string {
32+
const name = this.element.getAttribute('name') ?? '';
33+
const desc = this.element.getAttribute('desc');
34+
35+
return `${name} ${desc ? `- ${desc}` : ''}`;
36+
}
37+
38+
@state()
39+
get voltage(): string | null {
40+
const V = this.element.querySelector(selectors.VoltageLevel + ' > Voltage');
41+
if (V === null) return null;
42+
const v = V.textContent ?? '';
43+
const m = V.getAttribute('multiplier');
44+
const u = m === null ? 'V' : ' ' + m + 'V';
45+
return v ? v + u : null;
46+
}
47+
48+
@state()
49+
voltageLevelHeader(voltageLevel: Element): string {
50+
const name = voltageLevel.getAttribute('name') ?? '';
51+
const desc = voltageLevel.getAttribute('desc');
52+
53+
return `${name} ${desc ? `- ${desc}` : ''}
54+
${this.voltage === null ? '' : `(${this.voltage})`}`;
55+
}
56+
57+
@state()
58+
bayHeader(bay: Element): string {
59+
const name = bay.getAttribute('name') ?? '';
60+
const desc = bay.getAttribute('desc');
61+
62+
return `${name} ${desc ? `(${desc})` : ''}`;
63+
}
64+
65+
private renderIEDs(bay: Element): TemplateResult {
66+
const template: string =
67+
bay.querySelector(`Private[type="${SIEMENS_SITIPE_BAY_TEMPLATE}"]`)
68+
?.textContent ?? '';
69+
70+
return html`
71+
<div>
72+
${Array.from(
73+
bay.querySelectorAll(
74+
`Private[type="${SIEMENS_SITIPE_IED_REF}"]` ?? []
75+
)
76+
).map(
77+
iedTemplate =>
78+
html`<action-icon
79+
.label=${iedTemplate.textContent
80+
? `${iedTemplate.textContent} (${template})`
81+
: ''}
82+
icon="developer_board"
83+
></action-icon>`
84+
)}
85+
</div>
86+
`;
87+
}
88+
89+
private renderBay(bay: Element): TemplateResult {
90+
return html`<action-pane label="${this.bayHeader(bay)}"
91+
>${this.renderIEDs(bay)}</action-pane
92+
>`;
93+
}
94+
95+
private renderVoltageLevel(voltageLevel: Element): TemplateResult {
96+
return html`<action-pane label="${this.voltageLevelHeader(voltageLevel)}">
97+
<div class="bayContainer">
98+
${Array.from(voltageLevel.querySelectorAll(selectors.Bay) ?? []).map(
99+
this.renderBay.bind(this)
100+
)}
101+
</div>
102+
</action-pane>`;
103+
}
104+
105+
render(): TemplateResult {
106+
return html`<action-pane label="${this.substationHeader}">
107+
${Array.from(
108+
this.element.querySelectorAll(selectors.VoltageLevel) ?? []
109+
).map(this.renderVoltageLevel.bind(this))}
110+
</action-pane>`;
111+
}
112+
113+
static styles = css`
114+
.bayContainer {
115+
display: grid;
116+
grid-gap: 12px;
117+
box-sizing: border-box;
118+
grid-template-columns: repeat(auto-fit, minmax(316px, auto));
119+
}
120+
121+
@media (max-width: 387px) {
122+
.bayContainer {
123+
grid-template-columns: repeat(auto-fit, minmax(196px, auto));
124+
}
125+
}
126+
`;
127+
}

test/integration/__snapshots__/open-scd.test.snap.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
/* @web/test-runner snapshot v1 */
22
export const snapshots = {};
33

4-
snapshots["open-scd looks like its snapshot"] =
5-
`<compas-session-expiring-dialog>
4+
snapshots[
5+
'open-scd looks like its snapshot'
6+
] = `<compas-session-expiring-dialog>
67
</compas-session-expiring-dialog>
78
<compas-session-expired-dialog>
89
</compas-session-expired-dialog>
@@ -947,6 +948,22 @@ snapshots["open-scd looks like its snapshot"] =
947948
</mwc-icon>
948949
Cleanup
949950
</mwc-check-list-item>
951+
<mwc-check-list-item
952+
aria-disabled="false"
953+
class="official"
954+
graphic="control"
955+
hasmeta=""
956+
left=""
957+
mwc-list-item=""
958+
selected=""
959+
tabindex="-1"
960+
value="/src/editors/Sitipe.js"
961+
>
962+
<mwc-icon slot="meta">
963+
conveyor_belt
964+
</mwc-icon>
965+
Sitipe
966+
</mwc-check-list-item>
950967
<mwc-list-item
951968
aria-disabled="false"
952969
graphic="avatar"
@@ -1711,4 +1728,3 @@ snapshots["open-scd looks like its snapshot"] =
17111728
</mwc-dialog>
17121729
`;
17131730
/* end snapshot open-scd looks like its snapshot */
1714-

0 commit comments

Comments
 (0)