OpenEnergyTools / scl-communication-editor

Apache License 2.0
0 stars 1 forks source link

Provide FCDA descriptions when showing a connection #30

Open danyill opened 6 months ago

danyill commented 6 months ago

The display of connections is already quite nice with the ExtRef description:

image

image

However not all devices have very semantic data models (also in the screenshots using GGIO).

We've found that that joining the descriptions from the LDevice, LN, DO (but not usually DA) is quite helpful and we've often edited these descriptions so the descriptions of this data structure somewhat match the ExtRef description.

In the subscriber-later-binding plugin I used the following to generate a description.

If we thought this was a good idea then perhaps we could provide it in scl-lib and then use it here (I can do a PR at some stage and make some improvements in the code although I'd like to agree a call signature first).

I'm keen to have this in the publisher plugin as well.

export function getFcdaInstDesc(fcda: Element): fcdaDesc {
  const [doName, daName] = ['doName', 'daName'].map(attr =>
    fcda.getAttribute(attr)
  );

  const ied = fcda.closest('IED')!;

  const anyLn = Array.from(
    ied.querySelectorAll(
      `LDevice[inst="${fcda.getAttribute(
        'ldInst'
      )}"] > LN, LDevice[inst="${fcda.getAttribute('ldInst')}"] LN0`
    )
  ).find(
    lN =>
      (lN.getAttribute('prefix') ?? '') ===
        (fcda.getAttribute('prefix') ?? '') &&
      lN.getAttribute('lnClass') === (fcda.getAttribute('lnClass') ?? '') &&
      (lN.getAttribute('inst') ?? '') === (fcda.getAttribute('lnInst') ?? '')
  );

  if (!anyLn) return {};

  let descs: fcdaDesc = {};

  const ldDesc = anyLn.closest('LDevice')!.getAttribute('desc');
  const lnDesc = anyLn.getAttribute('desc');
  descs = { ...descs, ...(ldDesc && ldDesc !== '' && { LDevice: ldDesc }) };
  descs = { ...descs, ...(lnDesc && lnDesc !== '' && { LN: lnDesc }) };

  const doNames = doName!.split('.');

  const doi = anyLn.querySelector(`DOI[name="${doNames[0]}"`);

  if (!doi) return descs;

  const doiDesc = doi?.getAttribute('desc');
  descs = { ...descs, ...(doiDesc && doiDesc !== '' && { DOI: doiDesc }) };

  let previousDI: Element = doi;
  doNames.slice(1).forEach(sdiName => {
    const sdi = previousDI.querySelector(`SDI[name="${sdiName}"]`);
    if (sdi) previousDI = sdi;
    const sdiDesc = sdi?.getAttribute('desc');
    if (!('SDI' in descs)) {
      descs = {
        ...descs,
        ...(sdiDesc && sdiDesc !== '' && { SDI: [sdiDesc] })
      };
    } else if (sdiDesc) descs.SDI!.push(sdiDesc);
  });

  if (!daName) return descs;

  const daNames = daName?.split('.');
  const dai = previousDI.querySelector(`DAI[name="${daNames[0]}"]`);

  const daiDesc = dai?.getAttribute('desc');
  descs = { ...descs, ...(daiDesc && daiDesc !== '' && { DAI: daiDesc }) };

  return descs;
}