diff --git a/src/editors/SingleLineDiagram.ts b/src/editors/SingleLineDiagram.ts index 07f5cf8c36..065091eb87 100644 --- a/src/editors/SingleLineDiagram.ts +++ b/src/editors/SingleLineDiagram.ts @@ -4,12 +4,18 @@ import { LitElement, property, query, + state, TemplateResult, } from 'lit-element'; import panzoom from 'panzoom'; -import { identity, newWizardEvent, SCLTag } from '../foundation.js'; +import { + compareNames, + identity, + newWizardEvent, + SCLTag +} from '../foundation.js'; import { getAbsolutePosition, createTerminalElement, @@ -26,13 +32,23 @@ import { getAbsolutePositionTerminal, drawCNodeConnections, getConnectivityNodesDrawingPosition, + createSubstationElement, } from './singlelinediagram/sld-drawing.js'; import { isBusBar, getConnectedTerminals, getPathNameAttribute, + getNameAttribute, + getDescriptionAttribute, } from './singlelinediagram/foundation.js'; +import {isSCLNamespace} from "../schemas.js"; import { wizards } from '../wizards/wizard-library.js'; +import {SingleSelectedEvent} from "@material/mwc-list/mwc-list-foundation"; +import {translate} from "lit-translate"; + +import '@material/mwc-list/mwc-list-item'; +import '@material/mwc-select'; +import '@material/mwc-textfield'; /** * Main class plugin for Single Line Diagram editor. @@ -42,206 +58,287 @@ export default class SingleLineDiagramPlugin extends LitElement { @property({ attribute: false }) doc!: XMLDocument; + @state() + selectedSubstation: Element | undefined; + + // Container for giving the panzoom to. + @query('#panzoom') panzoomContainer!: HTMLElement; + // The main canvas to draw everything on. + @query('#svg') svg!: SVGElement; + + private get substations() : Element[] { + return Array.from(this.doc.querySelectorAll(':root > Substation')) + .sort((a,b) => compareNames(a,b)); + } + /** - * Get all the BusBars from the document. + * Get all the Power Transformers from an element. */ - private get busBars(): Element[] { - return Array.from(this.doc.querySelectorAll('Bay')).filter(bay => - isBusBar(bay) - ); + private getPowerTransformers(parentElement: Element): Element[] { + return Array.from(parentElement.querySelectorAll('PowerTransformer')) + .filter(isSCLNamespace); } /** - * Get all the bays from the document. + * Get all the Voltage Levels from the substation. */ - private get bays(): Element[] { - return Array.from(this.doc.querySelectorAll('Bay')).filter( - bay => !isBusBar(bay) - ); + private getVoltageLevels(substationElement: Element): Element[] { + return Array.from(substationElement.querySelectorAll('VoltageLevel')) + .filter(isSCLNamespace); } /** - * Get all the VoltageLevels from the SCL document. + * Get all the BusBars from the voltage level. */ - private get voltageLevels(): Element[] { - return Array.from(this.doc.querySelectorAll('VoltageLevel')); + private getBusBars(voltageLevelElement: Element): Element[] { + return Array.from(voltageLevelElement.querySelectorAll('Bay')) + .filter(isSCLNamespace) + .filter(bay => isBusBar(bay)); } - // Container for giving the panzoom to. - @query('#panzoom') panzoomContainer!: HTMLElement; - // The main canvas to draw everything on. - @query('#svg') svg!: HTMLElement; + /** + * Get all the bays from the voltage level. + */ + private getBays(voltageLevelElement: Element): Element[] { + return Array.from(voltageLevelElement.querySelectorAll('Bay')) + .filter(isSCLNamespace) + .filter(bay => !isBusBar(bay)); + } /** - * Add an element to a specific element. - * @param elementToAdd - The element to add. - * @param groupName - Identity sting if the element + * Get all the Conducting Equipment from a Bay. + * @param bayElement - The Bay to search in. */ - private addElementToGroup( - elementToAdd: Element, - identity: string | number - ): void { - this.svg - .querySelectorAll(`g[id="${identity}"]`) - .forEach(group => group.appendChild(elementToAdd)); + private getConductingEquipments(bayElement: Element): Element[] { + return Array.from(bayElement.querySelectorAll('ConductingEquipment')) + .filter(isSCLNamespace); } /** - * Draw all available Voltage Levels of this SCL document. - * Should only be a element. + * Get all the Connectivity Nodes from a Bay/Busbar. + * @param bayElement - The Bay/Busbar to search in. */ - private drawVoltageLevels(): void { - this.voltageLevels.forEach(voltageLevel => { - const voltageLevelElement = createVoltageLevelElement(voltageLevel); - this.svg.appendChild(voltageLevelElement); - }); + private getConnectivityNode(bayElement: Element): Element[] { + return Array.from(bayElement.querySelectorAll('ConnectivityNode')) + .filter(isSCLNamespace) + .filter(cNode => cNode.getAttribute('name') !== 'grounded'); } /** - * Draw all available Bays of this SCL document. - * Should only be a element. + * Search for Equipment (ConductionEquipment or PowerTransformer) which has a terminal wth a connectivityNode + * tha is the same as the passed pathName. + * @param parentElement - The Element to search in for Equipment. + * @param pathName - The PathName to search for in the Terminal. */ - private drawBays(): void { - this.bays.forEach(bay => { - const bayElement = createBayElement(bay); + private findEquipment(parentElement: Element, pathName: string | undefined): Element[] { + return Array.from(parentElement.querySelectorAll('ConductingEquipment, PowerTransformer')) + .filter(isSCLNamespace) + .filter(element => element.querySelector(`Terminal[connectivityNode="${pathName}"]`)); + } - this.addElementToGroup(bayElement, identity(bay.parentElement)); - }); + /** + * Draw all equipment and connections of the selected Substation. + */ + private drawSubstation(): void { + const substationGroup = createSubstationElement(this.selectedSubstation!); + this.svg.appendChild(substationGroup); + + this.drawPowerTransformers(this.selectedSubstation!, substationGroup); + this.drawVoltageLevels(this.selectedSubstation!, substationGroup); } /** - * Draw all available `PowerTransformer`s of this SCL document. + * Draw all available `PowerTransformer`s of passed parent element. * Should only be a element. + * @param parentElement - The parent element to search for PowerTransformers. + * @param parentGroup - The SVG Group to which to add the PowerTransformer. */ - private drawPowerTransformers(): void { - Array.from(this.doc.querySelectorAll('PowerTransformer')).forEach( - powerTransformer => { - const powerTransformerElement = - createPowerTransformerElement(powerTransformer); - - if (powerTransformer.parentElement?.tagName === 'Substation') - this.svg.appendChild(powerTransformerElement); - else - this.addElementToGroup( - powerTransformerElement, - identity(powerTransformer.parentElement) - ); - } - ); + private drawPowerTransformers(parentElement: Element, parentGroup: SVGElement): void { + this.getPowerTransformers(parentElement) + .forEach(powerTransformerElement => this.drawPowerTransformer(parentGroup, powerTransformerElement)); } /** - * Draw all available Conducting Equipments of this SCL document. + * Draw an SVG from the passed PowerTransformer Element. * Should only be a element. + * @param parentGroup - The SVG Group to which to add the PowerTransformer. + * @param powerTransformerElement - The PowerTransformer to draw. */ - private drawConductingEquipments(): void { - Array.from(this.doc.querySelectorAll('ConductingEquipment')) - .filter( - child => - Array.from(child.querySelectorAll('Terminal')).filter( - terminal => terminal.getAttribute('cNodeName') !== 'grounded' - ).length !== 0 - ) - .forEach(equipment => { - const eqElement = createConductingEquipmentElement(equipment, () => - this.openEditWizard(equipment!) - ); - - this.addElementToGroup(eqElement, identity(equipment.parentElement)); - }); + private drawPowerTransformer(parentGroup: SVGElement, powerTransformerElement: Element): void { + const powerTransformerGroup = createPowerTransformerElement(powerTransformerElement); + parentGroup.appendChild(powerTransformerGroup); } /** - * Draw all available Connectivity Nodes of this SCL document. + * Draw all available Voltage Levels of the passed Substation Element. + * Should only be a element. + * @param substationElement - The substation containing the voltage levels. + * @param substationGroup - The group to which to add the SVGs. */ - private drawConnectivityNodes(): void { - this.bays.forEach(bay => { - Array.from(bay.querySelectorAll('ConnectivityNode')) - .filter(cNode => cNode.getAttribute('name') !== 'grounded') - .filter(cNode => getConnectedTerminals(cNode).length > 0) - .forEach(cNode => { - const cNodeElement = createConnectivityNodeElement(cNode, () => - this.openEditWizard(cNode) - ); + private drawVoltageLevels(substationElement: Element, substationGroup: SVGElement): void { + // First draw all the devices on the SVG for all voltage levels. + this.getVoltageLevels(substationElement) + .forEach(voltageLevelElement => { + const voltageLevelGroup = createVoltageLevelElement(voltageLevelElement); + substationGroup.appendChild(voltageLevelGroup); + + this.drawPowerTransformers(voltageLevelElement, voltageLevelGroup); + this.drawBays(voltageLevelElement, voltageLevelGroup); + this.drawBusBars(voltageLevelElement, voltageLevelGroup); + }); - this.addElementToGroup(cNodeElement, identity(cNode.parentElement)); - }); - }); + // After all devices are drawn we can draw the connections between the devices. + this.getVoltageLevels(substationElement) + .forEach(voltageLevelElement => { + this.getBusBars(voltageLevelElement).forEach( busbarElement => { + this.drawBusBarConnections(substationElement, substationGroup, busbarElement); + }); + + this.getBays(voltageLevelElement).forEach( bayElement => { + this.drawBayConnections(substationElement, substationGroup, bayElement); + }); + }); + } + + /** + * Draw all available Bays of the passed Voltage Level Element. + * Should only be a element. + * @param voltageLevelElement - The Voltage Level containing the bays. + * @param voltageLevelGroup - The group to which to add the SVGs. + * */ + private drawBays(voltageLevelElement: Element, voltageLevelGroup: SVGElement): void { + this.getBays(voltageLevelElement) + .forEach(bayElement => { + const bayGroup = createBayElement(bayElement); + voltageLevelGroup.appendChild(bayGroup); + + this.drawPowerTransformers(bayElement, bayGroup); + this.drawConductingEquipments(bayElement, bayGroup); + this.drawConnectivityNodes(bayElement, bayGroup); + }); } /** - * Draw all available Bus Bars of this SCL document. + * Draw all available Conducting Equipments of the passed Bay Element. + * Should only be a element. + * @param bayElement - The Bay containing the Conducting Equipment. + * @param bayGroup - The group to which to add the SVGs. */ - private drawBusBars(): void { - this.busBars.forEach(busBar => { - const busBarElement = createBusBarElement( - busBar, - getBusBarLength(busBar.parentElement ?? this.doc) - ); - - this.addElementToGroup(busBarElement, identity(busBar.parentElement)); - }); + private drawConductingEquipments(bayElement: Element, bayGroup: SVGElement): void { + this.getConductingEquipments(bayElement) + .filter(conductingEquipmentElement => + Array.from(conductingEquipmentElement.querySelectorAll('Terminal')) + .filter( + terminal => terminal.getAttribute('cNodeName') !== 'grounded' + ).length !== 0) + .forEach(conductingEquipmentElement => { + const conductingEquipmentGroup = createConductingEquipmentElement(conductingEquipmentElement, () => + this.openEditWizard(conductingEquipmentElement!) + ); + bayGroup.appendChild(conductingEquipmentGroup); + }); } - private drawConnectivityNodeConnections(): void { - this.bays.forEach(bay => { - Array.from(bay.querySelectorAll('ConnectivityNode')) - .filter(cNode => cNode.getAttribute('name') !== 'grounded') - .forEach(cNode => { - Array.from( - this.doc.querySelectorAll('ConductingEquipment, PowerTransformer') - ) - .filter(element => - element.querySelector( - `Terminal[connectivityNode="${cNode.getAttribute('pathName')}"]` - ) - ) - .forEach(element => { - const sides = getDirections(element, cNode); + /** + * Draw all available Connectivity Nodes of the passed Bay Element. + * @param bayElement - The Bay containing the Connectivity Nodes. + * @param bayGroup - The group to which to add the SVGs. + * */ + private drawConnectivityNodes(bayElement: Element, bayGroup: SVGElement): void { + this.getConnectivityNode(bayElement) + .filter(cNode => getConnectedTerminals(cNode).length > 0) + .forEach(cNode => { + const cNodegroup = createConnectivityNodeElement(cNode, () => + this.openEditWizard(cNode) + ); - const elementsTerminalPosition = getAbsolutePositionTerminal( - element, - sides.startDirection - ); + bayGroup.appendChild(cNodegroup); + }); + } - const cNodePosition = getConnectivityNodesDrawingPosition( - cNode, - sides.endDirection + /** + * Draw all connections between the different Equipment in the Bay and the Bay has with other Equipment outside + * the bay. + * @param rootElement - The Element containing all the other elements to which the Bay is connected. + * @param rootGroup - The SVG Element that contains all groups from the elements to add path to. + * @param bayElement - The Bay that holds the Connectivity Node to connect with. + */ + private drawBayConnections(rootElement: Element, rootGroup: SVGElement, bayElement: Element): void { + this.getConnectivityNode(bayElement) + .forEach(cNode => { + this.findEquipment(rootElement, getPathNameAttribute(cNode)) + .forEach(element => { + const sides = getDirections(element, cNode); + + const elementsTerminalPosition = getAbsolutePositionTerminal( + element, + sides.startDirection + ); + + const cNodePosition = getConnectivityNodesDrawingPosition( + cNode, + sides.endDirection + ); + + rootGroup + .querySelectorAll(`g[id="${identity(bayElement)}"]`) + .forEach(eq => + drawCNodeConnections( + cNodePosition, + elementsTerminalPosition, + eq + ) ); - drawCNodeConnections( - cNodePosition, - elementsTerminalPosition, - this.svg - ); + const terminalElement = element.querySelector( + `Terminal[connectivityNode="${cNode.getAttribute('pathName')}"]` + ); - const terminalElement = element.querySelector( - `Terminal[connectivityNode="${cNode.getAttribute('pathName')}"]` - ); + const terminal = createTerminalElement( + terminalElement!, + sides.startDirection, + () => this.openEditWizard(terminalElement!) + ); - const terminal = createTerminalElement( - terminalElement!, - sides.startDirection, - () => this.openEditWizard(terminalElement!) - ); + rootGroup + .querySelectorAll(`g[id="${identity(element)}"]`) + .forEach(eq => eq.appendChild(terminal)); + }); + }); + } - this.svg - .querySelectorAll(`g[id="${identity(element)}"]`) - .forEach(eq => eq.appendChild(terminal)); - }); - }); - }); + /** + * Draw all available Busbars of the passed Voltage Level Element. + * @param voltageLevelElement - The Voltage Level containing the Busbars. + * @param voltageLevelGroup - The group to which to add the SVGs. + */ + private drawBusBars(voltageLevelElement: Element, voltageLevelGroup: SVGElement): void { + this.getBusBars(voltageLevelElement) + .forEach(busbarElement => this.drawBusBar(voltageLevelElement, voltageLevelGroup, busbarElement)); } - private drawBusBarConnections(): void { - this.busBars.forEach(busBar => { - const pathName = getPathNameAttribute(busBar.children[0]); - const busBarPosition = getAbsolutePositionBusBar(busBar); + /** + * Draw an SVG of the passed Busbar Element. + * @param parentElement - The parent (Voltage Level) Element that is used to determine the length of the Busbar. + * @param parentGroup - The group to which to add the line. + * @param busbarElement - The Busbar Element to draw. + */ + private drawBusBar(parentElement: Element, parentGroup: SVGElement, busbarElement: Element): void { + const busBarGroup = createBusBarElement(busbarElement, getBusBarLength(parentElement)); + parentGroup.appendChild(busBarGroup); + } + + /** + * Draw all the connections a Busbar has with other Equipment. + * @param rootElement - The Element containing all the other elements to which the Busbar is connected. + * @param rootGroup - The SVG Element that contains all groups from the elements to add path to. + * @param busbarElement - The Busbar that holds the Connectivity Node to connect with. + */ + private drawBusBarConnections(rootElement: Element, rootGroup: SVGElement, busbarElement: Element): void { + const pathName = getPathNameAttribute(busbarElement.children[0]); + const busBarPosition = getAbsolutePositionBusBar(busbarElement); - Array.from(this.doc.querySelectorAll('ConductingEquipment')) - .filter(cEquipment => - cEquipment.querySelector(`Terminal[connectivityNode="${pathName}"]`) - ) + this.findEquipment(rootElement, pathName) .forEach(element => { const elementPosition = getAbsolutePosition(element); @@ -262,11 +359,15 @@ export default class SingleLineDiagramPlugin extends LitElement { `Terminal[connectivityNode="${pathName}"]` ); - drawBusBarRoute( - busbarTerminalPosition, - elementsTerminalPosition, - this.svg - ); + rootGroup + .querySelectorAll(`g[id="${identity(busbarElement)}"]`) + .forEach(eq => + drawBusBarRoute( + busbarTerminalPosition, + elementsTerminalPosition, + eq + ) + ); const terminal = createTerminalElement( terminalElement!, @@ -274,26 +375,28 @@ export default class SingleLineDiagramPlugin extends LitElement { () => this.openEditWizard(terminalElement!) ); - this.svg - .querySelectorAll(` g[id="${identity(element)}"]`) + rootGroup + .querySelectorAll(`g[id="${identity(element)}"]`) .forEach(eq => eq.appendChild(terminal)); }); - }); } /** - * Draw all the Substation elements. + * Remove all the child elements (and descendants) from the SVG Element, to have a clean start. + */ + private clearSVG(): void { + while (this.svg.firstChild) { + this.svg.removeChild(this.svg.lastChild!); + } + } + + /** + * Draw all the elements of the selected Substation. */ drawSubstationElements(): void { - this.drawVoltageLevels(); - this.drawBays(); - this.drawConductingEquipments(); - this.drawPowerTransformers(); - this.drawConnectivityNodes(); - this.drawBusBars(); - - this.drawConnectivityNodeConnections(); - this.drawBusBarConnections(); + // First clean the existing drawing, because the selected substation may have changed. + this.clearSVG(); + this.drawSubstation(); } /** @@ -310,21 +413,101 @@ export default class SingleLineDiagramPlugin extends LitElement { this.drawSubstationElements(); } + onSelect(event: SingleSelectedEvent): void { + // Set the selected Substation. + this.selectedSubstation = this.substations[event.detail.index]; + this.drawSubstationElements(); + } + + private renderSubstationSelector(): TemplateResult { + const substationList = this.substations; + if (substationList.length > 0) { + if (this.selectedSubstation === undefined) { + this.selectedSubstation = this.substations[0]; + } + + if (substationList.length > 1) { + const selectedSubstationName = getNameAttribute(this.selectedSubstation); + return html ` + + ${this.substations.map( + substation => { + const name = getNameAttribute(substation); + const description = getDescriptionAttribute(substation); + return html` + + ${name}${description !== undefined ? ' (' + description + ')' : ''} + ` + })} + + `; + } + + const name = getNameAttribute(this.selectedSubstation); + const description = getDescriptionAttribute(this.selectedSubstation); + return html ` + + + `; + } + return html ` +

+ ${translate('substation.missing')} +

+ ` + } + render(): TemplateResult { // TODO: Width and Height should be a percentage, not fixed height/width. - return html`
-
- -
-
`; + return html ` + ${this.renderSubstationSelector()} + +
+
+ +
+
`; } static styles = css` + h1 { + color: var(--mdc-theme-on-surface); + font-family: 'Roboto', sans-serif; + font-weight: 300; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + margin: 0px; + line-height: 48px; + padding-left: 0.3em; + } + + #substationSelector { + width: 30vw; + margin: 0.67em 0 0 0.67em; + } + + #selectedSubstation { + width: 30vw; + margin: 0.67em 0 0 0.67em; + } + + #noSubstationSelector { + color: var(--base1) + } + .sldContainer { overflow: hidden; } diff --git a/src/editors/singlelinediagram/foundation.ts b/src/editors/singlelinediagram/foundation.ts index d457b2ee4c..f13b0729bd 100644 --- a/src/editors/singlelinediagram/foundation.ts +++ b/src/editors/singlelinediagram/foundation.ts @@ -104,10 +104,10 @@ export function getConnectedTerminals(element: Element): Element[] { return Array.from(substationElement.getElementsByTagName('Terminal')).filter( terminal => terminal.getAttribute('connectivityNode') === path && - terminal.getAttribute('substationName') === substationName && - terminal.getAttribute('voltageLevelName') === voltageLevelName && - terminal.getAttribute('bayName') === bayName && - terminal.getAttribute('cNodeName') === getNameAttribute(element) + terminal.getAttribute('cNodeName') === getNameAttribute(element) && + (!terminal.hasAttribute('substationName') || terminal.getAttribute('substationName') === substationName) && + (!terminal.hasAttribute('voltageLevelName') || terminal.getAttribute('voltageLevelName') === voltageLevelName) && + (!terminal.hasAttribute('bayName') || terminal.getAttribute('bayName') === bayName) ); } diff --git a/src/editors/singlelinediagram/sld-drawing.ts b/src/editors/singlelinediagram/sld-drawing.ts index b66f324180..f2b058f48d 100644 --- a/src/editors/singlelinediagram/sld-drawing.ts +++ b/src/editors/singlelinediagram/sld-drawing.ts @@ -85,10 +85,10 @@ export function getAbsolutePositionConnectivityNode(element: Element): Point { * Calculate the absolute offset of a terminal next to an element. * @param parentElementPosition - The position of the parent element of the terminal. * @param elementOffset - The offset of the parent element. - * @param terminalSide - The side of the parent element where the terminal should be placed. + * @param terminalSide - The side of the parent element where the terminal should be placed. * @param customTerminalOffset - An optional parameter containing the offset of the terminal next to the parent element. * This may vary, for example for Connectivity Nodes. - * + * * @returns The absolute position of the terminal. */ function absoluteOffsetTerminal( @@ -199,6 +199,15 @@ function createGroupElement(element: Element): SVGElement { return finalElement; } +/** + * Create a Substation element. + * @param substation - The Substation from the SCL document to use. + * @returns A Substation element. + */ +export function createSubstationElement(substation: Element): SVGElement { + return createGroupElement(substation); +} + /** * Create a Voltage Level element. * @param voltageLevel - The Voltage Level from the SCL document to use. @@ -433,12 +442,12 @@ export function createConnectivityNodeElement( * Draw a route from ConnectivityNode to equipments Terminal (ConductingEquipment or PowerTransformer) * @param cNodesTerminalPosition - The start position in px of the SCL element ConnectivityNode. * @param equipmentsTerminalPosition - The end position in px of the SCL element ConductingEquipment or PowerTransformer. - * @param svgToDrawOn - The SVG to draw the route on. + * @param svgElementToDrawOn - The SVG Element to draw the route on. */ export function drawCNodeConnections( cNodesTerminalPosition: Point, equipmentsTerminalPosition: Point, - svgToDrawOn: HTMLElement + svgElementToDrawOn: SVGElement ): void { const path = getOrthogonalPath( equipmentsTerminalPosition, @@ -464,19 +473,19 @@ export function drawCNodeConnections( // Inserting elements like this works kind of like z-index (not supported in SVG yet), // these elements are placed behind all other elements. // By doing it like this, all other elements are hoverable for example. - svgToDrawOn.insertAdjacentElement('afterbegin', line); + svgElementToDrawOn.insertAdjacentElement('afterbegin', line); } /** * Draw a route from the bus bar to elements terminal position. * @param busbarsTerminalPosition - The start position in px the bus bar. * @param equipmentsTerminalPosition - The end position in px of the SCL element ConductingEquipment or PowerTransformer. - * @param svgToDrawOn - The SVG to draw the route on. + * @param svgElementToDrawOn - The SVG Element to draw the route on. */ export function drawBusBarRoute( busbarsTerminalPosition: Point, equipmentsTerminalPosition: Point, - svgToDrawOn: HTMLElement + svgElementToDrawOn: SVGElement ): void { const path = [busbarsTerminalPosition].concat([equipmentsTerminalPosition]); @@ -495,7 +504,7 @@ export function drawBusBarRoute( line.setAttribute('stroke', 'currentColor'); line.setAttribute('stroke-width', '1.5'); - svgToDrawOn.appendChild(line); + svgElementToDrawOn.appendChild(line); } /** @@ -555,7 +564,7 @@ export function getParentElementName( * @param root - Either the whole SCL file or the voltage level where the bus bar resides * @returns - the length of the bus bar */ -export function getBusBarLength(root: Element | XMLDocument): number { +export function getBusBarLength(root: Element): number { return ( Math.max( ...Array.from( diff --git a/src/schemas.ts b/src/schemas.ts index cf5c73cbf5..22f4bf7b5d 100644 --- a/src/schemas.ts +++ b/src/schemas.ts @@ -15,6 +15,16 @@ export function newEmptySCD( return new DOMParser().parseFromString(markup, 'application/xml'); } +export const SCL_NAMESPACE= "http://www.iec.ch/61850/2003/SCL"; + +/** + * Check if the namespace of the passed element is the standard SCL Namespace. + * @param element - The element to check. + */ +export function isSCLNamespace(element: Element): boolean { + return element.namespaceURI === SCL_NAMESPACE; +} + export interface ValidationError { file: string; line: number; @@ -2367,9 +2377,9 @@ export const schemas = { SCL schema version "2007" revision "B" release 1, for IEC 61850-6 Ed. 2.1. Draft 2014-07-18. - + COPYRIGHT (c) IEC, 2014. All rights reserved. Disclaimer: The IEC disclaims liability for any personal injury, property or other damages of any nature whatsoever, whether special, indirect, consequential or compensatory, directly or indirectly resulting from this software and the document upon which its methods are based, use of, or reliance upon. - + Implemented Ed. 2 Tissues: 658, 668, 687, 768, 779, 789, 804, 806, 807, 822, 824, 845, 853, 855, 856, 857, 886, 936, 1175, 1189, 1208. Tissues not relevant for the SCL schema: 660, 661 (Ed.3), 663, 678, 699, 700, 705, 706 (Ed.3), 718, 719, 721, 731, 733, 752, 769, 787, 788, 815, 823, 825, 837, 847, 865, 873, 883, 884, 885, 901, 914, 915, 918, 927 (Ed.3), 930, 938, 949, 961, 1048, 1054, 1059, 1118, 1130, 1131, 1147, 1161, 1168, 1170 (Ed.3), 1173, 1188, 1195, 1200, 1204, 1207, 1221, 1224, 1241 (Ed.3), 1255, 1257 (Ed.3), 1284. @@ -2444,7 +2454,7 @@ export const schemas = { SCL schema version "2007" revision "B" release 1, for IEC 61850-6 Ed. 2.1. Draft 2014-07-18. - + COPYRIGHT (c) IEC, 2014. All rights reserved. Disclaimer: The IEC disclaims liability for any personal injury, property or other damages of any nature whatsoever, whether special, indirect, consequential or compensatory, directly or indirectly resulting from this software and the document upon which its methods are based, use of, or reliance upon. @@ -3174,7 +3184,7 @@ export const schemas = { SCL schema version "2007" revision "B" release 1, for IEC 61850-6 Ed. 2.1. Draft 2014-07-18. - + COPYRIGHT (c) IEC, 2014. All rights reserved. Disclaimer: The IEC disclaims liability for any personal injury, property or other damages of any nature whatsoever, whether special, indirect, consequential or compensatory, directly or indirectly resulting from this software and the document upon which its methods are based, use of, or reliance upon. @@ -3325,7 +3335,7 @@ export const schemas = { SCL schema version "2007" revision "B" release 1, for IEC 61850-6 Ed. 2.1. Draft 2014-07-18. - + COPYRIGHT (c) IEC, 2014. All rights reserved. Disclaimer: The IEC disclaims liability for any personal injury, property or other damages of any nature whatsoever, whether special, indirect, consequential or compensatory, directly or indirectly resulting from this software and the document upon which its methods are based, use of, or reliance upon. @@ -4021,7 +4031,7 @@ export const schemas = { SCL schema version "2007" revision "B" release 1, for IEC 61850-6 Ed. 2.1. Draft 2014-07-18. - + COPYRIGHT (c) IEC, 2014. All rights reserved. Disclaimer: The IEC disclaims liability for any personal injury, property or other damages of any nature whatsoever, whether special, indirect, consequential or compensatory, directly or indirectly resulting from this software and the document upon which its methods are based, use of, or reliance upon. @@ -4203,7 +4213,7 @@ export const schemas = { SCL schema version "2007" revision "B" release 1, for IEC 61850-6 Ed. 2.1. Draft 2014-07-18. - + COPYRIGHT (c) IEC, 2014. All rights reserved. Disclaimer: The IEC disclaims liability for any personal injury, property or other damages of any nature whatsoever, whether special, indirect, consequential or compensatory, directly or indirectly resulting from this software and the document upon which its methods are based, use of, or reliance upon. @@ -5090,7 +5100,7 @@ export const schemas = { SCL schema version "2007" revision "B" release 1, for IEC 61850-6 Ed. 2.1. Draft 2014-07-18. - + COPYRIGHT (c) IEC, 2014. All rights reserved. Disclaimer: The IEC disclaims liability for any personal injury, property or other damages of any nature whatsoever, whether special, indirect, consequential or compensatory, directly or indirectly resulting from this software and the document upon which its methods are based, use of, or reliance upon. @@ -5449,7 +5459,7 @@ export const schemas = { SCL schema version "2007" revision "B" release 1, for IEC 61850-6 Ed. 2.1. Draft 2014-07-18. - + COPYRIGHT (c) IEC, 2014. All rights reserved. Disclaimer: The IEC disclaims liability for any personal injury, property or other damages of any nature whatsoever, whether special, indirect, consequential or compensatory, directly or indirectly resulting from this software and the document upon which its methods are based, use of, or reliance upon. diff --git a/src/translations/de.ts b/src/translations/de.ts index 7de1a1e3cc..8108cc105a 100644 --- a/src/translations/de.ts +++ b/src/translations/de.ts @@ -392,6 +392,9 @@ export const de: Translations = { updatedesc: { abb: 'Signalbeschreibungen zu ABB IEDs hinzugefügt', }, + sld: { + substationSelector: 'Schaltanlage auswählen', + }, add: 'Hinzufügen', new: 'Neu', remove: 'Entfernen', diff --git a/src/translations/en.ts b/src/translations/en.ts index 3a1c9fa638..1a03abdf1d 100644 --- a/src/translations/en.ts +++ b/src/translations/en.ts @@ -389,6 +389,9 @@ export const en = { updatedesc: { abb: 'Added signal descriptions to ABB IEDs', }, + sld: { + substationSelector: 'Select a substation', + }, add: 'Add', new: 'New', remove: 'Remove', diff --git a/test/unit/editors/singlelinediagram/sld-drawing.test.ts b/test/unit/editors/singlelinediagram/sld-drawing.test.ts index f91599c90b..32122f7be1 100644 --- a/test/unit/editors/singlelinediagram/sld-drawing.test.ts +++ b/test/unit/editors/singlelinediagram/sld-drawing.test.ts @@ -188,20 +188,13 @@ describe('Single Line Diagram drawing', () => { describe('defines a getBusBarLength function that', () => { it('returns a correct length for the bus bar given voltage level as root', () => { - const element = doc.querySelector('VoltageLevel[name="J1"]') ?? doc; + const element = doc.querySelector('VoltageLevel[name="J1"]')!; expect(getBusBarLength(element)).to.eql( 18 * 2 * SVG_GRID_SIZE + (SVG_GRID_SIZE - EQUIPMENT_SIZE) / 2 + SVG_GRID_SIZE ); }); - it('returns a correct length for the bus bar given XMLDocument as root', () => { - expect(getBusBarLength(doc)).to.eql( - 18 * 2 * SVG_GRID_SIZE + - (SVG_GRID_SIZE - EQUIPMENT_SIZE) / 2 + - SVG_GRID_SIZE - ); - }); }); describe('creates a group element for every given PowerTransformer element that', () => { diff --git a/test/unit/schema.test.ts b/test/unit/schema.test.ts new file mode 100644 index 0000000000..079a321e61 --- /dev/null +++ b/test/unit/schema.test.ts @@ -0,0 +1,19 @@ +import {expect} from "@open-wc/testing"; + +import {isSCLNamespace, SCL_NAMESPACE} from "../../src/schemas.js"; + +describe('schema', () => { + it('when element belongs to SCL Namespace, function should return true', () => { + const doc = document.implementation.createDocument(SCL_NAMESPACE, null, null); + const element = doc.createElementNS(SCL_NAMESPACE, "SCL"); + + expect(isSCLNamespace(element)).to.be.true; + }); + + it('when element not belonging to SCL Namespace, function should return false', () => { + const doc = document.implementation.createDocument(SCL_NAMESPACE, null, null); + const element = doc.createElementNS("https://someother.namespace.com", "SCL"); + + expect(isSCLNamespace(element)).to.be.false; + }); +});