19#include "../../utils/Path.h"
23 m_elementType = TYPE_LINE;
24 for (
int i = 0; i < 2; i++) {
25 for (
int j = 0; j < 3; j++) { m_electricalData.faultCurrent[i][j] = std::complex<double>(0.0, 0.0); }
29Line::Line(wxString name) :
Branch()
31 m_elementType = TYPE_LINE;
32 for (
int i = 0; i < 2; i++) {
33 for (
int j = 0; j < 3; j++) { m_electricalData.faultCurrent[i][j] = std::complex<double>(0.0, 0.0); }
35 m_electricalData.name = name;
112 wxGraphicsMatrix identityMatrix = gc->GetTransform();
113 identityMatrix.Set();
115 wxColour elementColour;
118 elementColour = guiColour->eventElement;
120 elementColour = guiColour->enabled;
123 elementColour = guiColour->disable;
125 std::vector<wxPoint2DDouble> pointList = m_pointList;
126 if (!m_inserted && pointList.size() > 0) {
127 wxPoint2DDouble secondPoint = m_position;
128 if (pointList.size() > 2) { secondPoint = pointList[2]; }
129 pointList[1] =
GetSwitchPoint(m_parentList[0], pointList[0], secondPoint);
130 pointList.push_back(m_position);
135 gc->SetPen(wxPen(guiColour->selection, 2 + m_borderSize * 2.0));
136 gc->SetBrush(*wxTRANSPARENT_BRUSH);
137 if (pointList.size() > 0)
138 gc->StrokeLines(pointList.size(), &pointList[0]);
141 gc->SetPen(*wxTRANSPARENT_PEN);
142 gc->SetBrush(wxBrush(guiColour->selection));
143 if (pointList.size() > 0) {
144 DrawDCCircle(pointList[0], 5.0 + m_borderSize / scale, 10, gc);
145 if (m_inserted) {
DrawDCCircle(pointList[pointList.size() - 1], 5.0 + m_borderSize / scale, 10, gc); }
150 gc->SetPen(wxPen(elementColour, 2));
151 gc->SetBrush(*wxTRANSPARENT_BRUSH);
152 if (pointList.size() > 0)
153 gc->StrokeLines(pointList.size(), &pointList[0]);
161 gc->SetPen(*wxTRANSPARENT_PEN);
162 gc->SetBrush(wxBrush(elementColour));
163 if (pointList.size() > 0) {
165 if (m_inserted) {
DrawDCCircle(pointList[pointList.size() - 1], 5.0, 10, gc); }
171 gc->SetTransform(identityMatrix);
173 for (
int i = 2; i < (int)m_pointList.size() - 2; i++) {
184 wxColour elementColour;
187 elementColour = guiColour->eventElement;
189 elementColour = guiColour->enabled;
192 elementColour = guiColour->disable;
194 std::vector<wxPoint> pointList;
195 for (
auto pt : m_pointList) {
196 pointList.emplace_back(pt.m_x, pt.m_y);
201 dc.SetPen(wxPen(guiColour->selection, 2 + m_borderSize * 2.0));
202 dc.SetBrush(*wxTRANSPARENT_BRUSH);
203 if (pointList.size() > 0)
204 dc.DrawLines(pointList.size(), &pointList[0]);
207 dc.SetPen(*wxTRANSPARENT_PEN);
208 dc.SetBrush(wxBrush(guiColour->selection));
209 if (pointList.size() > 0) {
210 DrawDCCircle(pointList[0], 5.0 + m_borderSize / scale, dc);
211 if (m_inserted) {
DrawDCCircle(pointList[pointList.size() - 1], 5.0 + m_borderSize / scale, dc); }
216 dc.SetPen(wxPen(elementColour, 2));
217 dc.SetBrush(*wxTRANSPARENT_BRUSH);
218 if (pointList.size() > 0)
219 dc.DrawLines(pointList.size(), &pointList[0]);
227 dc.SetPen(*wxTRANSPARENT_PEN);
228 dc.SetBrush(wxBrush(elementColour));
229 if (pointList.size() > 0) {
231 if (m_inserted) {
DrawDCCircle(pointList[pointList.size() - 1], 5.0, dc); }
237 if (!m_parentList[0]) {
238 m_pointList[0] = m_movePts[0] + position - m_moveStartPt;
239 UpdateSwitchesPosition();
240 UpdatePowerFlowArrowsPosition();
242 if (!m_parentList[1]) {
243 m_pointList[m_pointList.size() - 1] = m_movePts[m_pointList.size() - 1] + position - m_moveStartPt;
244 UpdateSwitchesPosition();
245 UpdatePowerFlowArrowsPosition();
248 if (!m_parentList[0] && !m_parentList[1]) {
249 for (
int i = 2; i < (int)m_pointList.size() - 2; i++) {
250 m_pointList[i] = m_movePts[i] + position - m_moveStartPt;
259 if (m_parentList.size() == 0) {
260 m_position = position;
261 m_parentList.push_back(parent);
263 wxPoint2DDouble parentPt =
267 m_pointList.push_back(parentPt);
268 m_pointList.push_back(
GetSwitchPoint(parent, parentPt, m_position));
270 wxRect2DDouble genRect(0, 0, 0, 0);
271 m_switchRect.push_back(genRect);
274 Bus* parentBus =
static_cast<Bus*
>(parent);
275 m_electricalData.nominalVoltage = parentBus->GetElectricalData().nominalVoltage;
276 m_electricalData.nominalVoltageUnit = parentBus->GetElectricalData().nominalVoltageUnit;
281 else if (parent != m_parentList[0]) {
282 Bus* parentBus =
static_cast<Bus*
>(parent);
283 if (m_electricalData.nominalVoltage != parentBus->GetElectricalData().nominalVoltage ||
284 m_electricalData.nominalVoltageUnit != parentBus->GetElectricalData().nominalVoltageUnit) {
285 wxMessageDialog msgDialog(
nullptr,
286 _(
"Unable to connect two buses with different nominal voltages.\n"
287 "Use a transformer or edit the bus properties."),
288 _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
289 msgDialog.ShowModal();
293 m_parentList.push_back(parent);
295 wxPoint2DDouble parentPt =
301 wxPoint2DDouble secondPoint = parentPt;
302 if (m_pointList.size() > 2) { secondPoint = m_pointList[2]; }
303 m_pointList[1] =
GetSwitchPoint(m_parentList[0], m_pointList[0], secondPoint);
306 m_pointList.push_back(
GetSwitchPoint(parent, parentPt, m_pointList[m_pointList.size() - 1]));
308 m_pointList.push_back(parentPt);
310 wxRect2DDouble genRect(0, 0, 0, 0);
311 m_switchRect.push_back(genRect);
315 UpdatePowerFlowArrowsPosition();
323 for (
auto it = m_pointList.begin(); it != m_pointList.end(); ++it) {
324 if (rect.Contains(*it))
return true;
332 for (
int i = 2; i < (int)m_pointList.size() - 2; i++) {
333 if (m_activePickboxID == i) {
334 m_pointList[i] = m_movePts[i] + position - m_moveStartPt;
335 UpdateSwitchesPosition();
336 UpdatePowerFlowArrowsPosition();
342 for (
int i = 2; i < (int)m_pointList.size() - 2; i++) {
343 wxRect2DDouble rect(m_pointList[i].m_x - 5.0, m_pointList[i].m_y - 5.0, 10.0, 10.0);
344 if (rect.Contains(position)) {
345 m_activePickboxID = i;
354 if (m_parentList.size() != 0) { m_pointList.push_back(point); }
359 m_moveStartPt = position;
360 m_movePts = m_pointList;
367 if (parent == m_parentList[0]) {
368 m_pointList[0] = m_movePts[0] + position - m_moveStartPt;
371 else if (parent == m_parentList[1]) {
372 m_pointList[m_pointList.size() - 1] = m_movePts[m_pointList.size() - 1] + position - m_moveStartPt;
377 for (
int i = 2; i < (int)m_pointList.size() - 1; i++) {
378 m_pointList[i] = m_movePts[i] + position - m_moveStartPt;
384 if (m_activeNodeID == 1) {
385 m_pointList[0] = m_movePts[0] + position - m_moveStartPt;
386 if (m_parentList[0]) {
387 m_parentList[0]->RemoveChild(
this);
388 m_parentList[0] =
nullptr;
392 else if (m_activeNodeID == 2) {
393 m_pointList[m_pointList.size() - 1] = m_movePts[m_pointList.size() - 1] + position - m_moveStartPt;
394 if (m_parentList[1]) {
395 m_parentList[1]->RemoveChild(
this);
396 m_parentList[1] =
nullptr;
403 UpdateSwitchesPosition();
404 UpdatePowerFlowArrowsPosition();
414 wxString busName[2] = {
"?",
"?" };
415 if (m_parentList.size() == 2) {
417 for (
Element* element : m_parentList) {
419 Bus* bus =
static_cast<Bus*
>(element);
420 busName[i] = bus->GetElectricalData().name;
426 wxMenu* textMenu =
new wxMenu();
428 textMenu->Append(ID_TXT_NAME, _(
"Name"));
429 textMenu->Append(ID_TXT_BRANCH_ACTIVE_POWER_1_2, _(
"Active power (") + busName[0] + _(
" to ") + busName[1] + wxT(
")"));
430 textMenu->Append(ID_TXT_BRANCH_ACTIVE_POWER_2_1, _(
"Active power (") + busName[1] + _(
" to ") + busName[0] + wxT(
")"));
431 textMenu->Append(ID_TXT_BRANCH_REACTIVE_POWER_1_2, _(
"Reactive power (") + busName[0] + _(
" to ") + busName[1] + wxT(
")"));
432 textMenu->Append(ID_TXT_BRANCH_REACTIVE_POWER_2_1, _(
"Reactive power (") + busName[1] + _(
" to ") + busName[0] + wxT(
")"));
433 textMenu->Append(ID_TXT_BRANCH_LOSSES, _(
"Losses"));
434 textMenu->Append(ID_TXT_BRANCH_CURRENT_1_2, _(
"Current (") + busName[0] + _(
" to ") + busName[1] + wxT(
")"));
435 textMenu->Append(ID_TXT_BRANCH_CURRENT_2_1, _(
"Current (") + busName[1] + _(
" to ") + busName[0] + wxT(
")"));
436 textMenu->Append(ID_TXT_BRANCH_FAULT_CURRENT_1_2, _(
"Fault current (") + busName[0] + _(
" to ") + busName[1] + wxT(
")"));
437 textMenu->Append(ID_TXT_BRANCH_FAULT_CURRENT_2_1, _(
"Fault current (") + busName[1] + _(
" to ") + busName[0] + wxT(
")"));
438 textMenu->SetClientData(menu.GetClientData());
439 menu.AppendSubMenu(textMenu, _(
"Add text"));
442 wxMenuItem* addNodeItem =
new wxMenuItem(&menu,
ID_LINE_ADD_NODE, _(
"Insert node"));
443 addNodeItem->SetBitmap(wxImage(Paths::GetDataPath() +
"/images/menu/addNode16.png"));
444 menu.Append(addNodeItem);
448 addNodeItem->SetBitmap(wxImage(Paths::GetDataPath() +
"/images/menu/removeNode16.png"));
449 menu.Append(addNodeItem);
451 wxMenuItem* deleteItem =
new wxMenuItem(&menu,
ID_DELETE, _(
"Delete"));
452 deleteItem->SetBitmap(wxImage(Paths::GetDataPath() +
"/images/menu/delete16.png"));
453 menu.Append(deleteItem);
457void Line::RemoveNode(wxPoint2DDouble point)
460 for (
int i = 2; i < (int)m_pointList.size() - 2; i++) {
461 if (m_activePickboxID == i) {
462 m_pointList.erase(m_pointList.begin() + i);
467 UpdateSwitchesPosition();
468 UpdatePowerFlowArrowsPosition();
471void Line::AddNode(wxPoint2DDouble point)
473 int segmentNumber = 0;
475 if (segmentNumber > 0 && segmentNumber < (
int)m_pointList.size() - 2) {
476 m_pointList.insert(m_pointList.begin() + segmentNumber + 1, point);
478 UpdateSwitchesPosition();
479 UpdatePowerFlowArrowsPosition();
484 if (m_pointList.size() > 0) {
486 leftUp = m_pointList[0];
487 rightBottom = m_pointList[0];
488 for (
int i = 1; i < (int)m_pointList.size(); i++) {
489 if (m_pointList[i].m_x < leftUp.m_x) leftUp.m_x = m_pointList[i].m_x;
490 if (m_pointList[i].m_y < leftUp.m_y) leftUp.m_y = m_pointList[i].m_y;
491 if (m_pointList[i].m_x > rightBottom.m_x) rightBottom.m_x = m_pointList[i].m_x;
492 if (m_pointList[i].m_y > rightBottom.m_y) rightBottom.m_y = m_pointList[i].m_y;
500 lineForm.CenterOnParent();
501 if (lineForm.ShowModal() == wxID_OK) {
509 if (nominalVoltage.size() > 0) {
510 m_electricalData.nominalVoltage = nominalVoltage[0];
511 m_electricalData.nominalVoltageUnit = nominalVoltageUnit[0];
517 if (m_activeNodeID == 1 && parent == m_parentList[0])
return false;
518 if (m_activeNodeID == 2 && parent == m_parentList[1])
return false;
520 if (parent && m_activeNodeID != 0) {
521 wxRect2DDouble nodeRect(0, 0, 0, 0);
522 if (m_activeNodeID == 1) {
523 nodeRect = wxRect2DDouble(m_pointList[0].m_x - 5.0 - m_borderSize, m_pointList[0].m_y - 5.0 - m_borderSize,
524 10 + 2.0 * m_borderSize, 10 + 2.0 * m_borderSize);
526 if (m_activeNodeID == 2) {
527 nodeRect = wxRect2DDouble(m_pointList[m_pointList.size() - 1].m_x - 5.0 - m_borderSize,
528 m_pointList[m_pointList.size() - 1].m_y - 5.0 - m_borderSize,
529 10 + 2.0 * m_borderSize, 10 + 2.0 * m_borderSize);
535 Bus* parentBus =
static_cast<Bus*
>(parent);
536 if (!m_parentList[0] && !m_parentList[1]) {
537 m_electricalData.nominalVoltage = parentBus->GetElectricalData().nominalVoltage;
538 m_electricalData.nominalVoltageUnit = parentBus->GetElectricalData().nominalVoltageUnit;
540 else if (m_electricalData.nominalVoltage != parentBus->GetElectricalData().nominalVoltage ||
541 m_electricalData.nominalVoltageUnit != parentBus->GetElectricalData().nominalVoltageUnit) {
542 wxMessageDialog msgDialog(
nullptr,
543 _(
"Unable to connect two buses with different nominal voltages.\n"
544 "Use a transformer or edit the bus properties."),
545 _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
546 msgDialog.ShowModal();
551 if (m_activeNodeID == 1) {
553 if (m_parentList[1] == parent) {
558 m_parentList[0] = parent;
562 m_pointList[0], -parent->
GetAngle());
565 m_pointList[0] = parentPt;
567 UpdateSwitchesPosition();
568 UpdatePowerFlowArrowsPosition();
571 if (m_activeNodeID == 2) {
572 if (m_parentList[0] == parent) {
577 m_parentList[1] = parent;
579 wxPoint2DDouble parentPt =
583 m_pointList[m_pointList.size() - 1] = parentPt;
585 UpdateSwitchesPosition();
586 UpdatePowerFlowArrowsPosition();
591 if (m_activeNodeID == 1) m_parentList[0] =
nullptr;
592 if (m_activeNodeID == 2) m_parentList[1] =
nullptr;
600 m_pfDirection = pfDirection;
601 UpdatePowerFlowArrowsPosition();
604void Line::UpdatePowerFlowArrowsPosition()
606 std::vector<wxPoint2DDouble> edges;
607 switch (m_pfDirection) {
609 m_powerFlowArrow.clear();
612 for (
int i = 1; i < (int)m_pointList.size() - 1; i++) { edges.push_back(m_pointList[i]); }
615 for (
int i = (
int)m_pointList.size() - 2; i > 0; i--) { edges.push_back(m_pointList[i]); }
625 double rotAngle = m_rotationAngle;
626 if (!clockwise) rotAngle = -m_rotationAngle;
628 if (parent == m_parentList[0]) {
631 else if (parent == m_parentList[1]) {
632 m_pointList[m_pointList.size() - 1] = parent->
RotateAtPosition(m_pointList[m_pointList.size() - 1], rotAngle);
634 UpdateSwitchesPosition();
635 UpdatePowerFlowArrowsPosition();
640 m_pointList = pointList;
641 UpdateSwitchesPosition();
642 UpdatePowerFlowArrowsPosition();
654 wxString tipText = m_electricalData.name;
659 busNumber[0] =
static_cast<Bus*
>(m_parentList[0])->GetElectricalData().number + 1;
660 busNumber[1] =
static_cast<Bus*
>(m_parentList[1])->GetElectricalData().number + 1;
662 tipText += _(
"\nP") + wxString::Format(
"(%d-%d) = ", busNumber[0], busNumber[1]) +
663 wxString::FromDouble(m_electricalData.powerFlow[0].real(), 5) + _(
" p.u.");
664 tipText += _(
"\nQ") + wxString::Format(
"(%d-%d) = ", busNumber[0], busNumber[1]) +
665 wxString::FromDouble(m_electricalData.powerFlow[0].imag(), 5) + _(
" p.u.");
666 tipText += _(
"\nP") + wxString::Format(
"(%d-%d) = ", busNumber[1], busNumber[0]) +
667 wxString::FromDouble(m_electricalData.powerFlow[1].real(), 5) + _(
" p.u.");
668 tipText += _(
"\nQ") + wxString::Format(
"(%d-%d) = ", busNumber[1], busNumber[0]) +
669 wxString::FromDouble(m_electricalData.powerFlow[1].imag(), 5) + _(
" p.u.");
671 if (!m_electricalData.harmonicOrder.empty()) {
672 tipText += _(
"\n\nHarmonic currents:");
674 for (
auto& hCurrent1 : m_electricalData.harmonicCurrent[0]) {
675 auto& hCurrent2 = m_electricalData.harmonicCurrent[1][i];
677 i1.Printf(_(
"\nIh(%d)(%d-%d) = %.5e%s%.2f%s p.u."), m_electricalData.harmonicOrder[i], busNumber[0], busNumber[1], std::abs(hCurrent1), wxString(L
'\u2220'), wxRadToDeg(std::arg(hCurrent1)), wxString(L
'\u00B0'));
678 i2.Printf(_(
"\nIh(%d)(%d-%d) = %.5e%s%.2f%s p.u."), m_electricalData.harmonicOrder[i], busNumber[1], busNumber[0], std::abs(hCurrent2), wxString(L
'\u2220'), wxRadToDeg(std::arg(hCurrent2)), wxString(L
'\u00B0'));
693 data.name = m_electricalData.name;
695 data.nominalPower = m_electricalData.nominalPower;
696 data.nominalPowerUnit = m_electricalData.nominalPowerUnit;
698 data.nominalVoltage = m_electricalData.nominalVoltage;
699 data.nominalVoltageUnit = m_electricalData.nominalVoltageUnit;
701 data.useLinePower = m_electricalData.useLinePower;
703 data.lineSize = m_electricalData.lineSize;
705 data.resistance = m_electricalData.resistance;
706 data.resistanceUnit = m_electricalData.resistanceUnit;
708 data.indReactance = m_electricalData.indReactance;
709 data.indReactanceUnit = m_electricalData.indReactanceUnit;
711 data.capSusceptance = m_electricalData.capSusceptance;
712 data.capSusceptanceUnit = m_electricalData.capSusceptanceUnit;
714 data.zeroResistance = m_electricalData.zeroResistance;
715 data.zeroIndReactance = m_electricalData.zeroIndReactance;
716 data.zeroCapSusceptance = m_electricalData.zeroCapSusceptance;
718 data.powerFlow[0] = m_electricalData.powerFlow[0];
719 data.powerFlow[1] = m_electricalData.powerFlow[1];
721 data.harmonicOrder = m_electricalData.harmonicOrder;
722 data.harmonicCurrent[0] = m_electricalData.harmonicCurrent[0];
723 data.harmonicCurrent[1] = m_electricalData.harmonicCurrent[1];
726 double lineBasePower = GetValueFromUnit(data.nominalPower, data.nominalPowerUnit);
727 double baseVoltage = GetValueFromUnit(data.nominalVoltage, data.nominalVoltageUnit);
728 double systemBaseImpedance = (baseVoltage * baseVoltage) / systemBasePower;
729 double lineBaseImpedance = (baseVoltage * baseVoltage) / lineBasePower;
732 double r = data.resistance;
733 if (data.resistanceUnit == ElectricalUnit::UNIT_OHM_km) data.resistance = r * data.lineSize / systemBaseImpedance;
734 else if (data.resistanceUnit == ElectricalUnit::UNIT_PU) {
735 if (data.useLinePower) data.resistance = (r * lineBaseImpedance) / systemBaseImpedance;
738 data.resistance = r / systemBaseImpedance;
740 data.resistanceUnit = ElectricalUnit::UNIT_PU;
743 double x = data.indReactance;
744 if (data.indReactanceUnit == ElectricalUnit::UNIT_OHM_km) data.indReactance = x * data.lineSize / systemBaseImpedance;
745 else if (data.indReactanceUnit == ElectricalUnit::UNIT_PU) {
746 if (data.useLinePower) data.indReactance = (x * lineBaseImpedance) / systemBaseImpedance;
749 data.indReactance = x / systemBaseImpedance;
751 data.indReactanceUnit = ElectricalUnit::UNIT_PU;
754 double b = data.capSusceptance;
755 if (data.capSusceptanceUnit == ElectricalUnit::UNIT_S_km) data.capSusceptance = b * data.lineSize * systemBaseImpedance;
756 else if (data.capSusceptanceUnit == ElectricalUnit::UNIT_PU) {
757 if (data.useLinePower) data.capSusceptance = (b / lineBaseImpedance) * systemBaseImpedance;
760 data.capSusceptance = b * systemBaseImpedance;
762 data.capSusceptanceUnit = ElectricalUnit::UNIT_PU;
767 double r0 = data.zeroResistance;
768 if (data.useLinePower) data.zeroResistance = (r0 * lineBaseImpedance) / systemBaseImpedance;
771 double x0 = data.zeroIndReactance;
772 if (data.useLinePower) data.zeroIndReactance = (x0 * lineBaseImpedance) / systemBaseImpedance;
775 double b0 = data.zeroCapSusceptance;
776 if (data.useLinePower) data.zeroCapSusceptance = (b0 / lineBaseImpedance) * systemBaseImpedance;
779 data.powerFlow[0] = std::complex<double>(0, 0);
780 data.powerFlow[1] = std::complex<double>(0, 0);
781 data.faultCurrent[0][0] = std::complex<double>(0, 0);
782 data.faultCurrent[0][1] = std::complex<double>(0, 0);
783 data.faultCurrent[0][2] = std::complex<double>(0, 0);
784 data.faultCurrent[1][0] = std::complex<double>(0, 0);
785 data.faultCurrent[1][1] = std::complex<double>(0, 0);
786 data.faultCurrent[1][2] = std::complex<double>(0, 0);
792rapidxml::xml_node<>* Line::SaveElement(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementListNode)
794 auto elementNode = XMLParser::AppendNode(doc, elementListNode,
"Line");
795 XMLParser::SetNodeAttribute(doc, elementNode,
"ID", m_elementID);
796 auto cadProp = XMLParser::AppendNode(doc, elementNode,
"CADProperties");
797 auto nodeList = XMLParser::AppendNode(doc, cadProp,
"NodeList");
800 for (
unsigned int i = 0; i < m_pointList.size(); i++) {
803 if ((i != 1) && (i != m_pointList.size() - 2)) {
804 auto nodePos = XMLParser::AppendNode(doc, nodeList,
"Node");
805 XMLParser::SetNodeAttribute(doc, nodePos,
"ID", nodeID);
806 auto nodePosX = XMLParser::AppendNode(doc, nodePos,
"X");
807 XMLParser::SetNodeValue(doc, nodePosX, m_pointList[i].m_x);
808 auto nodePosY = XMLParser::AppendNode(doc, nodePos,
"Y");
809 XMLParser::SetNodeValue(doc, nodePosY, m_pointList[i].m_y);
814 auto parentIDList = XMLParser::AppendNode(doc, cadProp,
"ParentIDList");
815 for (
unsigned int i = 0; i < m_parentList.size(); i++) {
816 if (m_parentList[i]) {
817 auto parentID = XMLParser::AppendNode(doc, parentIDList,
"ParentID");
818 XMLParser::SetNodeAttribute(doc, parentID,
"ID",
static_cast<int>(i));
819 XMLParser::SetNodeValue(doc, parentID, m_parentList[i]->
GetID());
823 auto electricalProp = XMLParser::AppendNode(doc, elementNode,
"ElectricalProperties");
824 auto isOnline = XMLParser::AppendNode(doc, electricalProp,
"IsOnline");
825 XMLParser::SetNodeValue(doc, isOnline, m_online);
826 auto name = XMLParser::AppendNode(doc, electricalProp,
"Name");
827 XMLParser::SetNodeValue(doc, name, m_electricalData.name);
828 auto nominalVoltage = XMLParser::AppendNode(doc, electricalProp,
"NominalVoltage");
829 XMLParser::SetNodeValue(doc, nominalVoltage, m_electricalData.nominalVoltage);
830 XMLParser::SetNodeAttribute(doc, nominalVoltage,
"UnitID",
static_cast<int>(m_electricalData.nominalVoltageUnit));
831 auto nominalPower = XMLParser::AppendNode(doc, electricalProp,
"NominalPower");
832 XMLParser::SetNodeValue(doc, nominalPower, m_electricalData.nominalPower);
833 XMLParser::SetNodeAttribute(doc, nominalPower,
"UnitID",
static_cast<int>(m_electricalData.nominalPowerUnit));
834 auto resistance = XMLParser::AppendNode(doc, electricalProp,
"Resistance");
835 XMLParser::SetNodeValue(doc, resistance, m_electricalData.resistance);
836 XMLParser::SetNodeAttribute(doc, resistance,
"UnitID",
static_cast<int>(m_electricalData.resistanceUnit));
837 auto indReactance = XMLParser::AppendNode(doc, electricalProp,
"IndReactance");
838 XMLParser::SetNodeValue(doc, indReactance, m_electricalData.indReactance);
839 XMLParser::SetNodeAttribute(doc, indReactance,
"UnitID",
static_cast<int>(m_electricalData.indReactanceUnit));
840 auto capSusceptance = XMLParser::AppendNode(doc, electricalProp,
"CapSusceptance");
841 XMLParser::SetNodeValue(doc, capSusceptance, m_electricalData.capSusceptance);
842 XMLParser::SetNodeAttribute(doc, capSusceptance,
"UnitID",
static_cast<int>(m_electricalData.capSusceptanceUnit));
843 auto lineSize = XMLParser::AppendNode(doc, electricalProp,
"LineSize");
844 XMLParser::SetNodeValue(doc, lineSize, m_electricalData.lineSize);
845 auto useLinePower = XMLParser::AppendNode(doc, electricalProp,
"UseLinePower");
846 XMLParser::SetNodeValue(doc, useLinePower, m_electricalData.useLinePower);
848 auto fault = XMLParser::AppendNode(doc, electricalProp,
"Fault");
849 auto zeroResistance = XMLParser::AppendNode(doc, fault,
"ZeroResistance");
850 XMLParser::SetNodeValue(doc, zeroResistance, m_electricalData.zeroResistance);
851 auto zeroIndReactance = XMLParser::AppendNode(doc, fault,
"ZeroIndReactance");
852 XMLParser::SetNodeValue(doc, zeroIndReactance, m_electricalData.zeroIndReactance);
853 auto zeroCapSusceptance = XMLParser::AppendNode(doc, fault,
"ZeroCapSusceptance");
854 XMLParser::SetNodeValue(doc, zeroCapSusceptance, m_electricalData.zeroCapSusceptance);
856 SaveSwitchingData(doc, electricalProp);
861bool Line::OpenElement(rapidxml::xml_node<>* elementNode, std::vector<Element*> parentList)
863 auto cadPropNode = elementNode->first_node(
"CADProperties");
864 if (!cadPropNode)
return false;
867 std::vector<wxPoint2DDouble> ptsList;
868 auto nodePosList = cadPropNode->first_node(
"NodeList");
869 if (!nodePosList)
return false;
870 auto nodePos = nodePosList->first_node(
"Node");
872 double nodePosX = XMLParser::GetNodeValueDouble(nodePos,
"X");
873 double nodePosY = XMLParser::GetNodeValueDouble(nodePos,
"Y");
874 ptsList.push_back(wxPoint2DDouble(nodePosX, nodePosY));
875 nodePos = nodePos->next_sibling(
"Node");
879 auto parentIDList = cadPropNode->first_node(
"ParentIDList");
880 if (!parentIDList)
return false;
881 auto parentNode = parentIDList->first_node(
"ParentID");
882 long parentID[2] = { -1, -1 };
885 wxString(parentNode->first_attribute(
"ID")->value()).ToCLong(&index);
886 wxString(parentNode->value()).ToCLong(&parentID[index]);
887 parentNode = parentNode->next_sibling(
"ParentID");
890 std::vector<wxPoint2DDouble> nodePtsList;
891 nodePtsList.push_back(ptsList[0]);
892 nodePtsList.push_back(ptsList[ptsList.size() - 1]);
895 std::vector<Bus*> dummyBusList;
896 for (
unsigned int i = 0; i < nodePtsList.size(); ++i) {
897 if (parentID[i] == -1)
899 Bus* dummyBus =
new Bus(nodePtsList[i]);
900 dummyBusList.push_back(dummyBus);
904 AddParent(parentList[parentID[i]], nodePtsList[i]);
909 std::vector<wxPoint2DDouble> midPts;
910 for (
unsigned int i = 1; i < ptsList.size() - 1; i++) midPts.push_back(ptsList[i]);
911 m_pointList.insert(m_pointList.begin() + 2, midPts.begin(), midPts.end());
915 for (
auto it = dummyBusList.begin(), itEnd = dummyBusList.end(); it != itEnd; ++it) {
919 dummyBusList.clear();
921 auto electricalProp = elementNode->first_node(
"ElectricalProperties");
922 if (!electricalProp)
return false;
924 SetOnline(XMLParser::GetNodeValueInt(electricalProp,
"IsOnline"));
925 m_electricalData.name = electricalProp->first_node(
"Name")->value();
926 m_electricalData.nominalVoltage = XMLParser::GetNodeValueDouble(electricalProp,
"NominalVoltage");
927 m_electricalData.nominalVoltageUnit =
928 static_cast<ElectricalUnit
>(XMLParser::GetAttributeValueInt(electricalProp,
"NominalVoltage",
"UnitID"));
929 m_electricalData.nominalPower = XMLParser::GetNodeValueDouble(electricalProp,
"NominalPower");
930 m_electricalData.nominalPowerUnit =
931 static_cast<ElectricalUnit
>(XMLParser::GetAttributeValueInt(electricalProp,
"NominalPower",
"UnitID"));
932 m_electricalData.resistance = XMLParser::GetNodeValueDouble(electricalProp,
"Resistance");
933 m_electricalData.resistanceUnit =
934 static_cast<ElectricalUnit
>(XMLParser::GetAttributeValueInt(electricalProp,
"Resistance",
"UnitID"));
935 m_electricalData.indReactance = XMLParser::GetNodeValueDouble(electricalProp,
"IndReactance");
936 m_electricalData.indReactanceUnit =
937 static_cast<ElectricalUnit
>(XMLParser::GetAttributeValueInt(electricalProp,
"IndReactance",
"UnitID"));
938 m_electricalData.capSusceptance = XMLParser::GetNodeValueDouble(electricalProp,
"CapSusceptance");
939 m_electricalData.capSusceptanceUnit =
940 static_cast<ElectricalUnit
>(XMLParser::GetAttributeValueInt(electricalProp,
"CapSusceptance",
"UnitID"));
941 m_electricalData.lineSize = XMLParser::GetNodeValueDouble(electricalProp,
"LineSize");
942 m_electricalData.useLinePower = XMLParser::GetNodeValueInt(electricalProp,
"UseLinePower");
944 auto fault = electricalProp->first_node(
"Fault");
945 m_electricalData.zeroResistance = XMLParser::GetNodeValueDouble(fault,
"ZeroResistance");
946 m_electricalData.zeroIndReactance = XMLParser::GetNodeValueDouble(fault,
"ZeroIndReactance");
947 m_electricalData.zeroCapSusceptance = XMLParser::GetNodeValueDouble(fault,
"ZeroCapSusceptance");
949 if (!OpenSwitchingData(electricalProp))
return false;
PowerFlowDirection
Direction of power flow arrows.
Abstract class for branch power elements.
virtual void UpdateSwitches()
Update the switch position.
virtual void RemoveParent(Element *parent)
Remove a parent.
Node for power elements. All others power elements are connected through this.
Base class of all elements of the program. This class is responsible for manage graphical and his dat...
virtual bool Intersects(wxRect2DDouble rect) const =0
Check if the element's rect intersects other rect.
virtual int GetID() const
Get the element ID.
virtual double PointToLineDistance(wxPoint2DDouble point, int *segmentNumber=nullptr) const
Calculate the distance between a line (formed by point list) and a point.
wxPoint2DDouble GetPosition() const
Get the element position.
double GetAngle() const
Get the element angle.
virtual wxPoint2DDouble RotateAtPosition(wxPoint2DDouble pointToRotate, double angle, bool degrees=true) const
Rotate a point as element position being the origin.
virtual void DrawDCPickbox(wxPoint2DDouble position, wxGraphicsContext *gc) const
Draw a point.
virtual wxPoint2DDouble WorldToScreen(wxPoint2DDouble translation, double scale, double offsetX=0.0, double offsetY=0.0) const
Convert the element position to screen position.
virtual void AddChild(Element *child)
Add a child to the child list.
bool SetOnline(bool online=true)
Set if the element is online or offline.
virtual void DrawDCCircle(wxPoint2DDouble position, double radius, int numSegments, wxGraphicsContext *gc) const
Draw a circle using device context.
void SetInserted(bool inserted=true)
Set if the element is properly inserted in the workspace.
virtual void AddPoint(wxPoint2DDouble point)
Add point to the list of points that connect the element to the bus.
virtual bool Contains(wxPoint2DDouble position) const
Checks if the element contains a position.
virtual bool Intersects(wxRect2DDouble rect) const
Check if the element's rect intersects other rect.
virtual void SetNominalVoltage(std::vector< double > nominalVoltage, std::vector< ElectricalUnit > nominalVoltageUnit)
Set nominal voltage of the element.
virtual bool AddParent(Element *parent, wxPoint2DDouble position)
Add a parent to the element. This method must be used on power elements that connect to a bus,...
virtual void Move(wxPoint2DDouble position)
Move the element other position.
virtual void SetPowerFlowDirection(PowerFlowDirection pfDirection)
Set the direction of the power flow.
virtual wxString GetTipText() const
Get the tip text.
virtual bool SetNodeParent(Element *parent)
Set a perent to the node. If all conditions are met, a new parent are added to the element and the po...
virtual void DrawDC(GUIColour *guiColour, wxPoint2DDouble translation, double scale, wxGraphicsContext *gc) const
Draw the element using GDI+.
virtual bool ShowForm(wxWindow *parent, Element *element, wxWindow *workspace=nullptr)
Show element data form.
virtual bool GetContextMenu(wxMenu &menu)
Get the element contex menu.
virtual void CalculateBoundaries(wxPoint2DDouble &leftUp, wxPoint2DDouble &rightBottom) const
Calculate the element boundaries.
virtual void RotateNode(Element *parent, bool clockwise=true)
Rotate a node.
virtual void StartMove(wxPoint2DDouble position)
Update the element attributes related to the movement.
virtual void MoveNode(Element *parent, wxPoint2DDouble position)
Move a node. StartMove(wxPoint2DDouble position) before start moving.
virtual bool PickboxContains(wxPoint2DDouble position)
Check if a pickbox contains a point. If contains the attributes related to pickbox movement will be c...
virtual void SetPointList(std::vector< wxPoint2DDouble > pointList)
Set the list of points that connect the element to the bus.
virtual void MovePickbox(wxPoint2DDouble position)
Move the pickbox.
virtual Element * GetCopy()
Get a the element copy.
virtual void SetDynamicEvent(bool dynEvent=true)
Set if the power element have dynamic event.
virtual void CalculatePowerFlowPts(std::vector< wxPoint2DDouble > edges)
Calculate the points of the power flow arrows.
virtual void DrawDCPowerFlowPts(GUIColour *guiColour, wxGraphicsContext *gc) const
Draw power flow arrows.
virtual void DrawDCSwitches(GUIColour *guiColour, wxGraphicsContext *gc) const
Draw switch.
virtual wxPoint2DDouble GetSwitchPoint(Element *parent, wxPoint2DDouble parentPoint, wxPoint2DDouble secondPoint) const
Get the correct switch position.
std::vector< double > swTime