Skip to content

Commit aff0367

Browse files
feat(wizards/smv): add edit wizard and allow access from sampledvaluecontrol wizard (openscd#519)
* fix(wiazrds/address): wrong wizard input definition * fix(translation): complex action title * feat(wizards/smv): add edit wizard * feat(wiazrds/sampledvaluecontrol): allow access to scm wizard
1 parent b6f7ea1 commit aff0367

File tree

11 files changed

+681
-89
lines changed

11 files changed

+681
-89
lines changed

src/translations/de.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,11 @@ export const de: Translations = {
405405
addaddress: 'GSE bearbeitet ({{identity}})',
406406
},
407407
},
408+
smv: {
409+
action: {
410+
addaddress: 'SMV bearbeitet ({{identity}})',
411+
},
412+
},
408413
subscriber: {
409414
title: 'Subscriber Update',
410415
description: 'GOOSE Ziele aktualisieren: ',

src/translations/en.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,11 @@ export const en = {
402402
addaddress: 'Edit GSE ({{identity}})',
403403
},
404404
},
405+
smv: {
406+
action: {
407+
addaddress: 'Edit SMV ({{identity}})',
408+
},
409+
},
405410
subscriber: {
406411
title: 'Subscriber update',
407412
description: 'Subscriber update: ',

src/wizards/address.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export function renderGseSmvAddress(parent: Element): TemplateResult[] {
3939
?.innerHTML.trim() ?? null}
4040
?nullable=${typeNullable[ptype]}
4141
pattern="${ifDefined(typePattern[ptype])}"
42+
required
4243
></wizard-textfield>`
4344
)}`,
4445
];

src/wizards/sampledvaluecontrol.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,24 @@ import {
2323
} from '../foundation.js';
2424
import { securityEnableEnum, smpModEnum } from './foundation/enums.js';
2525
import { maxLength, patterns } from './foundation/limits.js';
26+
import { editSMvWizard } from './smv.js';
27+
28+
function getSMV(element: Element): Element | null {
29+
const cbName = element.getAttribute('name');
30+
const iedName = element.closest('IED')?.getAttribute('name');
31+
const apName = element.closest('AccessPoint')?.getAttribute('name');
32+
const ldInst = element.closest('LDevice')?.getAttribute('inst');
33+
34+
return (
35+
element
36+
.closest('SCL')
37+
?.querySelector(
38+
`:root > Communication > SubNetwork > ` +
39+
`ConnectedAP[iedName="${iedName}"][apName="${apName}"] > ` +
40+
`SMV[ldInst="${ldInst}"][cbName="${cbName}"]`
41+
) ?? null
42+
);
43+
}
2644

2745
interface ContentOptions {
2846
name: string | null;
@@ -159,6 +177,8 @@ export function editSampledValueControlWizard(element: Element): Wizard {
159177
const nofASDU = element.getAttribute('nofASDU');
160178
const securityEnable = element.getAttribute('securityEnabled');
161179

180+
const sMV = getSMV(element);
181+
162182
return [
163183
{
164184
title: get('wizard.title.edit', { tagName: element.tagName }),
@@ -179,6 +199,18 @@ export function editSampledValueControlWizard(element: Element): Wizard {
179199
nofASDU,
180200
securityEnable,
181201
}),
202+
sMV
203+
? html`<mwc-button
204+
id="editsmv"
205+
label=${translate('scl.Communication')}
206+
icon="edit"
207+
@click="${(e: MouseEvent) => {
208+
e.target?.dispatchEvent(
209+
newSubWizardEvent(() => editSMvWizard(sMV))
210+
);
211+
}}}"
212+
></mwc-button>`
213+
: html``,
182214
],
183215
},
184216
];

src/wizards/smv.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { get } from 'lit-translate';
2+
3+
import { Checkbox } from '@material/mwc-checkbox';
4+
5+
import {
6+
ComplexAction,
7+
identity,
8+
Wizard,
9+
WizardAction,
10+
WizardActor,
11+
WizardInput,
12+
} from '../foundation.js';
13+
import { renderGseSmvAddress, updateAddress } from './address.js';
14+
15+
export function updateSmvAction(element: Element): WizardActor {
16+
return (inputs: WizardInput[], wizard: Element): WizardAction[] => {
17+
const complexAction: ComplexAction = {
18+
actions: [],
19+
title: get('smv.action.addaddress', {
20+
identity: identity(element),
21+
}),
22+
};
23+
24+
const instType: boolean = (<Checkbox>(
25+
wizard.shadowRoot?.querySelector('#instType')
26+
))?.checked;
27+
const addressActions = updateAddress(element, inputs, instType);
28+
if (!addressActions.length) return [];
29+
30+
addressActions.forEach(action => {
31+
complexAction.actions.push(action);
32+
});
33+
34+
return [complexAction];
35+
};
36+
}
37+
38+
export function editSMvWizard(element: Element): Wizard {
39+
return [
40+
{
41+
title: get('wizard.title.edit', { tagName: element.tagName }),
42+
element,
43+
primary: {
44+
label: get('save'),
45+
icon: 'edit',
46+
action: updateSmvAction(element),
47+
},
48+
content: [...renderGseSmvAddress(element)],
49+
},
50+
];
51+
}

test/foundation.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ export function ipV6(): Arbitrary<string> {
3131
);
3232
}
3333

34+
export function MAC(): Arbitrary<string> {
35+
const h16Arb = hexaString({ minLength: 2, maxLength: 2 });
36+
const ls32Arb = tuple(h16Arb, h16Arb).map(([a, b]) => `${a}-${b}`);
37+
return tuple(array(h16Arb, { minLength: 4, maxLength: 4 }), ls32Arb).map(
38+
([eh, l]) => `${eh.join('-')}-${l}`
39+
);
40+
}
41+
3442
export function ipV6SubNet(): Arbitrary<string> {
3543
return integer({ min: 1, max: 127 }).map(num => `/${num}`);
3644
}
@@ -47,6 +55,7 @@ export const regExp = {
4755
desc: new RegExp(`^${patterns.normalizedString}$`),
4856
IPv4: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/,
4957
IPv6: /^([0-9A-F]{2}-){5}[0-9A-F]{2}$/,
58+
MAC: /^([0-9A-F]{2}-){5}[0-9A-F]{2}$/,
5059
OSI: /^[0-9A-F]+$/,
5160
OSIAPi: /^[0-9\u002C]+$/,
5261
OSIid: /^[0-9]+$/,

0 commit comments

Comments
 (0)