Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(editors/substation/conductingequipment): add earth-switch symbol #460

Merged
merged 13 commits into from
Jan 24, 2022
110 changes: 61 additions & 49 deletions src/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,55 +455,67 @@ export const voltageTransformerIcon = html`<svg
</svg>`;

export const earthSwitchIcon = html`<svg
xmlns="http://www.w3.org/2000/svg"
danyill marked this conversation as resolved.
Show resolved Hide resolved
viewBox="0 0 25 25"
>
<line
x1="12.5"
y1="2"
x2="12.5"
y2="8"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
/>
<line
x1="11"
y1="23"
x2="14"
y2="23"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
/>
<line
x1="12.5"
y1="23"
x2="12.5"
y2="18"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
/>
<line
x1="12.5"
y1="18"
x2="8"
y2="9"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
/>
<line
x1="11.5"
y1="8"
x2="13.5"
y2="8"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round"
/>
</svg> `;
viewBox="0 0 25 25"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<line
x1="12.5"
y1="19.25"
x2="12.5"
y2="16.25"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round" />
<line
x1="12.5"
y1="1.25"
x2="12.5"
y2="6.25"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round" />
<line
x1="12.5"
y1="6.25"
x2="17"
y2="15.25"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round" />
<line
x1="13.5"
y1="16.25"
x2="11.5"
y2="16.25"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round" />
<line
x1="17"
y1="19.25"
x2="8"
y2="19.25"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round" />>
<line
x1="15.5"
y1="21.45"
x2="9.5"
y2="21.45"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round" />
<line
x1="14.5"
y1="23.5"
x2="10.5"
y2="23.5"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="round" />
</svg>`;

export const generalConductingEquipmentIcon = html`<svg
xmlns="http://www.w3.org/2000/svg"
Expand Down
105 changes: 100 additions & 5 deletions src/wizards/conductingequipment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
WizardInput,
} from '../foundation.js';
import { updateNamingAction } from './foundation/actions.js';
import { getDirections } from '../editors/singlelinediagram/sld-drawing.js';
danyill marked this conversation as resolved.
Show resolved Hide resolved

const types: Partial<Record<string, string>> = {
// standard
Expand Down Expand Up @@ -49,11 +50,105 @@ const types: Partial<Record<string, string>> = {
TCR: 'Thyristor Controlled Reactive Component',
};

function typeStr(condEq: Element): string {
return condEq.getAttribute('type') === 'DIS' &&
condEq.querySelector('Terminal')?.getAttribute('cNodeName') === 'grounded'
? 'ERS'
: condEq.getAttribute('type') ?? '';
/**
* Finds Logical Node from IED based on Logical Node in the Substation SCL section.
* @param lNode - Logical Node in Substation SCL section.
* @returns - Logical Node in the IED SCL section.
*/
function getLNodefromIED(lNode: Element | null): Element | null {
danyill marked this conversation as resolved.
Show resolved Hide resolved
if (!lNode) return null;
const [iedName, ldInst, lnClass, lnInst, lnType] = [
'iedName',
'ldInst',
'lnClass',
'lnInst',
'lnType',
].map(attribute => lNode?.getAttribute(attribute));
return (<Element>lNode?.getRootNode()).querySelector(`IED[name='${iedName}'] > AccessPoint > Server > LDevice[inst='${ldInst}'] > LN[inst='${lnInst}'][lnType='${lnType}'][lnClass='${lnClass}']`);
danyill marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Finds data attribute by inspecting an IED's logical node or types.
* @param lNode - LNode within the IED section.
* @param doName - name for data object (e.g. SwTyp).
* @param sdName - id for sub data object.
* @param daName - name for data attribute (e.g. stVal).
* @returns - value of type as a string.
*/
function getDataAttributeValue(lNode: Element, doName: string, sdName: string | undefined, daName: string): string | undefined {
danyill marked this conversation as resolved.
Show resolved Hide resolved
const sdDef = sdName ? ` > SDI[name='${sdName}']` : '';
const daInstantiated = lNode.querySelector(`DOI[name='${doName}']${sdDef} > DAI[name='${daName}'`);
if (daInstantiated) {
danyill marked this conversation as resolved.
Show resolved Hide resolved
// data attribute is fully instantiated within a DOI/SDI, look for it within: DOI > ?SDI > DAI
return daInstantiated.querySelector('Val')?.innerHTML.trim();
} else {
const rootNode = <Element>lNode?.getRootNode();
danyill marked this conversation as resolved.
Show resolved Hide resolved
const lNodeType = lNode.getAttribute('type');
const lnClass = lNode.getAttribute('lnClass');
if (sdName) {
// definition to be found on the subdata type
// this code path has not been tested but may be of use elsewhere
const sdo = rootNode.querySelector(`DataTypeTemplates > LNodeType[id=${lNodeType}][class=${lnClass}] > SDO[name=${doName}]`);
if (sdo) {
const sdoRef = sdo.getAttribute('type');
return rootNode.querySelector(`DataTypeTemplates > DOType[id=${sdoRef}] > DA[name='${daName}'] > Val`)?.innerHTML.trim();
}
// definition missing
return undefined;
} else {
// definition must be on the data object type
const doObj = rootNode.querySelector(`DataTypeTemplates > LNodeType[id=${lNodeType}][class=${lnClass}] > DO[name=${doName}]`);
if (doObj) {
const doRef = doObj.getAttribute('type');
return rootNode.querySelector(`DataTypeTemplates > DOType[id=${doRef}] > DA[name='${daName}'] > Val`)?.innerHTML.trim();
}
// definition missing
return undefined;
}
}
}

/**
* Returns true if any terminal of the ConductingEquipment has a connectivity node name 'grounded'.
* @param condEq - SCL ConductingEquipment.
* @returns if any terminal of the ConductingEquipment is grounded.
*/
function containsGroundedTerminal(condEq: Element): boolean {
return (Array.from(condEq.querySelectorAll('Terminal'))
.map(t => t.getAttribute('cNodeName'))
.includes('grounded')
);
}

/**
* Looks to see if the Conducting Equipment contains an XSWI logical node. If so, check if the XSWI definition
* includes SwTyp and if stVal indicates an Earth/Earthing Switch.
* @param condEq - SCL ConductingEquipment
* @returns true if an earth switch is found, false otherwise.
*/
function containsEarthSwitchDefinition(condEq: Element): boolean {
const lNodeXSWI = condEq.querySelector("LNode[lnClass='XSWI']");
const lNode = getLNodefromIED(lNodeXSWI);
danyill marked this conversation as resolved.
Show resolved Hide resolved
if (lNode) {
const swTypVal = getDataAttributeValue(lNode, 'SwTyp', undefined, 'stVal');
return swTypVal ? ['Earthing Switch', 'High Speed Earthing Switch'].includes(swTypVal) : false;
danyill marked this conversation as resolved.
Show resolved Hide resolved
} else {
return false;
}
}

/**
* Find the type of an SCL conducting equipment. For earth switches derive this from terminals or XSWI logical node definition.
* @param condEq - SCL ConductingEquipment
* @returns Three letter primary apparatus device type as defined in IEC 61850-6.
*/
export function typeStr(condEq: Element): string {
if (containsGroundedTerminal(condEq) || (condEq.getAttribute('type') === 'DIS' && ( containsEarthSwitchDefinition (condEq)))) {
// these checks only carried out for a three phase system
return 'ERS';
} else {
return condEq.getAttribute('type') ?? '';
}
}

function typeName(condEq: Element): string {
Expand Down
12 changes: 1 addition & 11 deletions src/zeroline/foundation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { BayEditor } from './bay-editor.js';
import { SubstationEditor } from './substation-editor.js';
import { VoltageLevelEditor } from './voltage-level-editor.js';
import { typeStr } from '../wizards/conductingequipment.js';
danyill marked this conversation as resolved.
Show resolved Hide resolved

function containsReference(element: Element, iedName: string): boolean {
return Array.from(element.getElementsByTagName('LNode'))
Expand Down Expand Up @@ -223,17 +224,6 @@ export function getIcon(condEq: Element): TemplateResult {
return typeIcons[typeStr(condEq)] ?? generalConductingEquipmentIcon;
}

function typeStr(condEq: Element): string {
if (
condEq.getAttribute('type') === 'DIS' &&
condEq.querySelector('Terminal')?.getAttribute('cNodeName') === 'grounded'
) {
return 'ERS';
} else {
return condEq.getAttribute('type') ?? '';
}
}

const typeIcons: Partial<Record<string, TemplateResult>> = {
CBR: circuitBreakerIcon,
DIS: disconnectorIcon,
Expand Down
Loading