@@ -7,18 +7,23 @@ import { compareNames } from '../foundation.js';
7
7
import { stripExtensionFromName } from '../compas/foundation.js' ;
8
8
import { get } from 'lit-translate' ;
9
9
10
- type ColumnSettings = {
10
+ // Structure of the Configuration file defined by both types.
11
+ type ColumnConfiguration = {
11
12
header : string ;
12
13
attributeName ?: string ;
13
14
selector ?: string ;
14
15
useOwnerDocument ?: boolean ;
15
16
dataAttributePath ?: string [ ] ;
16
17
} ;
17
18
18
- export type Settings = {
19
- columns : ColumnSettings [ ] ;
19
+ export type Configuration = {
20
+ columns : ColumnConfiguration [ ] ;
20
21
} ;
21
22
23
+ /**
24
+ * Menu item to create a CSV File containing a line per IED holding the parameters of that IED.
25
+ * Which columns are being returned is configured in the file 'public/conf/export-ied-params.json'.
26
+ */
22
27
export default class ExportIEDParamsPlugin extends LitElement {
23
28
@property ( ) doc ! : XMLDocument ;
24
29
@property ( ) docName ! : string ;
@@ -31,6 +36,13 @@ export default class ExportIEDParamsPlugin extends LitElement {
31
36
return selector . replace ( / { { \s * i e d N a m e \s * } } / , iedName ) ;
32
37
}
33
38
39
+ /**
40
+ * Find the DO/DA/BDA element with the passed name defined below the type element passed.
41
+ * Depending on the type of Type element the query will search for the DO/DA/BDA element.
42
+ *
43
+ * @param typeElement - The type element, this can be a LNodeType, DOType or DAType element.
44
+ * @param name - The name of the element to search for below the type element.
45
+ */
34
46
private getDataElement ( typeElement : Element , name : string ) : Element | null {
35
47
if ( typeElement . tagName === 'LNodeType' ) {
36
48
return typeElement . querySelector ( `:scope > DO[name="${ name } "]` ) ;
@@ -43,6 +55,12 @@ export default class ExportIEDParamsPlugin extends LitElement {
43
55
}
44
56
}
45
57
58
+ /**
59
+ * Retrieve the value that will be added to the CSV file. If an attribute name is passed the value of the
60
+ * attribute is returned. Otherwise, the textContent of the element is returned.
61
+ * @param element - The element to retrieve the value from.
62
+ * @param attributeName - Optional the name of the attribute.
63
+ */
46
64
private getValue (
47
65
element : Element ,
48
66
attributeName : string | undefined
@@ -53,6 +71,12 @@ export default class ExportIEDParamsPlugin extends LitElement {
53
71
return element . textContent ?? '' ;
54
72
}
55
73
74
+ /**
75
+ * Use the DO/SDO/DA/BDA data element to search for the type element. In case of the DO/SDO a DOType is search
76
+ * for and otherwise a DAType is searched for if the data element is a struct type.
77
+ *
78
+ * @param lastElement - The data element to retrieve its type definition.
79
+ */
56
80
private getTypeElement ( lastElement : Element | null ) : Element | null {
57
81
if ( lastElement ) {
58
82
if ( [ 'DO' , 'SDO' ] . includes ( lastElement . tagName ) ) {
@@ -69,12 +93,22 @@ export default class ExportIEDParamsPlugin extends LitElement {
69
93
return null ;
70
94
}
71
95
96
+ /**
97
+ * Search for the DO/SDO/DA/BDA element in the Template section of the document using the path array passed.
98
+ * The LN element is the starting point for the search in the Template section.
99
+ *
100
+ * @param lnElement - The LN Element used as starting point in the Template section.
101
+ * @param dataAttributePath - The list of elements to search for, the names of the elements.
102
+ */
72
103
private getDataAttributeTemplateValue (
73
104
lnElement : Element ,
74
105
dataAttributePath : string [ ]
75
106
) : string | null {
76
107
// This is only useful if the element to start from is the LN(0) Element.
77
- if ( [ 'LN' , 'LN0' ] . includes ( lnElement . tagName ) ) {
108
+ if (
109
+ [ 'LN' , 'LN0' ] . includes ( lnElement . tagName ) &&
110
+ dataAttributePath . length >= 2
111
+ ) {
78
112
// Search LNodeType Element that is linked to the LN(0) Element.
79
113
const type = lnElement . getAttribute ( 'lnType' ) ;
80
114
let typeElement = this . doc . querySelector ( `LNodeType[id="${ type } "]` ) ;
@@ -96,42 +130,68 @@ export default class ExportIEDParamsPlugin extends LitElement {
96
130
return null ;
97
131
}
98
132
133
+ /**
134
+ * Search for the DAI element below the LN element using the path passed. The list of names is converted
135
+ * to a CSS Selector to search for the DAI Element and its Val Element.
136
+ *
137
+ * @param lnElement - The LN Element used as starting point for the search.
138
+ * @param dataAttributePath - The names of the DOI/SDI/DAI Elements to search for.
139
+ */
99
140
private getDataAttributeInstanceValue (
100
- element : Element ,
141
+ lnElement : Element ,
101
142
dataAttributePath : string [ ]
102
143
) : string | null {
103
- const daiSelector = dataAttributePath
104
- . slice ( )
105
- . reverse ( )
106
- . map ( ( path , index ) => {
107
- if ( index === 0 ) {
108
- return `DAI[name="${ path } "]` ;
109
- } else if ( index === dataAttributePath . length - 1 ) {
110
- return `DOI[name="${ path } "]` ;
111
- }
112
- return `SDI[name="${ path } "]` ;
113
- } )
114
- . reverse ( )
115
- . join ( ' > ' ) ;
116
-
117
- const daiValueElement = element . querySelector ( daiSelector + ' Val' ) ;
118
- if ( daiValueElement ) {
119
- return daiValueElement . textContent ;
144
+ if (
145
+ [ 'LN' , 'LN0' ] . includes ( lnElement . tagName ) &&
146
+ dataAttributePath . length >= 2
147
+ ) {
148
+ const daiSelector = dataAttributePath
149
+ . map ( ( path , index ) => {
150
+ if ( index === 0 ) {
151
+ // The first element is always a DOI element.
152
+ return `DOI[name="${ path } "]` ;
153
+ } else if ( index === dataAttributePath . length - 1 ) {
154
+ // The last element is always a DAI element.
155
+ return `DAI[name="${ path } "]` ;
156
+ }
157
+ // Every element(s) between the DOI and DAI element is always a SDI element.
158
+ return `SDI[name="${ path } "]` ;
159
+ } )
160
+ . join ( ' > ' ) ;
161
+
162
+ const daiValueElement = lnElement . querySelector ( daiSelector + ' Val' ) ;
163
+ return daiValueElement ?. textContent ?? null ;
120
164
}
121
165
return null ;
122
166
}
123
167
168
+ /**
169
+ * First check if there is an instance element found (DAI) found, otherwise search in the Template section.
170
+ *
171
+ * @param lnElement - The LN Element used as starting point for the search.
172
+ * @param dataAttributePath - The names of the DO(I)/SD(I)/DA(I) Elements to search for.
173
+ */
124
174
private getDataAttributeValue (
125
- element : Element ,
175
+ lnElement : Element ,
126
176
dataAttributePath : string [ ]
127
177
) : string {
128
- let value = this . getDataAttributeInstanceValue ( element , dataAttributePath ) ;
178
+ let value = this . getDataAttributeInstanceValue (
179
+ lnElement ,
180
+ dataAttributePath
181
+ ) ;
129
182
if ( ! value ) {
130
- value = this . getDataAttributeTemplateValue ( element , dataAttributePath ) ;
183
+ value = this . getDataAttributeTemplateValue ( lnElement , dataAttributePath ) ;
131
184
}
132
185
return value ?? '' ;
133
186
}
134
187
188
+ /**
189
+ * Retrieve the list of elements found by the selector or if no selector defined the IED element.
190
+ *
191
+ * @param iedElement - The IED element that will be used to search below if useOwnerDocument is false.
192
+ * @param selector - If passed the CSS selector to search for the elements.
193
+ * @param useOwnerDocument - If false will use the IED element to search below, otherwise the full document.
194
+ */
135
195
private getElements (
136
196
iedElement : Element ,
137
197
selector : string | undefined ,
@@ -152,62 +212,84 @@ export default class ExportIEDParamsPlugin extends LitElement {
152
212
return elements ;
153
213
}
154
214
155
- private contentIED ( settings : Settings , iedElement : Element ) : string [ ] {
156
- return settings . columns . map ( value => {
215
+ /**
216
+ * Create a single line of values for the CSV File.
217
+ *
218
+ * @param configuration - The configuration with values to retrieve.
219
+ * @param iedElement - The IED Element for which to retrieve the values.
220
+ */
221
+ private cvsLine ( configuration : Configuration , iedElement : Element ) : string [ ] {
222
+ return configuration . columns . map ( column => {
157
223
const elements = this . getElements (
158
224
iedElement ,
159
- value . selector ,
160
- value . useOwnerDocument ?? false
225
+ column . selector ,
226
+ column . useOwnerDocument ?? false
161
227
) ;
162
228
163
229
return elements
164
230
. map ( element => {
165
- if ( value . dataAttributePath ) {
166
- return this . getDataAttributeValue ( element , value . dataAttributePath ) ;
231
+ if ( column . dataAttributePath ) {
232
+ return this . getDataAttributeValue (
233
+ element ,
234
+ column . dataAttributePath
235
+ ) ;
167
236
}
168
- return this . getValue ( element , value . attributeName ) ;
237
+ return this . getValue ( element , column . attributeName ) ;
169
238
} )
170
239
. filter ( value => value ! )
171
240
. join ( ' / ' ) ;
172
241
} ) ;
173
242
}
174
243
175
- private content ( settings : Settings ) : string [ ] [ ] {
244
+ /**
245
+ * Create the full content of the CSV file, for each IED found a line of values is returned.
246
+ *
247
+ * @param configuration - The configuration of the values to retrieve.
248
+ */
249
+ private cvsLines ( configuration : Configuration ) : string [ ] [ ] {
176
250
const ieds = this . ieds ;
177
251
if ( ieds . length > 0 ) {
178
252
return ieds
179
253
. sort ( compareNames )
180
- . map ( iedElement => this . contentIED ( settings , iedElement ) ) ;
254
+ . map ( iedElement => this . cvsLine ( configuration , iedElement ) ) ;
181
255
}
182
256
return [ [ get ( 'compas.exportIEDParams.noIEDs' ) ] ] ;
183
257
}
184
258
185
- private columnHeaders ( settings : Settings ) : string [ ] {
186
- return settings . columns . map ( value => value . header ) ;
259
+ /**
260
+ * Return the headers values from the configuration.
261
+ *
262
+ * @param configuration - The configuration containing the header names.
263
+ */
264
+ private columnHeaders ( configuration : Configuration ) : string [ ] {
265
+ return configuration . columns . map ( column => column . header ) ;
187
266
}
188
267
189
- async getSettings ( ) : Promise < Settings > {
190
- return await import ( '../../public/conf/export-ied-parameters.json' ) . then (
268
+ /**
269
+ * Read the configuration file.
270
+ */
271
+ async getConfiguration ( ) : Promise < Configuration > {
272
+ return await import ( '../../public/conf/export-ied-params.json' ) . then (
191
273
module => module . default
192
274
) ;
193
275
}
194
276
195
277
async run ( ) : Promise < void > {
196
- // Import the JSON Configuration needed for the import .
197
- const settings = await this . getSettings ( ) ;
278
+ // Retrieve the JSON Configuration.
279
+ const configuration = await this . getConfiguration ( ) ;
198
280
199
- // Create the content using a CSV Writer .
200
- const content = stringify ( this . content ( settings ) , {
281
+ // Create the content using a CSV Library .
282
+ const content = stringify ( this . cvsLines ( configuration ) , {
201
283
header : true ,
202
- columns : this . columnHeaders ( settings ) ,
284
+ columns : this . columnHeaders ( configuration ) ,
203
285
} ) ;
204
286
const blob = new Blob ( [ content ] , {
205
287
type : 'text/csv' ,
206
288
} ) ;
207
289
208
290
// Push the data back to the user.
209
291
const a = document . createElement ( 'a' ) ;
210
- a . download = stripExtensionFromName ( this . docName ) + '-ied-parameters .csv' ;
292
+ a . download = stripExtensionFromName ( this . docName ) + '-ied-params .csv' ;
211
293
a . href = URL . createObjectURL ( blob ) ;
212
294
a . dataset . downloadurl = [ 'text/csv' , a . download , a . href ] . join ( ':' ) ;
213
295
a . style . display = 'none' ;
0 commit comments