1- import { Edit , Update } from "../foundation/utils.js" ;
1+ import { createElement , Edit , Update } from "../foundation/utils.js" ;
22
33import { controlBlockObjRef } from "../tControl/controlBlockObjRef.js" ;
44
@@ -30,7 +30,11 @@ function updateIEDNameTextContent(
3030
3131 return [
3232 { node } ,
33- { parent : iedName , node : document . createTextNode ( newIedName ) , reference : null } ,
33+ {
34+ parent : iedName ,
35+ node : document . createTextNode ( newIedName ) ,
36+ reference : null ,
37+ } ,
3438 ] ;
3539 } ) ;
3640}
@@ -134,6 +138,114 @@ function updateIedNameAttributes(
134138 } ) ;
135139}
136140
141+ function objectReferenceToIed ( dai : Element , oldIedName : string ) : boolean {
142+ const val = dai . querySelector ( ":scope > Val" ) ! ;
143+ const valContent = val . textContent ;
144+
145+ if ( ! valContent || ! valContent ?. startsWith ( oldIedName ) ) return false ;
146+
147+ const lDeviceName = valContent . slice ( oldIedName . length ) . split ( "/" ) [ 0 ] ;
148+ const ied = dai . closest ( "IED" ) ;
149+
150+ const hasLDevice = ied ?. querySelector (
151+ `:scope > AccessPoint > Server > LDevice[inst="${ lDeviceName } "]` ,
152+ ) ;
153+ if ( ! hasLDevice ) return false ;
154+
155+ return true ;
156+ }
157+
158+ function canModifyDA ( daiOrdaType : Element ) : boolean {
159+ const valImport = daiOrdaType . getAttribute ( "valImport" ) ;
160+ const valKind = daiOrdaType . getAttribute ( "valKind" ) ;
161+ return (
162+ valImport === "true" && valKind !== null && [ "Conf" , "RO" ] . includes ( valKind )
163+ ) ;
164+ }
165+
166+ function objRefDetails (
167+ anyLn : Element ,
168+ doName : string ,
169+ daName : string ,
170+ ) : { bType ?: string | null ; canModify ?: boolean } | undefined {
171+ const doc = anyLn . ownerDocument ;
172+ const lNodeType = doc . querySelector (
173+ `:root > DataTypeTemplates > LNodeType[id="${ anyLn . getAttribute (
174+ "lnType" ,
175+ ) } "]`,
176+ ) ;
177+
178+ let leaf : Element | null | undefined = lNodeType ;
179+
180+ const dO : Element | null | undefined = leaf ?. querySelector (
181+ `DO[name="${ doName } "], SDO[name="${ doName } "]` ,
182+ ) ;
183+ leaf = doc . querySelector (
184+ `:root > DataTypeTemplates > DOType[id="${ dO ?. getAttribute ( "type" ) } "]` ,
185+ ) ;
186+
187+ const dA : Element | null | undefined = leaf ?. querySelector (
188+ `DA[name="${ daName } "]` ,
189+ ) ;
190+ if ( ! dA ) return undefined ;
191+
192+ const bType = dA . getAttribute ( "bType" ) ;
193+ const canModify = canModifyDA ( dA ) ;
194+
195+ return { bType, canModify } ;
196+ }
197+
198+ /** Find references used by the IED with the basic type of object reference.
199+ * Then check if they require replacement.
200+ * This function does not process LGOS and LSVS GoCBRef as these are
201+ * handled separately.
202+ */
203+ function updateObjectReferences (
204+ ied : Element ,
205+ oldIedName : string ,
206+ newIedName : string ,
207+ checkPermission = false ,
208+ ) : Edit [ ] {
209+ const objRefCandidates = Array . from (
210+ ied . querySelectorAll ( "LN DAI > Val, LN0 DAI > Val" ) ,
211+ ) . filter ( ( val ) => {
212+ const dai = val . parentElement ! ;
213+ const ln = dai . closest ( "LN, LN0" ) ;
214+ const lnClass = ln ?. getAttribute ( "lnClass" ) ;
215+ const doiName = dai . closest ( "DOI, SDI" ) ?. getAttribute ( "name" ) ;
216+ const daiName = dai . getAttribute ( "name" ) ;
217+ const isSupervision =
218+ lnClass &&
219+ doiName &&
220+ [ "LGOS" , "LSVS" ] . includes ( lnClass ) &&
221+ [ "GoCBRef" , "SvCBRef" ] . includes ( doiName ) ;
222+ if ( ! ln || ! doiName || ! daiName || isSupervision ) return false ;
223+
224+ const objRefInfo = objRefDetails ( ln , doiName , daiName ) ;
225+
226+ return (
227+ objRefInfo ?. bType === "ObjRef" &&
228+ ( checkPermission === false ||
229+ objRefInfo ?. canModify ||
230+ canModifyDA ( dai ) ) &&
231+ objectReferenceToIed ( dai , oldIedName )
232+ ) ;
233+ } ) ;
234+
235+ return objRefCandidates . flatMap ( ( val ) => {
236+ const objRef = val . textContent ! ;
237+ const textContent = `${ newIedName } ${ objRef . slice ( oldIedName . length ) } ` ;
238+
239+ const newVal = createElement ( val . ownerDocument , "Val" , { } ) ;
240+ newVal . textContent = textContent ;
241+
242+ return [
243+ { node : val } ,
244+ { parent : val . parentElement ! , node : newVal , reference : null } ,
245+ ] ;
246+ } ) ;
247+ }
248+
137249/**
138250 * Function to schema valid update name and other attribute(s) in IED element
139251 * (rename IED)
@@ -145,9 +257,11 @@ function updateIedNameAttributes(
145257 * 3. Updates IEDName elements text content
146258 * ```
147259 * @param update - IED element and attributes to be changed in the IED element
260+ * @param checkPermission - Check permission before changing object references
261+ * (other than supervision node GoCBRef values).
148262 * @returns - Set of addition edits updating all references SCL elements
149263 */
150- export function updateIED ( update : Update ) : Edit [ ] {
264+ export function updateIED ( update : Update , checkPermission = false ) : Edit [ ] {
151265 if ( update . element . tagName !== "IED" ) return [ ] ;
152266 if ( ! update . attributes . name ) return [ update ] ;
153267
@@ -161,5 +275,6 @@ export function updateIED(update: Update): Edit[] {
161275 ...updateIedNameAttributes ( ied , oldIedName , newIedName ) ,
162276 ...updateSubscriptionSupervision ( ied , oldIedName , newIedName ) ,
163277 ...updateIEDNameTextContent ( ied , oldIedName , newIedName ) ,
278+ ...updateObjectReferences ( ied , oldIedName , newIedName , checkPermission ) ,
164279 ] ;
165280}
0 commit comments