Power System Platform  2026w10a-beta
Loading...
Searching...
No Matches
PowerElement.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 "PowerElement.h"
19#ifdef USING_WX_3_0_X
20#include "DegreesAndRadians.h"
21#endif
22#include <wx/brush.h>
23
25{
26 m_busColour.Set(0, 78, 255, 255);
27 m_onlineElementColour.Set(51, 51, 51, 255);
28 m_offlineElementColour.Set(128, 128, 128, 255);
29 m_closedSwitchColour.Set(0, 102, 0, 255);
30 m_openedSwitchColour.Set(255, 26, 26, 255);
31 m_powerFlowArrowColour.Set(255, 130, 0, 255);
32 m_dynamicEventColour.Set(255, 130, 0, 255);
33}
34
35void PowerElement::SetNominalVoltage(std::vector<double> nominalVoltage, std::vector<ElectricalUnit> nominalVoltageUnit)
36{
37}
38
39wxPoint2DDouble PowerElement::GetSwitchPoint(Element* parent,
40 wxPoint2DDouble parentPoint,
41 wxPoint2DDouble secondPoint) const
42{
43 double swLineSize = 25.0;
44 wxPoint2DDouble swPoint = wxPoint2DDouble(parentPoint.m_x, parentPoint.m_y - swLineSize);
45
46 // Rotate the second point (to compare).
47 double angle = parent->GetAngle();
48
49 secondPoint =
50 wxPoint2DDouble(std::cos(wxDegToRad(-angle)) * (secondPoint.m_x - parentPoint.m_x) -
51 std::sin(wxDegToRad(-angle)) * (secondPoint.m_y - parentPoint.m_y) + parentPoint.m_x,
52 std::sin(wxDegToRad(-angle)) * (secondPoint.m_x - parentPoint.m_x) +
53 std::cos(wxDegToRad(-angle)) * (secondPoint.m_y - parentPoint.m_y) + parentPoint.m_y);
54
55 // Rotate
56 if(secondPoint.m_y > parentPoint.m_y) angle -= 180.0;
57 return wxPoint2DDouble(std::cos(wxDegToRad(angle)) * (swPoint.m_x - parentPoint.m_x) -
58 std::sin(wxDegToRad(angle)) * (swPoint.m_y - parentPoint.m_y) + parentPoint.m_x,
59 std::sin(wxDegToRad(angle)) * (swPoint.m_x - parentPoint.m_x) +
60 std::cos(wxDegToRad(angle)) * (swPoint.m_y - parentPoint.m_y) + parentPoint.m_y);
61}
62
63bool PowerElement::SwitchesContains(wxPoint2DDouble position) const
64{
65 for(int i = 0; i < (int)m_switchRect.size(); i++) {
66 if(m_parentList[i]) {
67 if(m_switchRect[i].Contains(position)) return true;
68 }
69 }
70 return false;
71}
72
74{
75 // General method, to one switch only.
76 wxPoint2DDouble swCenter = wxPoint2DDouble((m_pointList[0].m_x + m_pointList[1].m_x) / 2.0,
77 (m_pointList[0].m_y + m_pointList[1].m_y) / 2.0);
78 m_switchRect[0] = wxRect2DDouble(swCenter.m_x - m_switchSize / 2.0, swCenter.m_y - m_switchSize / 2.0, m_switchSize,
79 m_switchSize);
80}
81
82//void PowerElement::DrawSwitches() const
83//{
84// int i = 0;
85// for(auto it = m_parentList.begin(); it != m_parentList.end(); it++) {
86// Element* parent = *it;
87// if(parent) {
88// if(m_online) {
89// glColor4dv(m_closedSwitchColour);
90// } else {
91// glColor4dv(m_openedSwitchColour);
92// }
93//
94// glPushMatrix();
95// glTranslated(m_switchRect[i].GetPosition().m_x + m_switchSize / 2.0,
96// m_switchRect[i].GetPosition().m_y + m_switchSize / 2.0, 0.0);
97// glRotated(parent->GetAngle(), 0.0, 0.0, 1.0);
98// glTranslated(-m_switchRect[i].GetPosition().m_x - m_switchSize / 2.0,
99// -m_switchRect[i].GetPosition().m_y - m_switchSize / 2.0, 0.0);
100//
101// DrawRectangle(m_switchRect[i].GetPosition() + wxPoint2DDouble(m_switchSize / 2.0, m_switchSize / 2.0),
102// m_switchSize, m_switchSize);
103//
104// glPopMatrix();
105// }
106// i++;
107// }
108//}
109
110void PowerElement::DrawDCSwitches(wxGraphicsContext* gc) const
111{
112 gc->SetPen(*wxTRANSPARENT_PEN);
113
114 int i = 0;
115 for (auto parent : m_parentList) {
116 if (parent) {
117 if (m_online) {
118 gc->SetBrush(wxBrush(m_closedSwitchColour));
119 }
120 else {
121 gc->SetBrush(wxBrush(m_openedSwitchColour));
122 }
123
124 gc->PushState();
125 gc->Translate(m_switchRect[i].GetPosition().m_x + m_switchSize / 2.0,
126 m_switchRect[i].GetPosition().m_y + m_switchSize / 2.0);
127 gc->Rotate(wxDegToRad(parent->GetAngle()));
128 gc->Translate(-m_switchRect[i].GetPosition().m_x - m_switchSize / 2.0,
129 -m_switchRect[i].GetPosition().m_y - m_switchSize / 2.0);
130
131 wxPoint2DDouble switchPos = m_switchRect[i].GetPosition();
132 gc->DrawRectangle(switchPos.m_x, switchPos.m_y, m_switchSize, m_switchSize);
133
134 gc->PopState();
135 }
136 i++;
137 }
138}
139
140void PowerElement::DrawDCSwitches(wxDC& dc) const
141{
142 dc.SetPen(*wxTRANSPARENT_PEN);
143
144 int i = 0;
145 for (auto parent : m_parentList) {
146 if (parent) {
147 if (m_online) {
148 dc.SetBrush(wxBrush(m_closedSwitchColour));
149 }
150 else {
151 dc.SetBrush(wxBrush(m_openedSwitchColour));
152 }
153
154 //gc->PushState();
155 //gc->Translate(m_switchRect[i].GetPosition().m_x + m_switchSize / 2.0,
156 // m_switchRect[i].GetPosition().m_y + m_switchSize / 2.0);
157 //gc->Rotate(wxDegToRad(parent->GetAngle()));
158 //gc->Translate(-m_switchRect[i].GetPosition().m_x - m_switchSize / 2.0,
159 // -m_switchRect[i].GetPosition().m_y - m_switchSize / 2.0);
160 //
161 //wxPoint2DDouble switchPos = m_switchRect[i].GetPosition();
162 //gc->DrawRectangle(switchPos.m_x, switchPos.m_y, m_switchSize, m_switchSize);
163 //
164 //gc->PopState();
165
166 DrawDCRectangle(m_switchRect[i].GetPosition() + wxPoint2DDouble(m_switchSize / 2.0, m_switchSize / 2.0),
167 m_switchSize, m_switchSize, parent->GetAngle(), dc);
168 }
169 i++;
170 }
171}
172
173void PowerElement::CalculatePowerFlowPts(std::vector<wxPoint2DDouble> edges)
174{
175 double arrowRate = 100.0; // One arrow to each "arrowRate" distance in pixels.
176
177 if(edges.size() < 2) return;
178
179 // Clear all power flow points
180 for(int i = 0; i < (int)m_powerFlowArrow.size(); i++) m_powerFlowArrow[i].clear();
181 m_powerFlowArrow.clear();
182
183 for(int i = 1; i < (int)edges.size(); i++) {
184 wxPoint2DDouble pt1 = edges[i - 1];
185 wxPoint2DDouble pt2 = edges[i];
186
187 double angle = std::atan2(pt2.m_y - pt1.m_y, pt2.m_x - pt1.m_x);
188
189 wxPoint2DDouble rotPt2(
190 std::cos(-angle) * (pt2.m_x - pt1.m_x) - std::sin(-angle) * (pt2.m_y - pt1.m_y) + pt1.m_x,
191 std::sin(-angle) * (pt2.m_x - pt1.m_x) + std::cos(-angle) * (pt2.m_y - pt1.m_y) + pt1.m_y);
192
193 int numArrows = std::abs(pt1.m_x - rotPt2.m_x) / arrowRate;
194 if(numArrows == 0) numArrows = 1;
195
196 for(int i = 0; i < numArrows; i++) {
197 wxPoint2DDouble arrowCenter(pt1.m_x + ((rotPt2.m_x - pt1.m_x) / double(numArrows + 1)) * double(i + 1),
198 pt1.m_y + ((rotPt2.m_y - pt1.m_y) / double(numArrows + 1)) * double(i + 1));
199
200 std::vector<wxPoint2DDouble> triPts;
201 triPts.push_back(arrowCenter + wxPoint2DDouble(5.0, 0.0));
202 triPts.push_back(arrowCenter + wxPoint2DDouble(-5.0, 5.0));
203 triPts.push_back(arrowCenter + wxPoint2DDouble(-5.0, -5.0));
204
205 // Rotate back.
206 for(int i = 0; i < 3; i++) {
207 triPts[i] = wxPoint2DDouble(
208 std::cos(angle) * (triPts[i].m_x - pt1.m_x) - std::sin(angle) * (triPts[i].m_y - pt1.m_y) + pt1.m_x,
209 std::sin(angle) * (triPts[i].m_x - pt1.m_x) + std::cos(angle) * (triPts[i].m_y - pt1.m_y) +
210 pt1.m_y);
211 }
212 m_powerFlowArrow.push_back(triPts);
213 }
214 }
215}
216
217//void PowerElement::DrawPowerFlowPts() const
218//{
219// if(m_online) {
220// glColor4dv(m_powerFlowArrowColour.GetRGBA());
221// for(int i = 0; i < (int)m_powerFlowArrow.size(); i++) { DrawTriangle(m_powerFlowArrow[i]); }
222// }
223//}
224
225void PowerElement::DrawDCPowerFlowPts(wxGraphicsContext* gc) const
226{
227 gc->SetPen(*wxTRANSPARENT_PEN);
228 if (m_online) {
229 gc->SetBrush(wxBrush(m_powerFlowArrowColour));
230 for (auto arrow : m_powerFlowArrow) { DrawDCTriangle(arrow, gc); }
231 }
232}
233
234void PowerElement::DrawDCPowerFlowPts(wxDC& dc) const
235{
236 dc.SetPen(*wxTRANSPARENT_PEN);
237 if (m_online) {
238 dc.SetBrush(wxBrush(m_powerFlowArrowColour));
239 for (auto arrow : m_powerFlowArrow) {
240 std::vector<wxPoint> arrowPts;
241 for (auto& pt : arrow) {
242 arrowPts.emplace_back(static_cast<int>(pt.m_x), static_cast<int>(pt.m_y));
243 }
244 DrawDCTriangle(arrowPts, dc);
245 }
246 }
247}
248
249double PowerElement::GetValueFromUnit(double value, ElectricalUnit valueUnit)
250{
251 switch(valueUnit) {
257 return value * 1e3;
258 } break;
262 return value * 1e6;
263 }
264 default:
265 break;
266 }
267 return value;
268}
269
270bool PowerElement::OpenCADProperties(rapidxml::xml_node<>* elementNode, std::vector<Element*> parentList)
271{
272 auto cadPropNode = elementNode->first_node("CADProperties");
273 if(!cadPropNode) return false;
274
275 auto position = cadPropNode->first_node("Position");
276 double posX = XMLParser::GetNodeValueDouble(position, "X");
277 double posY = XMLParser::GetNodeValueDouble(position, "Y");
278 auto size = cadPropNode->first_node("Size");
279 m_width = XMLParser::GetNodeValueDouble(size, "Width");
280 m_height = XMLParser::GetNodeValueDouble(size, "Height");
281 double angle = XMLParser::GetNodeValueDouble(cadPropNode, "Angle");
282 SetPosition(wxPoint2DDouble(posX, posY));
283
284 auto nodePosition = cadPropNode->first_node("NodePosition");
285 double nodePosX = XMLParser::GetNodeValueDouble(nodePosition, "X");
286 double nodePosY = XMLParser::GetNodeValueDouble(nodePosition, "Y");
287
288 int parentID = XMLParser::GetNodeValueInt(cadPropNode, "ParentID");
289 // If the opened power element has no parent, set up the basics CAD properties of the element manually, otherwise
290 // just class method AddParent to calculate properly.
291 if(parentID == -1) {
292 m_parentList.push_back(nullptr);
293 m_pointList.push_back(wxPoint2DDouble(nodePosX, nodePosY));
294 m_pointList.push_back(wxPoint2DDouble(nodePosX, nodePosY));
295 m_pointList.push_back(m_position + wxPoint2DDouble(0.0, -m_height / 2.0 - 10.0));
296 m_pointList.push_back(m_position + wxPoint2DDouble(0.0, -m_height / 2.0));
297
298 wxRect2DDouble genRect(0, 0, 0, 0);
299 m_switchRect.push_back(genRect); // Push a general rectangle.
301
302 m_online = false; // Not connected elements are always offline.
303 } else {
304 AddParent(parentList[parentID], wxPoint2DDouble(nodePosX, nodePosY));
305 }
306
307 // Set up the points properly.
308 StartMove(m_position);
309 Move(wxPoint2DDouble(posX, posY));
310
311 // Set the rotation properly.
312 int numRot = angle / m_rotationAngle;
313 bool clockwise = true;
314 if(numRot < 0) {
315 numRot = std::abs(numRot);
316 clockwise = false;
317 }
318 for(int i = 0; i < numRot; i++) Rotate(clockwise);
319
320 return true;
321}
322
323void PowerElement::SaveCADProperties(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementNode)
324{
325 auto cadProp = XMLParser::AppendNode(doc, elementNode, "CADProperties");
326 auto position = XMLParser::AppendNode(doc, cadProp, "Position");
327 auto posX = XMLParser::AppendNode(doc, position, "X");
328 XMLParser::SetNodeValue(doc, posX, m_position.m_x);
329 auto posY = XMLParser::AppendNode(doc, position, "Y");
330 XMLParser::SetNodeValue(doc, posY, m_position.m_y);
331 auto size = XMLParser::AppendNode(doc, cadProp, "Size");
332 auto width = XMLParser::AppendNode(doc, size, "Width");
333 XMLParser::SetNodeValue(doc, width, m_width);
334 auto height = XMLParser::AppendNode(doc, size, "Height");
335 XMLParser::SetNodeValue(doc, height, m_height);
336 auto angle = XMLParser::AppendNode(doc, cadProp, "Angle");
337 XMLParser::SetNodeValue(doc, angle, m_angle);
338 auto nodePos = XMLParser::AppendNode(doc, cadProp, "NodePosition");
339 auto nodePosX = XMLParser::AppendNode(doc, nodePos, "X");
340 XMLParser::SetNodeValue(doc, nodePosX, m_pointList[0].m_x);
341 auto nodePosY = XMLParser::AppendNode(doc, nodePos, "Y");
342 XMLParser::SetNodeValue(doc, nodePosY, m_pointList[0].m_y);
343 auto parentID = XMLParser::AppendNode(doc, cadProp, "ParentID");
344 Element* parent = m_parentList[0];
345 if(parent) XMLParser::SetNodeValue(doc, parentID, parent->GetID());
346}
347
348void PowerElement::SaveSwitchingData(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* electricalNode)
349{
350 auto switchingList = XMLParser::AppendNode(doc, electricalNode, "SwitchingList");
351 for(int i = 0; i < static_cast<int>(m_swData.swType.size()); i++) {
352 auto switching = XMLParser::AppendNode(doc, switchingList, "Switching");
353 XMLParser::SetNodeAttribute(doc, switching, "ID", i);
354 auto swType = XMLParser::AppendNode(doc, switching, "Type");
355 XMLParser::SetNodeValue(doc, swType, static_cast<int>(m_swData.swType[i]));
356 auto swTime = XMLParser::AppendNode(doc, switching, "Time");
357 XMLParser::SetNodeValue(doc, swTime, m_swData.swTime[i]);
358 }
359}
360
361bool PowerElement::OpenSwitchingData(rapidxml::xml_node<>* electricalNode)
362{
363 auto switchingList = electricalNode->first_node("SwitchingList");
364 if(!switchingList) return false;
365 auto swNode = switchingList->first_node("Switching");
366 while(swNode) {
367 m_swData.swType.push_back((SwitchingType)XMLParser::GetNodeValueInt(swNode, "Type"));
368 m_swData.swTime.push_back(XMLParser::GetNodeValueDouble(swNode, "Time"));
369 swNode = swNode->next_sibling("Switching");
370 }
371 return true;
372}
ElectricalUnit
Electrical units.
SwitchingType
Type of switching.
Base class of all elements of the program. This class is responsible for manage graphical and his dat...
Definition Element.h:112
virtual int GetID() const
Get the element ID.
Definition Element.h:271
wxPoint2DDouble GetPosition() const
Get the element position.
Definition Element.h:186
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,...
Definition Element.h:239
double GetAngle() const
Get the element angle.
Definition Element.h:211
virtual void DrawDCRectangle(wxPoint2DDouble position, double width, double height, double angle, wxDC &dc) const
Draw a circle.
Definition Element.cpp:45
virtual void StartMove(wxPoint2DDouble position)
Update the element attributes related to the movement.
Definition Element.cpp:329
void SetPosition(const wxPoint2DDouble position)
Set the element position and update the rectangle.
Definition Element.cpp:27
virtual void DrawDCTriangle(std::vector< wxPoint2DDouble > points, wxGraphicsContext *gc) const
Draw rectangle.
Definition Element.cpp:237
virtual void Move(wxPoint2DDouble position)
Move the element other position.
Definition Element.cpp:335
virtual bool Contains(wxPoint2DDouble position) const =0
Checks if the element contains a position.
virtual void Rotate(bool clockwise=true)
Rotate the element.
Definition Element.h:316
virtual void SetNominalVoltage(std::vector< double > nominalVoltage, std::vector< ElectricalUnit > nominalVoltageUnit)
Set nominal voltage of the element.
PowerElement()
Constructor.
virtual void CalculatePowerFlowPts(std::vector< wxPoint2DDouble > edges)
Calculate the points of the power flow arrows.
virtual void DrawDCSwitches(wxGraphicsContext *gc) const
Draw switch.
virtual bool SwitchesContains(wxPoint2DDouble position) const
Check if switch contains position.
virtual void UpdateSwitches()
Update the switch position.
virtual void DrawDCPowerFlowPts(wxGraphicsContext *gc) const
Draw power flow arrows.
virtual wxPoint2DDouble GetSwitchPoint(Element *parent, wxPoint2DDouble parentPoint, wxPoint2DDouble secondPoint) const
Get the correct switch position.
std::vector< double > swTime
std::vector< SwitchingType > swType