@@ -18,15 +18,26 @@ import {
1818 removeDataSet ,
1919} from '@openenergytools/scl-lib' ;
2020
21- import { DataSetElementEditor } from './data-set-element-editor.js' ;
22-
21+ import { MdDialog } from '@scopedelement/material-web/dialog/MdDialog.js' ;
22+ import { MdIcon } from '@scopedelement/material-web/icon/MdIcon.js' ;
23+ import { MdTextButton } from '@scopedelement/material-web/button/MdTextButton.js' ;
24+ import { MdRadio } from '@scopedelement/material-web/radio/radio.js' ;
25+ import { MdList } from '@scopedelement/material-web/list/MdList.js' ;
26+ import { MdListItem } from '@scopedelement/material-web/list/MdListItem.js' ;
2327import { pathIdentity , styles } from '../../foundation.js' ;
28+ import { DataSetElementEditor } from './data-set-element-editor.js' ;
2429
2530export class DataSetEditor extends ScopedElementsMixin ( LitElement ) {
2631 static scopedElements = {
2732 'action-list' : ActionList ,
33+ 'md-text-button' : MdTextButton ,
2834 'data-set-element-editor' : DataSetElementEditor ,
2935 'md-outlined-button' : MdOutlinedButton ,
36+ 'md-dialog' : MdDialog ,
37+ 'md-icon' : MdIcon ,
38+ 'md-radio' : MdRadio ,
39+ 'md-list' : MdList ,
40+ 'md-list-item' : MdListItem ,
3041 } ;
3142
3243 /** The document being edited as provided to plugins by [[`OpenSCD`]]. */
@@ -42,13 +53,24 @@ export class DataSetEditor extends ScopedElementsMixin(LitElement) {
4253 @state ( )
4354 selectedDataSet ?: Element ;
4455
56+ @state ( )
57+ lDevices : Element [ ] = [ ] ; // lDevices of the currently selected IED, used for the LDevice select dialog
58+
59+ @state ( )
60+ selectedLDevice : Element | null = null ;
61+
62+ @state ( )
63+ selectedIed : Element | null = null ;
64+
4565 @query ( '.selectionlist' ) selectionList ! : ActionList ;
4666
4767 @query ( '.change.scl.element' ) selectDataSetButton ! : MdOutlinedButton ;
4868
4969 @query ( 'data-set-element-editor' )
5070 dataSetElementEditor ! : DataSetElementEditor ;
5171
72+ @query ( '#ldevice-select' ) lDeviceSelectDialog ! : MdDialog ;
73+
5274 /** Resets selected DataSet, if not existing in new doc
5375 update(props: Map<string | number | symbol, unknown>): void {
5476 if (props.has('doc') && this.selectedDataSet) {
@@ -101,28 +123,30 @@ export class DataSetEditor extends ScopedElementsMixin(LitElement) {
101123 {
102124 icon : 'playlist_add' ,
103125 callback : ( ) => {
104- const insertDataSet = createDataSet ( ied ) ;
105- if ( insertDataSet ) {
106- this . dispatchEvent (
107- newEditEvent ( insertDataSet , { title : `Create New DataSet` } )
108- ) ;
109- } else {
126+ this . selectedIed = ied ;
127+ const lDevices = ied . querySelectorAll (
128+ ':scope > AccessPoint > Server > LDevice'
129+ ) ;
130+ if ( lDevices . length === 0 ) {
131+ // LDevice does not exist, cannot create DataSet
110132 const iedName = ied . getAttribute ( 'name' ) ;
111- let reason : string ;
112- const anyLn = ied . querySelector ( 'LN0, LN' ) ;
113- if ( ! anyLn ) {
114- reason = 'it has no LN0 or LN element' ;
115- } else {
116- reason = 'an unknown validation error occurred' ;
117- }
118-
133+ const reason = 'it has no LDevice element' ;
119134 this . dispatchEvent (
120135 newLogEvent ( {
121136 title : 'Could not create DataSet' ,
122137 message : `The DataSet could not be created in IED '${ iedName } ' because ${ reason } .` ,
123138 kind : 'warning' ,
124139 } )
125140 ) ;
141+ } else if ( lDevices . length === 1 ) {
142+ // only one LDevice, create DataSet directly
143+ const selectedLDevice = lDevices [ 0 ] ;
144+ this . createDataSet ( ied , selectedLDevice ) ;
145+ } else {
146+ // multiple LDevices, show select dialog
147+ this . selectedLDevice = null ;
148+ this . lDevices = Array . from ( lDevices ) ;
149+ this . lDeviceSelectDialog ?. show ( ) ;
126150 }
127151 } ,
128152 } ,
@@ -170,6 +194,36 @@ export class DataSetEditor extends ScopedElementsMixin(LitElement) {
170194 > </ action- lis t> ` ;
171195 }
172196
197+ private createDataSet ( ied : Element , targetLDevice : Element ) : void {
198+ if ( ! targetLDevice ) return ;
199+ const ln0 = targetLDevice . querySelector ( ':scope > LN0' ) ;
200+ if ( ! ln0 ) return ;
201+
202+ const insertDataSet = createDataSet ( ln0 ) ;
203+ if ( insertDataSet ) {
204+ this . dispatchEvent (
205+ newEditEvent ( insertDataSet , { title : `Create New DataSet` } )
206+ ) ;
207+ } else {
208+ const iedName = ied . getAttribute ( 'name' ) ;
209+ let reason : string ;
210+ const anyLn = ied . querySelector ( 'LN0, LN' ) ;
211+ if ( ! anyLn ) {
212+ reason = 'it has no LN0 or LN element' ;
213+ } else {
214+ reason = 'an unknown validation error occurred' ;
215+ }
216+
217+ this . dispatchEvent (
218+ newLogEvent ( {
219+ title : 'Could not create DataSet' ,
220+ message : `The DataSet could not be created in IED '${ iedName } ' because ${ reason } .` ,
221+ kind : 'warning' ,
222+ } )
223+ ) ;
224+ }
225+ }
226+
173227 private renderToggleButton ( ) : TemplateResult {
174228 return html `<md- outlined- butto n
175229 class= "change scl element"
@@ -181,12 +235,65 @@ export class DataSetEditor extends ScopedElementsMixin(LitElement) {
181235 > ` ;
182236 }
183237
238+ private renderLDeviceSelectDialog ( ) : TemplateResult {
239+ return html ` <md- dialog id= "ldevice-select" >
240+ <div slot= "headline" > Select LDevice </ div>
241+
242+ <div slot= "content" >
243+ <p style= "color: var(--mdc-theme-on-surface, #333); font-size: 0.95em;" >
244+ Choose the LDevice to which the new DataSet will be added .
245+ </ p>
246+ <for m>
247+ <md- lis t role= "radiogroup" >
248+ ${ this . lDevices . map (
249+ ( ld , i ) => html `
250+ <md- lis t- item>
251+ <md- radio
252+ id= "ldevice${ i } "
253+ name = "ldevice"
254+ value= "${ ld . getAttribute ( 'inst' ) } "
255+ ?checked= ${ this . selectedLDevice ?. getAttribute ( 'inst' ) ===
256+ ld . getAttribute ( 'inst' ) }
257+ @change = ${ ( ) => this . selectLDevice ( ld ) }
258+ > </ md- radio>
259+ <label for = "ldevice${ i } " > ${ ld . getAttribute ( 'inst' ) } </ label>
260+ </ md- lis t- item>
261+ `
262+ ) }
263+ </ md- lis t>
264+ </ for m>
265+ </ div>
266+
267+ <div slot= "actions" >
268+ <md- text- butto n @click = ${ ( ) => this . lDeviceSelectDialog ?. close ( ) }
269+ > Close </ md- text- butto n
270+ >
271+ <md- text- butto n
272+ class= "do picker save"
273+ ?dis abled= ${ ! this . selectedLDevice }
274+ @click = ${ ( ) => this . handleLDeviceSelect ( ) }
275+ > Select <md- icon slot= "icon" > check </ md- icon> </ md- text- butto n
276+ >
277+ </ div>
278+ </ md- dialog> ` ;
279+ }
280+
281+ private selectLDevice ( ld : Element | null ) : void {
282+ this . selectedLDevice = ld ;
283+ }
284+
285+ private handleLDeviceSelect ( ) : void {
286+ this . lDeviceSelectDialog ?. close ( ) ;
287+ if ( ! this . selectedLDevice || ! this . selectedIed ) return ;
288+ this . createDataSet ( this . selectedIed , this . selectedLDevice ) ;
289+ }
290+
184291 render ( ) : TemplateResult {
185292 if ( ! this . doc ) return html `<div> No SCL loaded </ div> ` ;
186293
187294 return html `${ this . renderToggleButton ( ) }
188295 <div class= "section" >
189- ${ this . renderSelectionList ( ) } ${ this . renderElementEditorContainer ( ) }
296+ ${ this . renderSelectionList ( ) } ${ this . renderElementEditorContainer ( ) } ${ this . renderLDeviceSelectDialog ( ) }
190297 </ div> ` ;
191298 }
192299
0 commit comments