20#include "utils/DegreesAndRadians.h"
26 m_elementType = TYPE_BUS;
32Bus::Bus(wxPoint2DDouble position, wxString name)
34 m_elementType = TYPE_BUS;
39 m_electricalData.name = name;
44void Bus::DrawDC(wxPoint2DDouble translation,
double scale, wxGraphicsContext* gc)
const
46 wxPoint2DDouble gcPosition = m_position - wxPoint2DDouble(m_width / 2.0, m_height / 2.0);
47 wxGraphicsMatrix identityMatrix = gc->GetTransform();
50 gc->SetPen(*wxTRANSPARENT_PEN);
57 gc->SetTransform(identityMatrix);
60 gc->Translate(screenPt.m_x, screenPt.m_y);
61 gc->Rotate(wxDegToRad(m_angle));
62 gc->Translate(-screenPt.m_x, -screenPt.m_y);
64 gc->SetBrush(wxBrush(m_selectionColour));
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);
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);
84 if (!m_electricalData.isConnected)
85 gc->SetBrush(wxBrush(m_offlineElementColour));
87 gc->SetBrush(wxBrush(m_dynamicEventColour));
89 gc->SetBrush(wxBrush(m_busColour));
91 gc->DrawRectangle(gcPosition.m_x, gcPosition.m_y, m_width, m_height);
103 gc->SetTransform(identityMatrix);
105 wxPoint2DDouble screenPt =
WorldToScreen(translation, scale);
106 gc->Translate(screenPt.m_x, screenPt.m_y);
107 gc->Rotate(wxDegToRad(m_angle));
108 gc->Translate(-screenPt.m_x, -screenPt.m_y);
110 wxPoint2DDouble pbPosition[2] = {
WorldToScreen(translation, scale, m_width / 2.0) - wxPoint2DDouble(4, 4),
111 WorldToScreen(translation, scale, -m_width / 2.0) - wxPoint2DDouble(4, 4) };
119 if (m_electricalData.hasFault) {
121 gc->SetTransform(identityMatrix);
123 wxPoint2DDouble screenPt =
WorldToScreen(translation, scale);
124 gc->Translate(screenPt.m_x, screenPt.m_y);
125 gc->Rotate(wxDegToRad(m_angle));
126 gc->Translate(-screenPt.m_x, -screenPt.m_y);
128 wxPoint2DDouble fsPosition =
WorldToScreen(translation, scale, m_width / 2.0);
130 wxColour faultSymbolColour(255, 0, 0, 255);
135 gc->SetBrush(*wxRED_BRUSH);
136 gc->SetPen(*wxTRANSPARENT_PEN);
139 wxPoint2DDouble* points =
new wxPoint2DDouble[4];
140 points[0] = wxPoint2DDouble(fsPosition.m_x + 1 * scale, fsPosition.m_y + 3 * scale);
141 points[1] = wxPoint2DDouble(fsPosition.m_x + 1 * scale, fsPosition.m_y - 3 * scale);
142 points[2] = wxPoint2DDouble(fsPosition.m_x + 11 * scale, fsPosition.m_y - 1 * scale);
143 points[3] = wxPoint2DDouble(fsPosition.m_x + 11 * scale, fsPosition.m_y + 5 * scale);
145 gc->DrawLines(4, points);
147 points[0] = wxPoint2DDouble(fsPosition.m_x + 7 * scale, fsPosition.m_y - 5 * scale);
148 points[1] = wxPoint2DDouble(fsPosition.m_x + 21 * scale, fsPosition.m_y + 3 * scale);
149 points[2] = wxPoint2DDouble(fsPosition.m_x + 7 * scale, fsPosition.m_y + 1 * scale);
151 gc->DrawLines(3, points);
159void Bus::DrawDC(wxPoint2DDouble translation,
double scale, wxDC& dc)
const
162 wxPoint2DDouble gcPosition = m_position;
164 dc.SetPen(*wxTRANSPARENT_PEN);
168 dc.SetBrush(wxBrush(m_selectionColour));
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) };
179 DrawDCRectangle(gcPosition, m_width + 2 * m_borderSize, m_height + 2 * m_borderSize, m_angle, dc);
183 if (!m_electricalData.isConnected)
184 dc.SetBrush(wxBrush(m_offlineElementColour));
186 dc.SetBrush(wxBrush(m_dynamicEventColour));
188 dc.SetBrush(wxBrush(m_busColour));
194 if (m_electricalData.hasFault) {
196 wxPoint2DDouble center = m_position;
197 wxPoint2DDouble fsPosition = center + wxPoint2DDouble(m_width / 2.0, 0.0);
199 wxColour faultSymbolColour(255, 0, 0, 255);
201 double localScale = 1.5;
203 dc.SetBrush(*wxRED_BRUSH);
204 dc.SetPen(*wxTRANSPARENT_PEN);
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);
221 dc.DrawPolygon(4, points);
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);
230 dc.DrawPolygon(3, points);
237 return m_rect.Contains(ptR);
242 if (m_angle == 0.0 || m_angle == 180.0)
return m_rect.Intersects(rect);
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);
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);
259 if (rectRight.Contains(ptR)) {
263 if (rectLeft.Contains(ptR)) {
273 double angle = m_angle;
274 while (angle >= 157.5) angle -= 180.0;
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);
285 return wxCursor(wxCURSOR_ARROW);
296 dx = ptR.m_x - m_position.m_x - m_width / 2.0;
298 dx = m_position.m_x - m_width / 2.0 - ptR.m_x;
300 if (m_width + dx < 20.0)
return;
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));
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));
317 double rotAngle = m_rotationAngle;
318 if (!clockwise) rotAngle = -m_rotationAngle;
321 if (m_angle >= 360 || m_angle <= -360) m_angle = 0.0;
328 wxMenu* textMenu =
new wxMenu();
330 textMenu->Append(ID_TXT_NAME, _(
"Name"));
331 textMenu->Append(ID_TXT_VOLTAGE, _(
"Voltage"));
332 textMenu->Append(ID_TXT_ANGLE, _(
"Angle"));
333 textMenu->Append(ID_TXT_FAULTCURRENT, _(
"Fault current"));
334 textMenu->Append(ID_TXT_FAULTVOLTAGE, _(
"Fault voltage"));
335 textMenu->Append(ID_TXT_SCC, _(
"Short-circuit power"));
336 textMenu->Append(ID_TXT_THD, _(
"Voltage THD"));
337 textMenu->SetClientData(menu.GetClientData());
340 menu.AppendSubMenu(textMenu, _(
"Add text"));
348 busForm.CentreOnParent();
349 if (busForm.ShowModal() == wxID_OK) {
363 wxString tipText = m_electricalData.name;
364 tipText += wxString::Format(
" (%d)", m_electricalData.number + 1);
371 tipText += _(
"\nV = ") + wxString::FromDouble(std::abs(m_electricalData.voltage), 5) + _(
" p.u.");
373 tipText += wxString(L
'\u03B8') +
" = " + wxString::FromDouble(wxRadToDeg(std::arg(m_electricalData.voltage)), 5)
374 + wxString(L
'\u00B0');
376 tipText += _(
"\n\nFault info:");
377 tipText += _(
"\nVa = ") + wxString::FromDouble(std::abs(m_electricalData.faultVoltage[0]), 5) + _(
" p.u.");
378 tipText += _(
"\nVb = ") + wxString::FromDouble(std::abs(m_electricalData.faultVoltage[1]), 5) + _(
" p.u.");
379 tipText += _(
"\nVc = ") + wxString::FromDouble(std::abs(m_electricalData.faultVoltage[2]), 5) + _(
" p.u.");
380 if (m_electricalData.hasFault) {
381 tipText += _(
"\nIa = ") + wxString::FromDouble(std::abs(m_electricalData.faultCurrent[0]), 5) + _(
" p.u.");
382 tipText += _(
"\nIb = ") + wxString::FromDouble(std::abs(m_electricalData.faultCurrent[1]), 5) + _(
" p.u.");
383 tipText += _(
"\nIc = ") + wxString::FromDouble(std::abs(m_electricalData.faultCurrent[2]), 5) + _(
" p.u.");
386 tipText += _(
"\n\nSsc = ") + wxString::FromDouble(std::abs(m_electricalData.scPower), 5) + _(
" p.u.");
388 wxString harmonicsInfo = _(
"\n\nHarmonics info:");
389 bool hasHarmonics =
false;
390 harmonicsInfo += _(
"\nTHD = ") + wxString::FromDouble(std::abs(m_electricalData.thd), 5) + wxT(
"%");
392 for (
auto& hVoltage : m_electricalData.harmonicVoltage) {
394 wxString hVoltageStr;
395 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'));
396 harmonicsInfo += hVoltageStr;
399 if (hasHarmonics) tipText += harmonicsInfo;
407 if (!m_electricalData.plotBus)
return false;
408 plotData.SetName(m_electricalData.name);
409 plotData.SetCurveType(ElementPlotData::CurveType::CT_BUS);
411 std::vector<double> absVoltage, argVoltage;
412 for (
unsigned int i = 0; i < m_electricalData.stabVoltageVector.size(); ++i) {
413 absVoltage.push_back(std::abs(m_electricalData.stabVoltageVector[i]));
414 argVoltage.push_back(wxRadToDeg(std::arg(m_electricalData.stabVoltageVector[i])));
416 plotData.AddData(absVoltage, _(
"Voltage"));
417 plotData.AddData(argVoltage, _(
"Angle"));
418 plotData.AddData(m_electricalData.stabFreqVector, _(
"Frequency"));
422 if (!m_electricalData.plotPQData)
return false;
423 plotData.SetName(m_electricalData.name);
424 plotData.SetCurveType(ElementPlotData::CurveType::CT_BUS);
425 plotData.AddData(m_electricalData.absImpedanceVector, _(
"Impedance"));
431rapidxml::xml_node<>* Bus::SaveElement(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementListNode)
433 m_electricalData.number = m_elementID;
435 auto elementNode = XMLParser::AppendNode(doc, elementListNode,
"Bus");
436 XMLParser::SetNodeAttribute(doc, elementNode,
"ID", m_elementID);
438 Element::SaveCADProperties(doc, elementNode);
440 auto electricalProp = XMLParser::AppendNode(doc, elementNode,
"ElectricalProperties");
441 auto name = XMLParser::AppendNode(doc, electricalProp,
"Name");
442 XMLParser::SetNodeValue(doc, name, m_electricalData.name);
443 auto nominalVoltage = XMLParser::AppendNode(doc, electricalProp,
"NominalVoltage");
444 XMLParser::SetNodeValue(doc, nominalVoltage, m_electricalData.nominalVoltage);
445 XMLParser::SetNodeAttribute(doc, nominalVoltage,
"UnitID",
static_cast<int>(m_electricalData.nominalVoltageUnit));
446 auto isVoltageControlled = XMLParser::AppendNode(doc, electricalProp,
"IsVoltageControlled");
447 XMLParser::SetNodeValue(doc, isVoltageControlled, m_electricalData.isVoltageControlled);
448 auto controlledVoltage = XMLParser::AppendNode(doc, electricalProp,
"ControlledVoltage");
449 XMLParser::SetNodeValue(doc, controlledVoltage, m_electricalData.controlledVoltage);
450 XMLParser::SetNodeAttribute(doc, controlledVoltage,
"Choice", m_electricalData.controlledVoltageUnitChoice);
451 auto slackBus = XMLParser::AppendNode(doc, electricalProp,
"SlackBus");
452 XMLParser::SetNodeValue(doc, slackBus, m_electricalData.slackBus);
454 auto fault = XMLParser::AppendNode(doc, electricalProp,
"Fault");
455 auto hasFault = XMLParser::AppendNode(doc, fault,
"HasFault");
456 XMLParser::SetNodeValue(doc, hasFault, m_electricalData.hasFault);
457 auto faultType = XMLParser::AppendNode(doc, fault,
"Type");
458 XMLParser::SetNodeValue(doc, faultType,
static_cast<int>(m_electricalData.faultType));
459 auto faultLocation = XMLParser::AppendNode(doc, fault,
"Location");
460 XMLParser::SetNodeValue(doc, faultLocation,
static_cast<int>(m_electricalData.faultLocation));
461 auto faultResistance = XMLParser::AppendNode(doc, fault,
"Resistance");
462 XMLParser::SetNodeValue(doc, faultResistance, m_electricalData.faultResistance);
463 auto faultReactance = XMLParser::AppendNode(doc, fault,
"Reactance");
464 XMLParser::SetNodeValue(doc, faultReactance, m_electricalData.faultReactance);
466 auto stability = XMLParser::AppendNode(doc, electricalProp,
"Stability");
467 auto plotBus = XMLParser::AppendNode(doc, stability,
"Plot");
468 XMLParser::SetNodeValue(doc, plotBus, m_electricalData.plotBus);
469 auto stabHasFault = XMLParser::AppendNode(doc, stability,
"HasFault");
470 XMLParser::SetNodeValue(doc, stabHasFault, m_electricalData.stabHasFault);
471 auto stabFaultTime = XMLParser::AppendNode(doc, stability,
"FaultTime");
472 XMLParser::SetNodeValue(doc, stabFaultTime, m_electricalData.stabFaultTime);
473 auto stabFaultLength = XMLParser::AppendNode(doc, stability,
"FaultLength");
474 XMLParser::SetNodeValue(doc, stabFaultLength, m_electricalData.stabFaultLength);
475 auto stabFaultResistance = XMLParser::AppendNode(doc, stability,
"FaultResistance");
476 XMLParser::SetNodeValue(doc, stabFaultResistance, m_electricalData.stabFaultResistance);
477 auto stabFaultReactance = XMLParser::AppendNode(doc, stability,
"FaultReactance");
478 XMLParser::SetNodeValue(doc, stabFaultReactance, m_electricalData.stabFaultReactance);
480 auto powerQuality = XMLParser::AppendNode(doc, electricalProp,
"PowerQuality");
481 auto plotPQData = XMLParser::AppendNode(doc, powerQuality,
"Plot");
482 XMLParser::SetNodeValue(doc, plotPQData, m_electricalData.plotPQData);
487bool Bus::OpenElement(rapidxml::xml_node<>* elementNode)
489 if (!Element::OpenCADProperties(elementNode))
return false;
491 auto electricalProp = elementNode->first_node(
"ElectricalProperties");
492 if (!electricalProp)
return false;
494 m_electricalData.name = electricalProp->first_node(
"Name")->value();
495 m_electricalData.nominalVoltage = XMLParser::GetNodeValueDouble(electricalProp,
"NominalVoltage");
496 m_electricalData.nominalVoltageUnit =
497 (
ElectricalUnit)XMLParser::GetAttributeValueInt(electricalProp,
"NominalVoltage",
"UnitID");
498 m_electricalData.isVoltageControlled = XMLParser::GetNodeValueInt(electricalProp,
"IsVoltageControlled");
499 m_electricalData.controlledVoltage = XMLParser::GetNodeValueDouble(electricalProp,
"ControlledVoltage");
500 m_electricalData.controlledVoltageUnitChoice =
501 XMLParser::GetAttributeValueInt(electricalProp,
"ControlledVoltage",
"Choice");
502 m_electricalData.slackBus = XMLParser::GetNodeValueInt(electricalProp,
"SlackBus");
503 auto fault = electricalProp->first_node(
"Fault");
504 m_electricalData.hasFault = XMLParser::GetNodeValueInt(fault,
"HasFault");
505 m_electricalData.faultType = (
FaultData)XMLParser::GetNodeValueInt(fault,
"Type");
506 m_electricalData.faultLocation = (
FaultData)XMLParser::GetNodeValueInt(fault,
"Location");
507 m_electricalData.faultResistance = XMLParser::GetNodeValueDouble(fault,
"Resistance");
508 m_electricalData.faultReactance = XMLParser::GetNodeValueDouble(fault,
"Reactance");
509 auto stability = electricalProp->first_node(
"Stability");
510 m_electricalData.plotBus = XMLParser::GetNodeValueInt(stability,
"Plot");
511 m_electricalData.stabHasFault = XMLParser::GetNodeValueInt(stability,
"HasFault");
512 m_electricalData.stabFaultTime = XMLParser::GetNodeValueDouble(stability,
"FaultTime");
513 m_electricalData.stabFaultLength = XMLParser::GetNodeValueDouble(stability,
"FaultLength");
514 m_electricalData.stabFaultResistance = XMLParser::GetNodeValueDouble(stability,
"FaultResistance");
515 m_electricalData.stabFaultReactance = XMLParser::GetNodeValueDouble(stability,
"FaultReactance");
517 auto powerQuality = electricalProp->first_node(
"PowerQuality");
518 if (powerQuality) m_electricalData.plotPQData = XMLParser::GetNodeValueInt(powerQuality,
"Plot");
ElectricalUnit
Electrical units.
FaultData
Information about fault (type and location).
Node for power elements. All others power elements are connected through this.
virtual bool PickboxContains(wxPoint2DDouble position)
Check if a pickbox contains a point. If contains the attributes related to pickbox movement will be c...
virtual wxString GetTipText() const
Get the tip text.
virtual bool GetPlotData(ElementPlotData &plotData, PlotStudy study=PlotStudy::STABILITY)
Fill the plot data.
virtual Element * GetCopy()
Get a the element copy.
virtual bool Intersects(wxRect2DDouble rect) const
Check if the element's rect intersects other rect.
virtual void DrawDC(wxPoint2DDouble translation, double scale, wxGraphicsContext *gc) const
Draw the element using GDI+.
virtual bool ShowForm(wxWindow *parent, Element *element)
Show element data form.
Bus()
Default constructor.
virtual wxCursor GetBestPickboxCursor() const
Get the best cursor to shown to the user when the mouse is above a pickbox.
virtual void Rotate(bool clockwise=true)
Rotate the element.
virtual bool Contains(wxPoint2DDouble position) const
Checks if the element contains a position.
virtual void MovePickbox(wxPoint2DDouble position)
Move the pickbox.
virtual bool GetContextMenu(wxMenu &menu)
Get the element contex menu.
Base class of all elements of the program. This class is responsible for manage graphical and his dat...
virtual bool RotatedRectanglesIntersects(wxRect2DDouble rect1, wxRect2DDouble rect2, double angle1, double angle2) const
Check if two roteted rectangles intersect.
virtual void GeneralMenuItens(wxMenu &menu)
Insert general itens to context menu.
virtual void DrawDCRectangle(wxPoint2DDouble position, double width, double height, double angle, wxDC &dc) const
Draw a circle.
void SetPosition(const wxPoint2DDouble position)
Set the element position and update the rectangle.
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.
static wxString StringFromDouble(double value, int minDecimal=1, int maxDecimals=13)
Convert a double value to string.
void SetInserted(bool inserted=true)
Set if the element is properly inserted in the workspace.
Abstract class of power elements.
virtual void SetDynamicEvent(bool dynEvent=true)
Set if the power element have dynamic event.