Power System Platform  2026w34a-beta
Loading...
Searching...
No Matches
Bus.cpp
1/*
2 * Copyright (C) 2017 Thales Lima Oliveira <thales@ufu.br>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18#include "Bus.h"
19#ifdef USING_WX_3_0_X
20#include "utils/DegreesAndRadians.h"
21#endif
22
23Bus::Bus() : PowerElement() { m_elementType = TYPE_BUS; }
24Bus::Bus(wxPoint2DDouble position) : PowerElement()
25{
26 m_elementType = TYPE_BUS;
27 m_width = 100.0;
28 m_height = 5.0;
29 SetPosition(position);
30}
31
32Bus::Bus(wxPoint2DDouble position, wxString name)
33{
34 m_elementType = TYPE_BUS;
35 m_width = 100.0;
36 m_height = 5.0;
37 SetPosition(position);
38
39 m_electricalData.name = name;
40}
41
42Bus::~Bus() {}
43
44void Bus::DrawDC(GUIColour* guiColour, wxPoint2DDouble translation, double scale, wxGraphicsContext* gc) const
45{
46 wxPoint2DDouble gcPosition = m_position - wxPoint2DDouble(m_width / 2.0, m_height / 2.0);
47 wxGraphicsMatrix identityMatrix = gc->GetTransform();
48 identityMatrix.Set(); // Set to identity
49
50 gc->SetPen(*wxTRANSPARENT_PEN);
51
52 // Draw selection (layer 1)
53 if (m_selected) {
54 // If the object is selected, the matrix is reset to remove scale effects applied to it, thus keeping the
55 // edges with fixed sizes for all zoom levels.
56 gc->PushState();
57 gc->SetTransform(identityMatrix);
58 // The matrix was reset, so we must use screen coordinates (WorldToScreen).
59 wxPoint2DDouble screenPt = WorldToScreen(translation, scale);
60 gc->Translate(screenPt.m_x, screenPt.m_y);
61 gc->Rotate(wxDegToRad(m_angle));
62 gc->Translate(-screenPt.m_x, -screenPt.m_y);
63
64 gc->SetBrush(wxBrush(guiColour->selection));
65
66 wxPoint2DDouble pts[4] = { WorldToScreen(translation, scale, -(m_width / 2.0), -(m_height / 2.0)) -
67 wxPoint2DDouble(m_borderSize, m_borderSize),
68 WorldToScreen(translation, scale, -(m_width / 2.0), (m_height / 2.0)) -
69 wxPoint2DDouble(m_borderSize, -m_borderSize),
70 WorldToScreen(translation, scale, (m_width / 2.0), (m_height / 2.0)) -
71 wxPoint2DDouble(-m_borderSize, -m_borderSize),
72 WorldToScreen(translation, scale, (m_width / 2.0), -(m_height / 2.0)) -
73 wxPoint2DDouble(-m_borderSize, m_borderSize) };
74 gc->DrawLines(4, pts);
75 gc->PopState();
76 }
77
78 gc->PushState();
79 gc->Translate(m_position.m_x, m_position.m_y);
80 gc->Rotate(wxDegToRad(m_angle));
81 gc->Translate(-m_position.m_x, -m_position.m_y);
82
83
84 if (!m_electricalData.isConnected)
85 gc->SetBrush(wxBrush(guiColour->disable));
86 //else if (m_dynEvent)
87 // gc->SetBrush(wxBrush(guiColour->eventElement));
88 else if (guiColour->useVoltageLevels)
89 gc->SetBrush(wxBrush(GetVoltageColour(guiColour)));
90 else
91 gc->SetBrush(wxBrush(guiColour->bus));
92
93 gc->DrawRectangle(gcPosition.m_x, gcPosition.m_y, m_width, m_height);
94
95 gc->PopState();
96
97 // Draw pickbox (layer 3)
98 if (m_showPickbox) {
99 gc->PushState();
100 gc->SetTransform(identityMatrix);
101
102 wxPoint2DDouble screenPt = WorldToScreen(translation, scale);
103 gc->Translate(screenPt.m_x, screenPt.m_y);
104 gc->Rotate(wxDegToRad(m_angle));
105 gc->Translate(-screenPt.m_x, -screenPt.m_y);
106
107 wxPoint2DDouble pbPosition[2] = { WorldToScreen(translation, scale, m_width / 2.0) - wxPoint2DDouble(4, 4),
108 WorldToScreen(translation, scale, -m_width / 2.0) - wxPoint2DDouble(4, 4) };
109 DrawDCPickbox(pbPosition[0], gc);
110 DrawDCPickbox(pbPosition[1], gc);
111
112 gc->PopState();
113 }
114
115 // Draw faulted bus symbol (layer 4)
116 if (m_electricalData.hasFault) {
117 gc->PushState();
118 gc->SetTransform(identityMatrix);
119
120 wxPoint2DDouble screenPt = WorldToScreen(translation, scale);
121 gc->Translate(screenPt.m_x, screenPt.m_y);
122 gc->Rotate(wxDegToRad(m_angle));
123 gc->Translate(-screenPt.m_x, -screenPt.m_y);
124
125 wxPoint2DDouble fsPosition = WorldToScreen(translation, scale, m_width / 2.0);
126
127 wxColour faultSymbolColour(255, 0, 0, 255);
128
129 double scale = 1.5;
130
131 gc->SetBrush(*wxRED_BRUSH);
132 gc->SetPen(*wxTRANSPARENT_PEN);
133 //gc->SetPen(*wxRED_PEN);
134
135 wxPoint2DDouble* points = new wxPoint2DDouble[4];
136 points[0] = wxPoint2DDouble(fsPosition.m_x + 1 * scale, fsPosition.m_y + 3 * scale);
137 points[1] = wxPoint2DDouble(fsPosition.m_x + 1 * scale, fsPosition.m_y - 3 * scale);
138 points[2] = wxPoint2DDouble(fsPosition.m_x + 11 * scale, fsPosition.m_y - 1 * scale);
139 points[3] = wxPoint2DDouble(fsPosition.m_x + 11 * scale, fsPosition.m_y + 5 * scale);
140
141 gc->DrawLines(4, points);
142
143 points[0] = wxPoint2DDouble(fsPosition.m_x + 7 * scale, fsPosition.m_y - 5 * scale);
144 points[1] = wxPoint2DDouble(fsPosition.m_x + 21 * scale, fsPosition.m_y + 3 * scale);
145 points[2] = wxPoint2DDouble(fsPosition.m_x + 7 * scale, fsPosition.m_y + 1 * scale);
146
147 gc->DrawLines(3, points);
148
149 delete[] points;
150
151 gc->PopState();
152 }
153
154 if (m_dynEvent) {
155 DrawStabilityEventGC(gc, translation, scale, guiColour, true);
156 }
157}
158
159void Bus::DrawDC(GUIColour* guiColour, wxPoint2DDouble translation, double scale, wxDC& dc) const
160{
161 //wxPoint2DDouble gcPosition = m_position - wxPoint2DDouble(m_width / 2.0, m_height / 2.0);
162 wxPoint2DDouble gcPosition = m_position;
163
164 dc.SetPen(*wxTRANSPARENT_PEN);
165
166 // Draw selection (layer 1)
167 if (m_selected) {
168 dc.SetBrush(wxBrush(guiColour->selection));
169
170 wxPoint2DDouble pts[4] = { WorldToScreen(translation, scale, -(m_width / 2.0), -(m_height / 2.0)) -
171 wxPoint2DDouble(m_borderSize, m_borderSize),
172 WorldToScreen(translation, scale, -(m_width / 2.0), (m_height / 2.0)) -
173 wxPoint2DDouble(m_borderSize, -m_borderSize),
174 WorldToScreen(translation, scale, (m_width / 2.0), (m_height / 2.0)) -
175 wxPoint2DDouble(-m_borderSize, -m_borderSize),
176 WorldToScreen(translation, scale, (m_width / 2.0), -(m_height / 2.0)) -
177 wxPoint2DDouble(-m_borderSize, m_borderSize) };
178
179 DrawDCRectangle(gcPosition, m_width + 2 * m_borderSize, m_height + 2 * m_borderSize, m_angle, dc);
180 }
181
182
183 if (!m_electricalData.isConnected)
184 dc.SetBrush(wxBrush(guiColour->disable));
185 else if (m_dynEvent || m_electricalData.hasFault)
186 dc.SetBrush(wxBrush(guiColour->eventElement));
187 else
188 dc.SetBrush(wxBrush(GetVoltageColour(guiColour)));
189
190 DrawDCRectangle(gcPosition, m_width, m_height, m_angle, dc);
191
192
193 // Draw faulted bus symbol (layer 4)
194 if (m_electricalData.hasFault) {
195
196 wxPoint2DDouble center = m_position;
197 wxPoint2DDouble fsPosition = center + wxPoint2DDouble(m_width / 2.0, 0.0);
198
199 wxColour faultSymbolColour(255, 0, 0, 255);
200
201 double localScale = 1.5;
202
203 dc.SetBrush(*wxRED_BRUSH);
204 dc.SetPen(*wxTRANSPARENT_PEN);
205 //gc->SetPen(*wxRED_PEN);
206
207 wxPoint points[4];
208
209 wxPoint2DDouble p;
210
211 // Primeiro polĂ­gono
212 p = wxPoint2DDouble(fsPosition.m_x + 1 * localScale, fsPosition.m_y + 3 * localScale);
213 points[0] = RotateAround(p, center, m_angle);
214 p = wxPoint2DDouble(fsPosition.m_x + 1 * localScale, fsPosition.m_y - 3 * localScale);
215 points[1] = RotateAround(p, center, m_angle);
216 p = wxPoint2DDouble(fsPosition.m_x + 11 * localScale, fsPosition.m_y - 1 * localScale);
217 points[2] = RotateAround(p, center, m_angle);
218 p = wxPoint2DDouble(fsPosition.m_x + 11 * localScale, fsPosition.m_y + 5 * localScale);
219 points[3] = RotateAround(p, center, m_angle);
220
221 dc.DrawPolygon(4, points);
222
223 p = wxPoint2DDouble(fsPosition.m_x + 7 * localScale, fsPosition.m_y - 5 * localScale);
224 points[0] = RotateAround(p, center, m_angle);
225 p = wxPoint2DDouble(fsPosition.m_x + 21 * localScale, fsPosition.m_y + 3 * localScale);
226 points[1] = RotateAround(p, center, m_angle);
227 p = wxPoint2DDouble(fsPosition.m_x + 7 * localScale, fsPosition.m_y + 1 * localScale);
228 points[2] = RotateAround(p, center, m_angle);
229
230 dc.DrawPolygon(3, points);
231 }
232}
233
234bool Bus::Contains(wxPoint2DDouble position) const
235{
236 wxPoint2DDouble ptR = RotateAtPosition(position, -m_angle);
237 return m_rect.Contains(ptR);
238}
239
240bool Bus::Intersects(wxRect2DDouble rect) const
241{
242 if (m_angle == 0.0 || m_angle == 180.0) return m_rect.Intersects(rect);
243
244 return RotatedRectanglesIntersects(m_rect, rect, m_angle, 0.0);
245}
246
247bool Bus::PickboxContains(wxPoint2DDouble position)
248{
249 m_activePickboxID = ID_PB_NONE;
250
251 wxPoint2DDouble ptR = RotateAtPosition(position, -m_angle);
252
253 wxPoint2DDouble center(m_position.m_x + m_width / 2.0, m_position.m_y);
254 wxRect2DDouble rectRight(center.m_x - 5.0, center.m_y - 5.0, 10.0, 10.0);
255
256 center = wxPoint2DDouble(m_position.m_x - m_width / 2.0, m_position.m_y);
257 wxRect2DDouble rectLeft(center.m_x - 5.0, center.m_y - 5.0, 10.0, 10.0);
258
259 if (rectRight.Contains(ptR)) {
260 m_activePickboxID = ID_PB_RIGHT;
261 return true;
262 }
263 if (rectLeft.Contains(ptR)) {
264 m_activePickboxID = ID_PB_LEFT;
265 return true;
266 }
267
268 return false;
269}
270
272{
273 double angle = m_angle;
274 while (angle >= 157.5) angle -= 180.0;
275
276 if (angle >= -22.5 && angle < 22.5)
277 return wxCursor(wxCURSOR_SIZEWE);
278 else if (angle >= 22.5 && angle < 67.5)
279 return wxCursor(wxCURSOR_SIZENWSE);
280 else if (angle >= 67.5 && angle < 112.5)
281 return wxCursor(wxCURSOR_SIZENS);
282 else if (angle >= 112.5 && angle < 157.5)
283 return wxCursor(wxCURSOR_SIZENESW);
284
285 return wxCursor(wxCURSOR_ARROW);
286}
287
288void Bus::MovePickbox(wxPoint2DDouble position)
289{
290 if (m_activePickboxID == ID_PB_NONE) return;
291
292 wxPoint2DDouble ptR = RotateAtPosition(position, -m_angle);
293
294 double dx = 0.0;
295 if (m_activePickboxID == ID_PB_RIGHT)
296 dx = ptR.m_x - m_position.m_x - m_width / 2.0;
297 else if (m_activePickboxID == ID_PB_LEFT)
298 dx = m_position.m_x - m_width / 2.0 - ptR.m_x;
299
300 if (m_width + dx < 20.0) return;
301
302 if (m_activePickboxID == ID_PB_RIGHT) {
303 m_position.m_x += (dx / 2.0) * std::cos(wxDegToRad(m_angle));
304 m_position.m_y += (dx / 2.0) * std::sin(wxDegToRad(m_angle));
305 }
306 else if (m_activePickboxID == ID_PB_LEFT) {
307 m_position.m_x -= (dx / 2.0) * std::cos(wxDegToRad(m_angle));
308 m_position.m_y -= (dx / 2.0) * std::sin(wxDegToRad(m_angle));
309 }
310 m_width += dx;
311
312 SetPosition(m_position);
313}
314
315void Bus::Rotate(bool clockwise)
316{
317 double rotAngle = m_rotationAngle;
318 if (!clockwise) rotAngle = -m_rotationAngle;
319
320 m_angle += rotAngle;
321 if (m_angle >= 360 || m_angle <= -360) m_angle = 0.0;
322}
323
324void Bus::AlignToGrid(double gridSize)
325{
326 if (gridSize <= 0.0) gridSize = 20.0;
327 wxPoint2DDouble newPos(std::round(m_position.m_x / gridSize) * gridSize,
328 std::round(m_position.m_y / gridSize) * gridSize);
329 m_width = std::round(m_width / gridSize) * gridSize;
330 if (m_width < gridSize) m_width = gridSize;
331 SetPosition(newPos);
332}
333
334bool Bus::GetContextMenu(wxMenu& menu)
335{
336 menu.Append(ContextMenuID::ID_EDIT_ELEMENT, _("Edit bus"));
337
338 wxMenu* textMenu = new wxMenu();
339
340 textMenu->Append(ID_TXT_NAME, _("Name"));
341 textMenu->Append(ID_TXT_VOLTAGE, _("Voltage"));
342 textMenu->Append(ID_TXT_ANGLE, _("Angle"));
343 textMenu->Append(ID_TXT_FAULTCURRENT, _("Fault current"));
344 textMenu->Append(ID_TXT_FAULTVOLTAGE, _("Fault voltage"));
345 textMenu->Append(ID_TXT_SCC, _("Short-circuit power"));
346 textMenu->Append(ID_TXT_THD, _("Voltage THD"));
347 textMenu->SetClientData(menu.GetClientData());
348
349
350 menu.AppendSubMenu(textMenu, _("Add text"));
351 GeneralMenuItens(menu);
352 return true;
353}
354
355bool Bus::ShowForm(wxWindow* parent, Element* element, wxWindow* workspace)
356{
357 BusForm busForm(parent, this);
358 busForm.CentreOnParent();
359 if (busForm.ShowModal() == wxID_OK) {
360 return true;
361 }
362 return false;
363}
364
366{
367 Bus* copy = new Bus();
368 *copy = *this;
369 return copy;
370}
371
372wxColour Bus::GetVoltageColour(GUIColour* guiColour) const
373{
374 double v_kV = m_electricalData.nominalVoltage;
375 if (m_electricalData.nominalVoltageUnit == ElectricalUnit::UNIT_V) {
376 v_kV /= 1000.0;
377 }
378
379 const std::vector<VoltageLevelColour>* levels = nullptr;
380 if (guiColour && !guiColour->voltageLevels.empty()) {
381 levels = &guiColour->voltageLevels;
382 }
383
384 if (levels && !levels->empty()) {
385 // 1. Find closest configured level within 15% tolerance
386 double minDiff = 1e9;
387 int bestIdx = -1;
388 for (size_t i = 0; i < levels->size(); ++i) {
389 double diff = std::abs(v_kV - (*levels)[i].voltage);
390 if (diff < minDiff) {
391 minDiff = diff;
392 bestIdx = static_cast<int>(i);
393 }
394 }
395
396 if (bestIdx >= 0) {
397 double tol = std::max((*levels)[bestIdx].voltage * 0.15, 0.5);
398 if (minDiff <= tol) {
399 return (*levels)[bestIdx].colour;
400 }
401 // 2. Range match (assuming ordered descending)
402 for (size_t i = 0; i < levels->size(); ++i) {
403 if (v_kV >= (*levels)[i].voltage * 0.9) {
404 return (*levels)[i].colour;
405 }
406 }
407 return levels->back().colour;
408 }
409 }
410
411 if (v_kV >= 500.0) {
412 return wxColour(0, 90, 220); // >= 500 kV (ex: 500 kV, 765 kV) - Royal Blue
413 } else if (v_kV >= 300.0) {
414 return wxColour(160, 82, 45); // 300 kV - 500 kV (ex: 345 kV, 440 kV) - Brown
415 } else if (v_kV >= 200.0) {
416 return wxColour(220, 35, 35); // 200 kV - 300 kV (ex: 230 kV) - Red
417 } else if (v_kV >= 100.0) {
418 return wxColour(0, 155, 65); // 100 kV - 200 kV (ex: 138 kV, 161 kV) - Green
419 } else if (v_kV >= 50.0) {
420 return wxColour(145, 40, 205); // 50 kV - 100 kV (ex: 69 kV, 88 kV) - Violet / Purple
421 } else if (v_kV >= 20.0) {
422 return wxColour(215, 35, 135); // 20 kV - 50 kV (ex: 34.5 kV) - Magenta / Pink
423 } else if (v_kV >= 10.0) {
424 return wxColour(0, 165, 195); // 10 kV - 20 kV (ex: 13.8 kV, 18 kV) - Cyan / Teal
425 } else if (v_kV >= 1.0) {
426 return wxColour(205, 150, 15); // 1 kV - 10 kV (ex: 2.4 kV, 4.16 kV, 6.9 kV) - Gold / Amber
427 } else {
428 return wxColour(120, 125, 135); // < 1 kV (220 V, 380 V, 440 V) - Slate Gray
429 }
430}
431
432wxString Bus::GetTipText() const
433{
434 wxString tipText = m_electricalData.name;
435 tipText += wxString::Format(" (%d)", m_electricalData.number + 1);
436 tipText += "\n";
437 tipText += StringFromDouble(m_electricalData.nominalVoltage, 1) +
438 (m_electricalData.nominalVoltageUnit == ElectricalUnit::UNIT_V
439 ? _(" V")
440 : _(" kV"));
441 tipText += "\n";
442 tipText += _("\nV = ") + wxString::FromDouble(std::abs(m_electricalData.voltage), 5) + _(" p.u.");
443 tipText += "\n";
444 tipText += wxString(L'\u03B8') + " = " + wxString::FromDouble(wxRadToDeg(std::arg(m_electricalData.voltage)), 5)
445 + wxString(L'\u00B0');
446
447 tipText += _("\n\nFault info:");
448 tipText += _("\nVa = ") + wxString::FromDouble(std::abs(m_electricalData.faultVoltage[0]), 5) + _(" p.u.");
449 tipText += _("\nVb = ") + wxString::FromDouble(std::abs(m_electricalData.faultVoltage[1]), 5) + _(" p.u.");
450 tipText += _("\nVc = ") + wxString::FromDouble(std::abs(m_electricalData.faultVoltage[2]), 5) + _(" p.u.");
451 if (m_electricalData.hasFault) {
452 tipText += _("\nIa = ") + wxString::FromDouble(std::abs(m_electricalData.faultCurrent[0]), 5) + _(" p.u.");
453 tipText += _("\nIb = ") + wxString::FromDouble(std::abs(m_electricalData.faultCurrent[1]), 5) + _(" p.u.");
454 tipText += _("\nIc = ") + wxString::FromDouble(std::abs(m_electricalData.faultCurrent[2]), 5) + _(" p.u.");
455 }
456
457 tipText += _("\n\nSsc = ") + wxString::FromDouble(std::abs(m_electricalData.scPower), 5) + _(" p.u.");
458
459 wxString harmonicsInfo = _("\n\nHarmonics info:");
460 bool hasHarmonics = false;
461 harmonicsInfo += _("\nTHD = ") + wxString::FromDouble(std::abs(m_electricalData.thd), 5) + wxT("%");
462 int i = 0;
463 for (auto& hVoltage : m_electricalData.harmonicVoltage) {
464 hasHarmonics = true;
465 wxString hVoltageStr;
466 hVoltageStr.Printf(_("\nVh(%d) = %.5e%s%.2f%s p.u."), m_electricalData.harmonicOrder[i], std::abs(hVoltage), wxString(L'\u2220'), wxRadToDeg(std::arg(hVoltage)), wxString(L'\u00B0'));
467 harmonicsInfo += hVoltageStr;
468 i++;
469 }
470 if (hasHarmonics) tipText += harmonicsInfo;
471
472 return tipText;
473}
474
476{
477 if (study == PlotStudy::STABILITY) {
478 if (!m_electricalData.plotBus) return false;
479 plotData.SetName(m_electricalData.name);
480 plotData.SetCurveType(ElementPlotData::CurveType::CT_BUS);
481
482 std::vector<double> absVoltage, argVoltage;
483 for (unsigned int i = 0; i < m_electricalData.stabVoltageVector.size(); ++i) {
484 absVoltage.push_back(std::abs(m_electricalData.stabVoltageVector[i]));
485 argVoltage.push_back(wxRadToDeg(std::arg(m_electricalData.stabVoltageVector[i])));
486 }
487 plotData.AddData(absVoltage, _("Voltage"));
488 plotData.AddData(argVoltage, _("Angle"));
489 plotData.AddData(m_electricalData.stabFreqVector, _("Frequency"));
490 return true;
491 }
492 else if (study == PlotStudy::FREQRESPONSE) {
493 if (!m_electricalData.plotPQData) return false;
494 plotData.SetName(m_electricalData.name);
495 plotData.SetCurveType(ElementPlotData::CurveType::CT_BUS);
496 plotData.AddData(m_electricalData.absImpedanceVector, _("Impedance"));
497 const auto& z = m_electricalData.complexImpedanceVector;
498 std::vector<double> r(z.size());
499 std::vector<double> x(z.size());
500 for (std::size_t i = 0; i < z.size(); ++i) {
501 r[i] = z[i].real();
502 x[i] = z[i].imag();
503 }
504 plotData.AddData(r, _("Resistance"));
505 plotData.AddData(x, _("Reactance"));
506 return true;
507 }
508 return false;
509}
510
511rapidxml::xml_node<>* Bus::SaveElement(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementListNode)
512{
513 m_electricalData.number = m_elementID;
514
515 auto elementNode = XMLParser::AppendNode(doc, elementListNode, "Bus");
516 XMLParser::SetNodeAttribute(doc, elementNode, "ID", m_elementID);
517
518 Element::SaveCADProperties(doc, elementNode);
519
520 auto electricalProp = XMLParser::AppendNode(doc, elementNode, "ElectricalProperties");
521 auto name = XMLParser::AppendNode(doc, electricalProp, "Name");
522 XMLParser::SetNodeValue(doc, name, m_electricalData.name);
523 auto nominalVoltage = XMLParser::AppendNode(doc, electricalProp, "NominalVoltage");
524 XMLParser::SetNodeValue(doc, nominalVoltage, m_electricalData.nominalVoltage);
525 XMLParser::SetNodeAttribute(doc, nominalVoltage, "UnitID", static_cast<int>(m_electricalData.nominalVoltageUnit));
526 auto isVoltageControlled = XMLParser::AppendNode(doc, electricalProp, "IsVoltageControlled");
527 XMLParser::SetNodeValue(doc, isVoltageControlled, m_electricalData.isVoltageControlled);
528 auto controlledVoltage = XMLParser::AppendNode(doc, electricalProp, "ControlledVoltage");
529 XMLParser::SetNodeValue(doc, controlledVoltage, m_electricalData.controlledVoltage);
530 XMLParser::SetNodeAttribute(doc, controlledVoltage, "Choice", m_electricalData.controlledVoltageUnitChoice);
531 auto slackBus = XMLParser::AppendNode(doc, electricalProp, "SlackBus");
532 XMLParser::SetNodeValue(doc, slackBus, m_electricalData.slackBus);
533
534 auto fault = XMLParser::AppendNode(doc, electricalProp, "Fault");
535 auto hasFault = XMLParser::AppendNode(doc, fault, "HasFault");
536 XMLParser::SetNodeValue(doc, hasFault, m_electricalData.hasFault);
537 auto faultType = XMLParser::AppendNode(doc, fault, "Type");
538 XMLParser::SetNodeValue(doc, faultType, static_cast<int>(m_electricalData.faultType));
539 auto faultLocation = XMLParser::AppendNode(doc, fault, "Location");
540 XMLParser::SetNodeValue(doc, faultLocation, static_cast<int>(m_electricalData.faultLocation));
541 auto faultResistance = XMLParser::AppendNode(doc, fault, "Resistance");
542 XMLParser::SetNodeValue(doc, faultResistance, m_electricalData.faultResistance);
543 auto faultReactance = XMLParser::AppendNode(doc, fault, "Reactance");
544 XMLParser::SetNodeValue(doc, faultReactance, m_electricalData.faultReactance);
545
546 auto stability = XMLParser::AppendNode(doc, electricalProp, "Stability");
547 auto plotBus = XMLParser::AppendNode(doc, stability, "Plot");
548 XMLParser::SetNodeValue(doc, plotBus, m_electricalData.plotBus);
549 auto stabHasFault = XMLParser::AppendNode(doc, stability, "HasFault");
550 XMLParser::SetNodeValue(doc, stabHasFault, m_electricalData.stabHasFault);
551 auto stabFaultTime = XMLParser::AppendNode(doc, stability, "FaultTime");
552 XMLParser::SetNodeValue(doc, stabFaultTime, m_electricalData.stabFaultTime);
553 auto stabFaultLength = XMLParser::AppendNode(doc, stability, "FaultLength");
554 XMLParser::SetNodeValue(doc, stabFaultLength, m_electricalData.stabFaultLength);
555 auto stabFaultResistance = XMLParser::AppendNode(doc, stability, "FaultResistance");
556 XMLParser::SetNodeValue(doc, stabFaultResistance, m_electricalData.stabFaultResistance);
557 auto stabFaultReactance = XMLParser::AppendNode(doc, stability, "FaultReactance");
558 XMLParser::SetNodeValue(doc, stabFaultReactance, m_electricalData.stabFaultReactance);
559
560 auto powerQuality = XMLParser::AppendNode(doc, electricalProp, "PowerQuality");
561 auto plotPQData = XMLParser::AppendNode(doc, powerQuality, "Plot");
562 XMLParser::SetNodeValue(doc, plotPQData, m_electricalData.plotPQData);
563
564 return elementNode;
565}
566
567bool Bus::OpenElement(rapidxml::xml_node<>* elementNode)
568{
569 if (!Element::OpenCADProperties(elementNode)) return false;
570
571 auto electricalProp = elementNode->first_node("ElectricalProperties");
572 if (!electricalProp) return false;
573
574 m_electricalData.name = electricalProp->first_node("Name")->value();
575 m_electricalData.nominalVoltage = XMLParser::GetNodeValueDouble(electricalProp, "NominalVoltage");
576 m_electricalData.nominalVoltageUnit =
577 (ElectricalUnit)XMLParser::GetAttributeValueInt(electricalProp, "NominalVoltage", "UnitID");
578 m_electricalData.isVoltageControlled = XMLParser::GetNodeValueInt(electricalProp, "IsVoltageControlled");
579 m_electricalData.controlledVoltage = XMLParser::GetNodeValueDouble(electricalProp, "ControlledVoltage");
580 m_electricalData.controlledVoltageUnitChoice =
581 XMLParser::GetAttributeValueInt(electricalProp, "ControlledVoltage", "Choice");
582 m_electricalData.slackBus = XMLParser::GetNodeValueInt(electricalProp, "SlackBus");
583 auto fault = electricalProp->first_node("Fault");
584 m_electricalData.hasFault = XMLParser::GetNodeValueInt(fault, "HasFault");
585 m_electricalData.faultType = (FaultData)XMLParser::GetNodeValueInt(fault, "Type");
586 m_electricalData.faultLocation = (FaultData)XMLParser::GetNodeValueInt(fault, "Location");
587 m_electricalData.faultResistance = XMLParser::GetNodeValueDouble(fault, "Resistance");
588 m_electricalData.faultReactance = XMLParser::GetNodeValueDouble(fault, "Reactance");
589 auto stability = electricalProp->first_node("Stability");
590 m_electricalData.plotBus = XMLParser::GetNodeValueInt(stability, "Plot");
591 m_electricalData.stabHasFault = XMLParser::GetNodeValueInt(stability, "HasFault");
592 m_electricalData.stabFaultTime = XMLParser::GetNodeValueDouble(stability, "FaultTime");
593 m_electricalData.stabFaultLength = XMLParser::GetNodeValueDouble(stability, "FaultLength");
594 m_electricalData.stabFaultResistance = XMLParser::GetNodeValueDouble(stability, "FaultResistance");
595 m_electricalData.stabFaultReactance = XMLParser::GetNodeValueDouble(stability, "FaultReactance");
596
597 auto powerQuality = electricalProp->first_node("PowerQuality");
598 if (powerQuality) m_electricalData.plotPQData = XMLParser::GetNodeValueInt(powerQuality, "Plot");
599
600 if (m_electricalData.stabHasFault) SetDynamicEvent(true);
601 SetInserted();
602 return true;
603}
@ ID_EDIT_ELEMENT
Definition Element.h:77
@ ID_PB_NONE
Definition Element.h:63
@ ID_PB_LEFT
Definition Element.h:65
@ ID_PB_RIGHT
Definition Element.h:64
FaultData
Information about fault (type and location).
PlotStudy
Form to edit the bus power data.
Definition BusForm.h:32
Node for power elements. All others power elements are connected through this.
Definition Bus.h:87
virtual bool PickboxContains(wxPoint2DDouble position)
Check if a pickbox contains a point. If contains the attributes related to pickbox movement will be c...
Definition Bus.cpp:247
virtual wxString GetTipText() const
Get the tip text.
Definition Bus.cpp:432
virtual bool GetPlotData(ElementPlotData &plotData, PlotStudy study=PlotStudy::STABILITY)
Fill the plot data.
Definition Bus.cpp:475
virtual void DrawDC(GUIColour *guiColour, wxPoint2DDouble translation, double scale, wxGraphicsContext *gc) const
Draw the element using GDI+.
Definition Bus.cpp:44
virtual Element * GetCopy()
Get a the element copy.
Definition Bus.cpp:365
virtual bool Intersects(wxRect2DDouble rect) const
Check if the element's rect intersects other rect.
Definition Bus.cpp:240
Bus()
Default constructor.
Definition Bus.cpp:23
virtual wxCursor GetBestPickboxCursor() const
Get the best cursor to shown to the user when the mouse is above a pickbox.
Definition Bus.cpp:271
virtual void Rotate(bool clockwise=true)
Rotate the element.
Definition Bus.cpp:315
virtual bool Contains(wxPoint2DDouble position) const
Checks if the element contains a position.
Definition Bus.cpp:234
virtual void MovePickbox(wxPoint2DDouble position)
Move the pickbox.
Definition Bus.cpp:288
virtual bool ShowForm(wxWindow *parent, Element *element, wxWindow *workspace=nullptr)
Show element data form.
Definition Bus.cpp:355
virtual bool GetContextMenu(wxMenu &menu)
Get the element contex menu.
Definition Bus.cpp:334
Base class of all elements of the program. This class is responsible for manage graphical and his dat...
Definition Element.h:115
virtual bool RotatedRectanglesIntersects(wxRect2DDouble rect1, wxRect2DDouble rect2, double angle1, double angle2) const
Check if two roteted rectangles intersect.
Definition Element.cpp:377
virtual void GeneralMenuItens(wxMenu &menu)
Insert general itens to context menu.
Definition Element.cpp:475
virtual void DrawDCRectangle(wxPoint2DDouble position, double width, double height, double angle, wxDC &dc) const
Draw a circle.
Definition Element.cpp:41
void SetPosition(const wxPoint2DDouble position)
Set the element position and update the rectangle.
Definition Element.cpp:33
virtual wxPoint2DDouble RotateAtPosition(wxPoint2DDouble pointToRotate, double angle, bool degrees=true) const
Rotate a point as element position being the origin.
Definition Element.cpp:298
virtual void DrawDCPickbox(wxPoint2DDouble position, wxGraphicsContext *gc) const
Draw a point.
Definition Element.cpp:291
virtual wxPoint2DDouble WorldToScreen(wxPoint2DDouble translation, double scale, double offsetX=0.0, double offsetY=0.0) const
Convert the element position to screen position.
Definition Element.cpp:354
static wxString StringFromDouble(double value, int minDecimal=1, int maxDecimals=13)
Convert a double value to string.
Definition Element.cpp:548
void SetInserted(bool inserted=true)
Set if the element is properly inserted in the workspace.
Definition Element.h:639
Abstract class of power elements.
virtual void SetDynamicEvent(bool dynEvent=true)
Set if the power element have dynamic event.