Power System Platform  2026w10a-beta
Loading...
Searching...
No Matches
Text.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 "Text.h"
19
20#include "../forms/TextForm.h"
21
22#include "../simulation/ElectricCalculation.h"
23
24#include "powerElement/Bus.h"
28#include "powerElement/Line.h"
29#include "powerElement/Load.h"
33
34#ifdef USING_WX_3_0_X
35#include "../utils/DegreesAndRadians.h"
36#endif
37
38Text::Text() : GraphicalElement()
39{
40 m_elementType = TYPE_TEXT;
41 SetText(m_text);
42 m_inserted = true;
43}
44Text::Text(wxPoint2DDouble position, wxString fontName, int fontSize) : GraphicalElement()
45{
46 m_elementType = TYPE_TEXT;
47 m_position = position;
48 m_fontName = fontName;
49 m_fontSize = fontSize;
50 SetText(m_text);
51 m_inserted = true;
52}
53
54Text::~Text()
55{
56 //for (auto& openGLText : m_openGLTextList) {
57 // if (openGLText) delete openGLText;
58 //}
59 //m_openGLTextList.clear();
60}
61
62bool Text::Contains(wxPoint2DDouble position) const
63{
64 wxPoint2DDouble ptR = RotateAtPosition(position, -m_angle);
65 return m_rect.Contains(ptR);
66}
67
68//void Text::Draw(wxPoint2DDouble translation, double scale)
69//{
70// // Draw selection rectangle
71//
72// // Push the current matrix on stack.
73// glPushMatrix();
74// // Rotate the matrix around the object position.
75// glTranslated(m_position.m_x, m_position.m_y, 0.0);
76// glRotated(m_angle, 0.0, 0.0, 1.0);
77// glTranslated(-m_position.m_x, -m_position.m_y, 0.0);
78//
79// if (m_selected) {
80// if (m_useAltSelectionColour) glColor4d(0.0, 0.8, 0.0, 0.5);
81// else glColor4d(0.0, 0.5, 1.0, 0.5);
82// DrawRectangle(m_position + wxPoint2DDouble(m_borderSize / 2.0, m_borderSize / 2.0), m_rect.m_width,
83// m_rect.m_height);
84// }
85//
86// // Draw text (layer 2)
87// glColor4d(0.0, 0.0, 0.0, 1.0);
88// if (m_isMultlineText) {
89// for (unsigned int i = 0; i < m_openGLTextList.size(); ++i) {
90// m_openGLTextList[i]->Draw(
91// m_position +
92// wxPoint2DDouble(0.0, (m_height * static_cast<double>(i) / static_cast<double>(m_numberOfLines)) -
93// (m_height * static_cast<double>(m_numberOfLines - 1) /
94// static_cast<double>(2 * m_numberOfLines))));
95// }
96// }
97// else if (m_openGLTextList.size() > 0) {
98// m_openGLTextList[0]->Draw(m_position);
99// }
100// glPopMatrix();
101//}
102
103void Text::DrawDC(wxPoint2DDouble translation, double scale, wxGraphicsContext* gc)
104{
105 // Update text extent using correct context
106 if (m_updateTextRectangle) {
107 for (GCText* gcText : m_gcTextList) {
108 if (gcText) delete gcText;
109 }
110 m_gcTextList.clear();
111
112 m_numberOfLines = m_text.Freq('\n') + 1;
113 if (m_numberOfLines > 1) m_isMultlineText = true;
114 m_width = 0.0;
115 m_height = 0.0;
116 wxString multText = m_text;
117 for (int i = 0; i < m_numberOfLines; ++i) {
118 wxString nextLine;
119 wxString currentLine = multText.BeforeFirst('\n', &nextLine);
120 multText = nextLine;
121
122 GCText* gcText = new GCText(currentLine);
123 wxFont font = wxFont(m_fontSize,
124 wxFONTFAMILY_DEFAULT,
125 wxFONTSTYLE_NORMAL,
126 wxFONTWEIGHT_NORMAL,
127 false,
128 m_fontName);
129 gcText->SetFont(font);
130 m_gcTextList.push_back(gcText);
131
132 gc->SetFont(font, *wxBLACK);
133
134 double w, h;
135 gc->GetTextExtent(currentLine, &w, &h);
136
137 if (w > m_width) m_width = w;
138 m_height += h;
139 }
140 SetPosition(m_position); // Update element rectangle.
141 m_updateTextRectangle = false;
142 }
143
144 // Draw selection rectangle
145
146 // Push the current matrix on stack.
147 gc->PushState();
148 // Rotate the matrix around the object position.
149 gc->Translate(m_position.m_x, m_position.m_y);
150 gc->Rotate(wxDegToRad(m_angle));
151 gc->Translate(-m_position.m_x, -m_position.m_y);
152
153 if (m_selected) {
154 //glColor4d(0.0, 0.5, 1.0, 0.5);
155 gc->SetPen(*wxTRANSPARENT_PEN);
156 if (m_useAltSelectionColour) gc->SetBrush(wxBrush(wxColour(0, 230, 0, 125)));
157 else gc->SetBrush(wxBrush(wxColour(0, 125, 255, 125)));
158
159 wxPoint2DDouble pos = m_position - wxPoint2DDouble(m_borderSize / 2.0 + m_width / 2, m_borderSize / 2.0 + m_height / 2);
160 gc->DrawRectangle(pos.m_x, pos.m_y, m_rect.m_width, m_rect.m_height);
161 }
162
163 // Draw text (layer 2)
164 //gc->SetPen(wxPen(wxColour(0, 0, 0, 255)));
165 //gc->SetBrush(*wxTRANSPARENT_BRUSH);
166 wxPoint2DDouble pos = m_position - wxPoint2DDouble(m_width / 2, m_height / 2);
167 if (m_isMultlineText) {
168 for (unsigned int i = 0; i < m_gcTextList.size(); ++i) {
169 m_gcTextList[i]->Draw(
170 pos +
171 wxPoint2DDouble(0.0, (m_height * static_cast<double>(i) / static_cast<double>(m_numberOfLines))), gc);
172 }
173 }
174 else if (m_gcTextList.size() > 0) {
175 m_gcTextList[0]->Draw(pos, gc);
176 }
177 gc->PopState();
178}
179
180void Text::DrawDC(wxPoint2DDouble translation, double scale, wxDC& dc)
181{
182 // Update text extent using correct context
183 if (m_updateTextRectangle) {
184 for (GCText* gcText : m_gcTextList) {
185 if (gcText) delete gcText;
186 }
187 m_gcTextList.clear();
188
189 m_numberOfLines = m_text.Freq('\n') + 1;
190 if (m_numberOfLines > 1) m_isMultlineText = true;
191 m_width = 0.0;
192 m_height = 0.0;
193 wxString multText = m_text;
194 for (int i = 0; i < m_numberOfLines; ++i) {
195 wxString nextLine;
196 wxString currentLine = multText.BeforeFirst('\n', &nextLine);
197 multText = nextLine;
198
199 GCText* gcText = new GCText(currentLine);
200 wxFont font = wxFont(m_fontSize,
201 wxFONTFAMILY_UNKNOWN,
202 wxFONTSTYLE_NORMAL,
203 wxFONTWEIGHT_NORMAL,
204 false,
205 m_fontName);
206 gcText->SetFont(font);
207 m_gcTextList.push_back(gcText);
208
209 //double w, h;
210 wxSize txtSize = dc.GetTextExtent(currentLine);
211
212 if (txtSize.GetWidth() > m_width) m_width = txtSize.GetWidth();
213 m_height += txtSize.GetHeight();
214 }
215 SetPosition(m_position); // Update element rectangle.
216 m_updateTextRectangle = false;
217 }
218
219 // Draw selection rectangle
220 if (m_selected) {
221 dc.SetPen(*wxTRANSPARENT_PEN);
222 if (m_useAltSelectionColour) dc.SetBrush(wxBrush(wxColour(0, 230, 0, 125)));
223 else dc.SetBrush(wxBrush(wxColour(0, 125, 255, 125)));
224
225 DrawDCRectangle(m_position, m_rect.m_width, m_rect.m_height, m_angle, dc);
226 }
227
228 // Draw text (layer 2)
229 //wxPoint2DDouble pos = m_position - wxPoint2DDouble(m_width / 2, m_height / 2);
230 if (m_isMultlineText) {
231 for (unsigned int i = 0; i < m_gcTextList.size(); ++i) {
232 m_gcTextList[i]->Draw(
233 m_position +
234 wxPoint2DDouble(0.0, (m_height * static_cast<double>(i) / static_cast<double>(m_numberOfLines))), m_width, m_height, dc, m_angle);
235 }
236 }
237 else if (m_gcTextList.size() > 0) {
238 m_gcTextList[0]->Draw(m_position, m_width, m_height, dc, m_angle);
239 }
240}
241
242bool Text::Intersects(wxRect2DDouble rect) const
243{
244 if (m_angle == 0.0 || m_angle == 180.0) return m_rect.Intersects(rect);
245 return RotatedRectanglesIntersects(m_rect, rect, m_angle, 0.0);
246}
247
248void Text::SetText(wxString text)
249{
250 m_text = text;
251 m_updateTextRectangle = true;
252}
253
254void Text::Rotate(bool clockwise)
255{
256 if (!m_allowRotation) return;
257
258 double rotAngle = m_rotationAngle;
259 if (!clockwise) rotAngle = -m_rotationAngle;
260
261 m_angle += rotAngle;
262 if (m_angle >= 360 || m_angle <= -360) m_angle = 0.0;
263}
264
265bool Text::ShowForm(wxWindow* parent, std::vector<Element*> elementList)
266{
267 TextForm textForm(parent, this, elementList);
268 if (textForm.ShowModal() == wxID_OK) {
269 return true;
270 }
271 return false;
272}
273
274void Text::UpdateText(double systemPowerBase)
275{
276 switch (m_elementTypeText) {
277 case TYPE_NONE:
278 SetText(m_text);
279 break;
280 case TYPE_BUS: {
281 Bus* bus = static_cast<Bus*>(m_element);
282 if (bus) {
283 BusElectricalData data = bus->GetElectricalData();
284 double baseVoltage = data.nominalVoltage;
285 if (data.nominalVoltageUnit == ElectricalUnit::UNIT_kV) baseVoltage *= 1e3;
286 double baseCurrent = systemPowerBase / (std::sqrt(3.0) * baseVoltage);
287
288 switch (m_dataType) {
289 case DATA_NAME: {
290 SetText(bus->GetElectricalData().name);
291 } break;
292 case DATA_VOLTAGE: {
293 double voltage = std::abs(data.voltage);
294 switch (m_unit) {
296 SetText(wxString::FromDouble(voltage, m_decimalPlaces) + " p.u.");
297 } break;
299 SetText(wxString::FromDouble(voltage * baseVoltage, m_decimalPlaces) + " V");
300 } break;
302 SetText(wxString::FromDouble(voltage * baseVoltage / 1e3, m_decimalPlaces) + " kV");
303 } break;
304 default:
305 break;
306 }
307 } break;
308 case DATA_ANGLE: {
309 double angle = std::arg(data.voltage);
310 switch (m_unit) {
312 SetText(wxString::FromDouble(angle, m_decimalPlaces) + " rad");
313 } break;
315 SetText(wxString::FromDouble(wxRadToDeg(angle), m_decimalPlaces) + (wxString)L'\u00B0');
316 } break;
317 default:
318 break;
319 }
320 } break;
321 case DATA_SC_CURRENT: {
322 double faultCurrent[3] = { std::abs(data.faultCurrent[0]), std::abs(data.faultCurrent[1]),
323 std::abs(data.faultCurrent[2]) };
324 switch (m_unit) {
326 wxString str =
327 "Ia = " + wxString::FromDouble(faultCurrent[0], m_decimalPlaces) + " p.u.";
328 str += "\nIb = " + wxString::FromDouble(faultCurrent[1], m_decimalPlaces) + " p.u.";
329 str += "\nIc = " + wxString::FromDouble(faultCurrent[2], m_decimalPlaces) + " p.u.";
330 SetText(str);
331 } break;
333 wxString str =
334 "Ia = " + wxString::FromDouble(faultCurrent[0] * baseCurrent, m_decimalPlaces) +
335 " A";
336 str += "\nIb = " +
337 wxString::FromDouble(faultCurrent[1] * baseCurrent, m_decimalPlaces) + " A";
338 str += "\nIc = " +
339 wxString::FromDouble(faultCurrent[2] * baseCurrent, m_decimalPlaces) + " A";
340 SetText(str);
341 } break;
343 wxString str =
344 "Ia = " +
345 wxString::FromDouble(faultCurrent[0] * baseCurrent / 1e3, m_decimalPlaces) + " kA";
346 str += "\nIb = " +
347 wxString::FromDouble(faultCurrent[1] * baseCurrent / 1e3, m_decimalPlaces) +
348 " kA";
349 str += "\nIc = " +
350 wxString::FromDouble(faultCurrent[2] * baseCurrent / 1e3, m_decimalPlaces) +
351 " kA";
352 SetText(str);
353 } break;
354 default:
355 break;
356 }
357 } break;
358 case DATA_SC_VOLTAGE: {
359 double faultVoltage[3] = { std::abs(data.faultVoltage[0]), std::abs(data.faultVoltage[1]),
360 std::abs(data.faultVoltage[2]) };
361 switch (m_unit) {
363 wxString str =
364 "Va = " + wxString::FromDouble(faultVoltage[0], m_decimalPlaces) + " p.u.";
365 str += "\nVb = " + wxString::FromDouble(faultVoltage[1], m_decimalPlaces) + " p.u.";
366 str += "\nVc = " + wxString::FromDouble(faultVoltage[2], m_decimalPlaces) + " p.u.";
367 SetText(str);
368 } break;
370 wxString str =
371 "Va = " + wxString::FromDouble(faultVoltage[0] * baseVoltage, m_decimalPlaces) +
372 " V";
373 str += "\nVb = " +
374 wxString::FromDouble(faultVoltage[1] * baseVoltage, m_decimalPlaces) + " V";
375 str += "\nVc = " +
376 wxString::FromDouble(faultVoltage[2] * baseVoltage, m_decimalPlaces) + " V";
377 SetText(str);
378 } break;
380 wxString str =
381 "Va = " +
382 wxString::FromDouble(faultVoltage[0] * baseVoltage / 1e3, m_decimalPlaces) + " kV";
383 str += "\nVb = " +
384 wxString::FromDouble(faultVoltage[1] * baseVoltage / 1e3, m_decimalPlaces) +
385 " kV";
386 str += "\nVc = " +
387 wxString::FromDouble(faultVoltage[2] * baseVoltage / 1e3, m_decimalPlaces) +
388 " kV";
389 SetText(str);
390 } break;
391 default:
392 break;
393 }
394 } break;
395 case DATA_SC_POWER: {
396 double scPower = data.scPower;
397 if (!data.isConnected) scPower = 0.0;
398 switch (m_unit) {
400 SetText(wxString::FromDouble(scPower, m_decimalPlaces) + " p.u.");
401 } break;
403 SetText(wxString::FromDouble(scPower * systemPowerBase, m_decimalPlaces) + " VA");
404 } break;
406 SetText(wxString::FromDouble(scPower * systemPowerBase / 1e3, m_decimalPlaces) +
407 " kVA");
408 } break;
410 SetText(wxString::FromDouble(scPower * systemPowerBase / 1e6, m_decimalPlaces) +
411 " MVA");
412 } break;
413 default:
414 break;
415 }
416 } break;
417 case DATA_PQ_THD: {
418 SetText(_("THD = ") + wxString::FromDouble(data.thd, m_decimalPlaces) + "%");
419 } break;
420 default:
421 break;
422 }
423 }
424 } break;
425 case TYPE_SYNC_GENERATOR: {
426 SyncGenerator* syncGenerator = static_cast<SyncGenerator*>(m_element);
427 if (syncGenerator) {
428 SyncGeneratorElectricalData data = syncGenerator->GetPUElectricalData(systemPowerBase);
429 double baseVoltage = syncGenerator->GetValueFromUnit(data.nominalVoltage, data.nominalVoltageUnit);
430 double baseCurrent = systemPowerBase / (std::sqrt(3.0) * baseVoltage);
431 bool busParentOnline = false;
432 auto parentList = syncGenerator->GetParentList();
433 if (parentList.size() == 1) {
434 Bus* busParent = dynamic_cast<Bus*>(parentList[0]);
435 if (busParent) busParentOnline = busParent->GetElectricalData().isConnected;
436 }
437 switch (m_dataType) {
438 case DATA_NAME: {
439 SetText(data.name);
440 } break;
441 case DATA_ACTIVE_POWER: {
442 double activePower = data.activePower;
443 if (!syncGenerator->IsOnline() || !busParentOnline) activePower = 0.0;
444 switch (m_unit) {
446 SetText(wxString::FromDouble(activePower, m_decimalPlaces) + " p.u.");
447 } break;
449 SetText(wxString::FromDouble(activePower * systemPowerBase, m_decimalPlaces) + " W");
450 } break;
452 SetText(wxString::FromDouble(activePower * systemPowerBase / 1e3, m_decimalPlaces) +
453 " kW");
454 } break;
456 SetText(wxString::FromDouble(activePower * systemPowerBase / 1e6, m_decimalPlaces) +
457 " MW");
458 } break;
459 default:
460 break;
461 }
462 } break;
463 case DATA_REACTIVE_POWER: {
464 double reactivePower = data.reactivePower;
465 if (!syncGenerator->IsOnline() || !busParentOnline) reactivePower = 0.0;
466 switch (m_unit) {
468 SetText(wxString::FromDouble(reactivePower, m_decimalPlaces) + " p.u.");
469 } break;
471 SetText(wxString::FromDouble(reactivePower * systemPowerBase, m_decimalPlaces) +
472 " var");
473 } break;
475 SetText(wxString::FromDouble(reactivePower * systemPowerBase / 1e3, m_decimalPlaces) +
476 " kvar");
477 } break;
479 SetText(wxString::FromDouble(reactivePower * systemPowerBase / 1e6, m_decimalPlaces) +
480 " Mvar");
481 } break;
482 default:
483 break;
484 }
485 } break;
486 case DATA_SC_CURRENT: {
487 double faultCurrent[3] = { std::abs(data.faultCurrent[0]), std::abs(data.faultCurrent[1]),
488 std::abs(data.faultCurrent[2]) };
489 switch (m_unit) {
491 wxString str =
492 "Ia = " + wxString::FromDouble(faultCurrent[0], m_decimalPlaces) + " p.u.";
493 str += "\nIb = " + wxString::FromDouble(faultCurrent[1], m_decimalPlaces) + " p.u.";
494 str += "\nIc = " + wxString::FromDouble(faultCurrent[2], m_decimalPlaces) + " p.u.";
495 SetText(str);
496 } break;
498 wxString str =
499 "Ia = " + wxString::FromDouble(faultCurrent[0] * baseCurrent, m_decimalPlaces) +
500 " A";
501 str += "\nIb = " +
502 wxString::FromDouble(faultCurrent[1] * baseCurrent, m_decimalPlaces) + " A";
503 str += "\nIc = " +
504 wxString::FromDouble(faultCurrent[2] * baseCurrent, m_decimalPlaces) + " A";
505 SetText(str);
506 } break;
508 wxString str =
509 "Ia = " +
510 wxString::FromDouble(faultCurrent[0] * baseCurrent / 1e3, m_decimalPlaces) + " kA";
511 str += "\nIb = " +
512 wxString::FromDouble(faultCurrent[1] * baseCurrent / 1e3, m_decimalPlaces) +
513 " kA";
514 str += "\nIc = " +
515 wxString::FromDouble(faultCurrent[2] * baseCurrent / 1e3, m_decimalPlaces) +
516 " kA";
517 SetText(str);
518 } break;
519 default:
520 break;
521 }
522 } break;
523 default:
524 break;
525 }
526 }
527 } break;
528 case TYPE_LINE: {
529 Line* line = static_cast<Line*>(m_element);
530 if (line) {
531 LineElectricalData data = line->GetElectricalData();
532 double baseVoltage = data.nominalVoltage;
533 if (data.nominalVoltageUnit == ElectricalUnit::UNIT_kV) baseVoltage *= 1e3;
534 double baseCurrent = systemPowerBase / (std::sqrt(3.0) * baseVoltage);
535 bool busParentOnline = false;
536 auto parentList = line->GetParentList();
537 if (parentList.size() == 2) {
538 Bus* busParent1 = dynamic_cast<Bus*>(parentList[0]);
539 Bus* busParent2 = dynamic_cast<Bus*>(parentList[1]);
540 if (busParent1 && busParent2) {
541 if (busParent1->GetElectricalData().isConnected && busParent1->GetElectricalData().isConnected)
542 busParentOnline = true;
543 }
544 }
545 switch (m_dataType) {
546 case DATA_NAME: {
547 SetText(data.name);
548 } break;
549 case DATA_PF_ACTIVE: {
550 double activePF = std::real(data.powerFlow[m_direction]);
551 if (!line->IsOnline() || !busParentOnline) activePF = 0.0;
552 switch (m_unit) {
554 SetText(wxString::FromDouble(activePF, m_decimalPlaces) + " p.u.");
555 } break;
557 SetText(wxString::FromDouble(activePF * systemPowerBase, m_decimalPlaces) + " W");
558 } break;
560 SetText(wxString::FromDouble(activePF * systemPowerBase / 1e3, m_decimalPlaces) +
561 " kW");
562 } break;
564 SetText(wxString::FromDouble(activePF * systemPowerBase / 1e6, m_decimalPlaces) +
565 " MW");
566 } break;
567 default:
568 break;
569 }
570 } break;
571 case DATA_PF_REACTIVE: {
572 double reactivePF = std::imag(data.powerFlow[m_direction]);
573 if (!line->IsOnline() || !busParentOnline) reactivePF = 0.0;
574 switch (m_unit) {
576 SetText(wxString::FromDouble(reactivePF, m_decimalPlaces) + " p.u.");
577 } break;
579 SetText(wxString::FromDouble(reactivePF * systemPowerBase, m_decimalPlaces) + " var");
580 } break;
582 SetText(wxString::FromDouble(reactivePF * systemPowerBase / 1e3, m_decimalPlaces) +
583 " kvar");
584 } break;
586 SetText(wxString::FromDouble(reactivePF * systemPowerBase / 1e6, m_decimalPlaces) +
587 " Mvar");
588 } break;
589 default:
590 break;
591 }
592 } break;
593 case DATA_PF_LOSSES: {
594 double losses = std::abs(std::real(data.powerFlow[0]) + std::real(data.powerFlow[1]));
595 if (!line->IsOnline() || !busParentOnline) losses = 0.0;
596 switch (m_unit) {
598 SetText(wxString::FromDouble(losses, m_decimalPlaces) + " p.u.");
599 } break;
601 SetText(wxString::FromDouble(losses * systemPowerBase, m_decimalPlaces) + " W");
602 } break;
604 SetText(wxString::FromDouble(losses * systemPowerBase / 1e3, m_decimalPlaces) + " kW");
605 } break;
607 SetText(wxString::FromDouble(losses * systemPowerBase / 1e6, m_decimalPlaces) + " MW");
608 } break;
609 default:
610 break;
611 }
612 } break;
613 case DATA_PF_CURRENT: {
614 double current = std::abs(data.current[m_direction]);
615 if (!line->IsOnline() || !busParentOnline) current = 0.0;
616 switch (m_unit) {
618 SetText(wxString::FromDouble(current, m_decimalPlaces) + " p.u.");
619 } break;
621 SetText(wxString::FromDouble(current * baseCurrent, m_decimalPlaces) + " A");
622 } break;
624 SetText(wxString::FromDouble(current * baseCurrent / 1e3, m_decimalPlaces) + " kA");
625 } break;
626 default:
627 break;
628 }
629 } break;
630 case DATA_SC_CURRENT: {
631 double faultCurrent[3] = { std::abs(data.faultCurrent[m_direction][0]),
632 std::abs(data.faultCurrent[m_direction][1]),
633 std::abs(data.faultCurrent[m_direction][2]) };
634 if (!line->IsOnline()) faultCurrent[0] = faultCurrent[1] = faultCurrent[2] = 0.0;
635 switch (m_unit) {
637 wxString str =
638 "Ia = " + wxString::FromDouble(faultCurrent[0], m_decimalPlaces) + " p.u.";
639 str += "\nIb = " + wxString::FromDouble(faultCurrent[1], m_decimalPlaces) + " p.u.";
640 str += "\nIc = " + wxString::FromDouble(faultCurrent[2], m_decimalPlaces) + " p.u.";
641 SetText(str);
642 } break;
644 wxString str =
645 "Ia = " + wxString::FromDouble(faultCurrent[0] * baseCurrent, m_decimalPlaces) +
646 " A";
647 str += "\nIb = " +
648 wxString::FromDouble(faultCurrent[1] * baseCurrent, m_decimalPlaces) + " A";
649 str += "\nIc = " +
650 wxString::FromDouble(faultCurrent[2] * baseCurrent, m_decimalPlaces) + " A";
651 SetText(str);
652 } break;
654 wxString str =
655 "Ia = " +
656 wxString::FromDouble(faultCurrent[0] * baseCurrent / 1e3, m_decimalPlaces) + " kA";
657 str += "\nIb = " +
658 wxString::FromDouble(faultCurrent[1] * baseCurrent / 1e3, m_decimalPlaces) +
659 " kA";
660 str += "\nIc = " +
661 wxString::FromDouble(faultCurrent[2] * baseCurrent / 1e3, m_decimalPlaces) +
662 " kA";
663 SetText(str);
664 } break;
665 default:
666 break;
667 }
668 } break;
669 default:
670 break;
671 }
672 }
673 } break;
674 case TYPE_TRANSFORMER: {
675 Transformer* transformer = static_cast<Transformer*>(m_element);
676 if (transformer) {
677 TransformerElectricalData data = transformer->GetElectricalData();
678 double baseVoltage[2] = { data.primaryNominalVoltage, data.secondaryNominalVoltage };
679 bool busParentOnline = false;
680 auto parentList = transformer->GetParentList();
681 if (parentList.size() == 2) {
682 Bus* busParent1 = dynamic_cast<Bus*>(parentList[0]);
683 Bus* busParent2 = dynamic_cast<Bus*>(parentList[1]);
684 if (busParent1 && busParent2) {
685 if (busParent1->GetElectricalData().isConnected && busParent1->GetElectricalData().isConnected)
686 busParentOnline = true;
687 }
688 }
689
690 if (data.primaryNominalVoltageUnit == ElectricalUnit::UNIT_kV) baseVoltage[0] *= 1e3;
691 if (data.secondaryNominalVoltageUnit == ElectricalUnit::UNIT_kV) baseVoltage[1] *= 1e3;
692
693 double baseCurrent[2] = { systemPowerBase / (std::sqrt(3.0) * baseVoltage[0]),
694 systemPowerBase / (std::sqrt(3.0) * baseVoltage[1]) };
695 switch (m_dataType) {
696 case DATA_NAME: {
697 SetText(data.name);
698 } break;
699 case DATA_PF_ACTIVE: {
700 double activePF = std::real(data.powerFlow[m_direction]);
701 if (!transformer->IsOnline() || !busParentOnline) activePF = 0.0;
702 switch (m_unit) {
704 SetText(wxString::FromDouble(activePF, m_decimalPlaces) + " p.u.");
705 } break;
707 SetText(wxString::FromDouble(activePF * systemPowerBase, m_decimalPlaces) + " W");
708 } break;
710 SetText(wxString::FromDouble(activePF * systemPowerBase / 1e3, m_decimalPlaces) +
711 " kW");
712 } break;
714 SetText(wxString::FromDouble(activePF * systemPowerBase / 1e6, m_decimalPlaces) +
715 " MW");
716 } break;
717 default:
718 break;
719 }
720 } break;
721 case DATA_PF_REACTIVE: {
722 double reactivePF = std::imag(data.powerFlow[m_direction]);
723 if (!transformer->IsOnline() || !busParentOnline) reactivePF = 0.0;
724 switch (m_unit) {
726 SetText(wxString::FromDouble(reactivePF, m_decimalPlaces) + " p.u.");
727 } break;
729 SetText(wxString::FromDouble(reactivePF * systemPowerBase, m_decimalPlaces) + " var");
730 } break;
732 SetText(wxString::FromDouble(reactivePF * systemPowerBase / 1e3, m_decimalPlaces) +
733 " kvar");
734 } break;
736 SetText(wxString::FromDouble(reactivePF * systemPowerBase / 1e6, m_decimalPlaces) +
737 " Mvar");
738 } break;
739 default:
740 break;
741 }
742 } break;
743 case DATA_PF_LOSSES: {
744 double losses = std::abs(std::real(data.powerFlow[0]) + std::real(data.powerFlow[1]));
745 if (!transformer->IsOnline() || !busParentOnline) losses = 0.0;
746 switch (m_unit) {
748 SetText(wxString::FromDouble(losses, m_decimalPlaces) + " p.u.");
749 } break;
751 SetText(wxString::FromDouble(losses * systemPowerBase, m_decimalPlaces) + " W");
752 } break;
754 SetText(wxString::FromDouble(losses * systemPowerBase / 1e3, m_decimalPlaces) + " kW");
755 } break;
757 SetText(wxString::FromDouble(losses * systemPowerBase / 1e6, m_decimalPlaces) + " MW");
758 } break;
759 default:
760 break;
761 }
762 } break;
763 case DATA_PF_CURRENT: {
764 double current = std::abs(data.current[m_direction]);
765 if (!transformer->IsOnline() || !busParentOnline) current = 0.0;
766 switch (m_unit) {
768 SetText(wxString::FromDouble(current, m_decimalPlaces) + " p.u.");
769 } break;
771 SetText(wxString::FromDouble(current * baseCurrent[m_direction], m_decimalPlaces) +
772 " A");
773 } break;
775 SetText(
776 wxString::FromDouble(current * baseCurrent[m_direction] / 1e3, m_decimalPlaces) +
777 " kA");
778 } break;
779 default:
780 break;
781 }
782 } break;
783 case DATA_SC_CURRENT: {
784 double faultCurrent[3] = { std::abs(data.faultCurrent[m_direction][0]),
785 std::abs(data.faultCurrent[m_direction][1]),
786 std::abs(data.faultCurrent[m_direction][2]) };
787 if (!transformer->IsOnline()) faultCurrent[0] = faultCurrent[1] = faultCurrent[2] = 0.0;
788 switch (m_unit) {
790 wxString str =
791 "Ia = " + wxString::FromDouble(faultCurrent[0], m_decimalPlaces) + " p.u.";
792 str += "\nIb = " + wxString::FromDouble(faultCurrent[1], m_decimalPlaces) + " p.u.";
793 str += "\nIc = " + wxString::FromDouble(faultCurrent[2], m_decimalPlaces) + " p.u.";
794 SetText(str);
795 } break;
797 wxString str =
798 "Ia = " +
799 wxString::FromDouble(faultCurrent[0] * baseCurrent[m_direction], m_decimalPlaces) +
800 " A";
801 str +=
802 "\nIb = " +
803 wxString::FromDouble(faultCurrent[1] * baseCurrent[m_direction], m_decimalPlaces) +
804 " A";
805 str +=
806 "\nIc = " +
807 wxString::FromDouble(faultCurrent[2] * baseCurrent[m_direction], m_decimalPlaces) +
808 " A";
809 SetText(str);
810 } break;
812 wxString str = "Ia = " +
813 wxString::FromDouble(faultCurrent[0] * baseCurrent[m_direction] / 1e3,
814 m_decimalPlaces) +
815 " kA";
816 str += "\nIb = " +
817 wxString::FromDouble(faultCurrent[1] * baseCurrent[m_direction] / 1e3,
818 m_decimalPlaces) +
819 " kA";
820 str += "\nIc = " +
821 wxString::FromDouble(faultCurrent[2] * baseCurrent[m_direction] / 1e3,
822 m_decimalPlaces) +
823 " kA";
824 SetText(str);
825 } break;
826 default:
827 break;
828 }
829 } break;
830 default:
831 break;
832 }
833 }
834 } break;
835 case TYPE_LOAD: {
836 Load* load = static_cast<Load*>(m_element);
837 if (load) {
838 LoadElectricalData data = load->GetPUElectricalData(systemPowerBase);
839 std::complex<double> sPower(data.activePower, data.reactivePower);
840 if (data.loadType == CONST_IMPEDANCE && load->IsOnline()) {
841 std::complex<double> v = static_cast<Bus*>(load->GetParentList()[0])->GetElectricalData().voltage;
842 sPower = std::pow(std::abs(v), 2) * sPower;
843 }
844 bool busParentOnline = false;
845 auto parentList = load->GetParentList();
846 if (parentList.size() == 1) {
847 Bus* busParent = dynamic_cast<Bus*>(parentList[0]);
848 if (busParent)
849 busParentOnline = busParent->GetElectricalData().isConnected;
850 }
851
852 if (!load->IsOnline() || !busParentOnline) sPower = std::complex<double>(0.0, 0.0);
853 switch (m_dataType) {
854 case DATA_NAME: {
855 SetText(data.name);
856 } break;
857 case DATA_ACTIVE_POWER: {
858 switch (m_unit) {
860 SetText(wxString::FromDouble(sPower.real(), m_decimalPlaces) + " p.u.");
861 } break;
863 SetText(wxString::FromDouble(sPower.real() * systemPowerBase, m_decimalPlaces) + " W");
864 } break;
866 SetText(wxString::FromDouble(sPower.real() * systemPowerBase / 1e3, m_decimalPlaces) +
867 " kW");
868 } break;
870 SetText(wxString::FromDouble(sPower.real() * systemPowerBase / 1e6, m_decimalPlaces) +
871 " MW");
872 } break;
873 default:
874 break;
875 }
876 } break;
877 case DATA_REACTIVE_POWER: {
878 switch (m_unit) {
880 SetText(wxString::FromDouble(sPower.imag(), m_decimalPlaces) + " p.u.");
881 } break;
883 SetText(wxString::FromDouble(sPower.imag() * systemPowerBase, m_decimalPlaces) +
884 " var");
885 } break;
887 SetText(wxString::FromDouble(sPower.imag() * systemPowerBase / 1e3, m_decimalPlaces) +
888 " kvar");
889 } break;
891 SetText(wxString::FromDouble(sPower.imag() * systemPowerBase / 1e6, m_decimalPlaces) +
892 " Mvar");
893 } break;
894 default:
895 break;
896 }
897 } break;
898 default:
899 break;
900 }
901 }
902 } break;
903 case TYPE_SYNC_MOTOR: {
904 SyncMotor* syncMotor = static_cast<SyncMotor*>(m_element);
905
906 if (syncMotor) {
907 SyncMotorElectricalData data = syncMotor->GetPUElectricalData(systemPowerBase);
908 bool busParentOnline = false;
909 auto parentList = syncMotor->GetParentList();
910 if (parentList.size() == 1) {
911 Bus* busParent = dynamic_cast<Bus*>(parentList[0]);
912 if (busParent)
913 busParentOnline = busParent->GetElectricalData().isConnected;
914 }
915 std::complex<double> sPower(data.activePower, data.reactivePower);
916 if (!syncMotor->IsOnline() || !busParentOnline) sPower = std::complex<double>(0.0, 0.0);
917 switch (m_dataType) {
918 case DATA_NAME: {
919 SetText(data.name);
920 } break;
921 case DATA_ACTIVE_POWER: {
922 switch (m_unit) {
924 SetText(wxString::FromDouble(sPower.real(), m_decimalPlaces) + " p.u.");
925 } break;
927 SetText(wxString::FromDouble(sPower.real() * systemPowerBase, m_decimalPlaces) + " W");
928 } break;
930 SetText(wxString::FromDouble(sPower.real() * systemPowerBase / 1e3, m_decimalPlaces) +
931 " kW");
932 } break;
934 SetText(wxString::FromDouble(sPower.real() * systemPowerBase / 1e6, m_decimalPlaces) +
935 " MW");
936 } break;
937 default:
938 break;
939 }
940 } break;
941 case DATA_REACTIVE_POWER: {
942 switch (m_unit) {
944 SetText(wxString::FromDouble(sPower.imag(), m_decimalPlaces) + " p.u.");
945 } break;
947 SetText(wxString::FromDouble(sPower.imag() * systemPowerBase, m_decimalPlaces) +
948 " var");
949 } break;
951 SetText(wxString::FromDouble(sPower.imag() * systemPowerBase / 1e3, m_decimalPlaces) +
952 " kvar");
953 } break;
955 SetText(wxString::FromDouble(sPower.imag() * systemPowerBase / 1e6, m_decimalPlaces) +
956 " Mvar");
957 } break;
958 default:
959 break;
960 }
961 } break;
962 default:
963 break;
964 }
965 }
966 } break;
967 case TYPE_IND_MOTOR: {
968 IndMotor* indMotor = static_cast<IndMotor*>(m_element);
969 if (indMotor) {
970 IndMotorElectricalData data = indMotor->GetPUElectricalData(systemPowerBase);
971 bool busParentOnline = false;
972 auto parentList = indMotor->GetParentList();
973 if (parentList.size() == 1) {
974 Bus* busParent = dynamic_cast<Bus*>(parentList[0]);
975 if (busParent)
976 busParentOnline = busParent->GetElectricalData().isConnected;
977 }
978 std::complex<double> sPower(data.activePower, data.reactivePower);
979 if (!indMotor->IsOnline() || !busParentOnline) sPower = std::complex<double>(0.0, 0.0);
980 switch (m_dataType) {
981 case DATA_NAME: {
982 SetText(data.name);
983 } break;
984 case DATA_ACTIVE_POWER: {
985 switch (m_unit) {
987 SetText(wxString::FromDouble(sPower.real(), m_decimalPlaces) + " p.u.");
988 } break;
990 SetText(wxString::FromDouble(sPower.real() * systemPowerBase, m_decimalPlaces) + " W");
991 } break;
993 SetText(wxString::FromDouble(sPower.real() * systemPowerBase / 1e3, m_decimalPlaces) +
994 " kW");
995 } break;
997 SetText(wxString::FromDouble(sPower.real() * systemPowerBase / 1e6, m_decimalPlaces) +
998 " MW");
999 } break;
1000 default:
1001 break;
1002 }
1003 } break;
1004 case DATA_REACTIVE_POWER: {
1005 switch (m_unit) {
1007 SetText(wxString::FromDouble(sPower.imag(), m_decimalPlaces) + " p.u.");
1008 } break;
1010 SetText(wxString::FromDouble(sPower.imag() * systemPowerBase, m_decimalPlaces) +
1011 " var");
1012 } break;
1014 SetText(wxString::FromDouble(sPower.imag() * systemPowerBase / 1e3, m_decimalPlaces) +
1015 " kvar");
1016 } break;
1018 SetText(wxString::FromDouble(sPower.imag() * systemPowerBase / 1e6, m_decimalPlaces) +
1019 " Mvar");
1020 }break;
1021 default:
1022 break;
1023 }
1024 } break;
1025 default:
1026 break;
1027 }
1028 }
1029 } break;
1030 case TYPE_CAPACITOR: {
1031 Capacitor* capacitor = static_cast<Capacitor*>(m_element);
1032 if (capacitor) {
1033 CapacitorElectricalData data = capacitor->GetPUElectricalData(systemPowerBase);
1034 double reativePower = data.reactivePower;
1035
1036 bool busParentOnline = false;
1037 auto parentList = capacitor->GetParentList();
1038 if (parentList.size() == 1) {
1039 Bus* busParent = dynamic_cast<Bus*>(parentList[0]);
1040 if (busParent)
1041 busParentOnline = busParent->GetElectricalData().isConnected;
1042 }
1043
1044 if (!capacitor->IsOnline() || !busParentOnline)
1045 reativePower = 0.0;
1046 else {
1047 std::complex<double> v =
1048 static_cast<Bus*>(capacitor->GetParentList()[0])->GetElectricalData().voltage;
1049 reativePower *= std::pow(std::abs(v), 2);
1050 }
1051 switch (m_dataType) {
1052 case DATA_NAME: {
1053 SetText(data.name);
1054 } break;
1055 case DATA_REACTIVE_POWER: {
1056 switch (m_unit) {
1058 SetText(wxString::FromDouble(reativePower, m_decimalPlaces) + " p.u.");
1059 }break;
1061 SetText(wxString::FromDouble(reativePower * systemPowerBase, m_decimalPlaces) + " var");
1062 }break;
1064 SetText(wxString::FromDouble(reativePower * systemPowerBase / 1e3, m_decimalPlaces) +
1065 " kvar");
1066 }break;
1068 SetText(wxString::FromDouble(reativePower * systemPowerBase / 1e6, m_decimalPlaces) +
1069 " Mvar");
1070 }break;
1071 default:
1072 break;
1073 }
1074 } break;
1075 default:
1076 break;
1077 }
1078 }
1079 } break;
1080 case TYPE_INDUCTOR: {
1081 Inductor* inductor = static_cast<Inductor*>(m_element);
1082 if (inductor) {
1083 InductorElectricalData data = inductor->GetPUElectricalData(systemPowerBase);
1084 double reativePower = data.reactivePower;
1085
1086 bool busParentOnline = false;
1087 auto parentList = inductor->GetParentList();
1088 if (parentList.size() == 1) {
1089 Bus* busParent = dynamic_cast<Bus*>(parentList[0]);
1090 if (busParent)
1091 busParentOnline = busParent->GetElectricalData().isConnected;
1092 }
1093
1094 if (!inductor->IsOnline() || !busParentOnline)
1095 reativePower = 0.0;
1096 else {
1097 std::complex<double> v =
1098 static_cast<Bus*>(inductor->GetParentList()[0])->GetElectricalData().voltage;
1099 reativePower *= std::pow(std::abs(v), 2);
1100 }
1101 switch (m_dataType) {
1102 case DATA_NAME: {
1103 SetText(data.name);
1104 } break;
1105 case DATA_REACTIVE_POWER: {
1106 switch (m_unit) {
1108 SetText(wxString::FromDouble(reativePower, m_decimalPlaces) + " p.u.");
1109 }break;
1111 SetText(wxString::FromDouble(reativePower * systemPowerBase, m_decimalPlaces) + " var");
1112 }break;
1114 SetText(wxString::FromDouble(reativePower * systemPowerBase / 1e3, m_decimalPlaces) +
1115 " kvar");
1116 }break;
1118 SetText(wxString::FromDouble(reativePower * systemPowerBase / 1e6, m_decimalPlaces) +
1119 " Mvar");
1120 } break;
1121 default:
1122 break;
1123 }
1124 } break;
1125 default:
1126 break;
1127 }
1128 }
1129 } break;
1130 case TYPE_HARMCURRENT: {
1131 HarmCurrent* harmCurrent = static_cast<HarmCurrent*>(m_element);
1132 if (harmCurrent) {
1133 auto data = harmCurrent->GetElectricalData();
1134 switch (m_dataType) {
1135 case DATA_NAME: {
1136 SetText(data.name);
1137 } break;
1138 default:
1139 break;
1140 }
1141 }
1142 } break;
1143 }
1144}
1145
1147{
1148 Text* copy = new Text();
1149 *copy = *this;
1150 std::vector<GCText*> copyList;
1151 for (auto it = m_gcTextList.begin(), itEnd = m_gcTextList.end(); it != itEnd; ++it) {
1152 copyList.push_back((*it)->GetCopy());
1153 }
1154 copy->m_gcTextList = copyList;
1155 return copy;
1156}
1157
1158//bool Text::IsGLTextOK()
1159//{
1160// bool isOk = true;
1161// for (auto it = m_openGLTextList.begin(), itEnd = m_openGLTextList.end(); it != itEnd; ++it) {
1162// if (!(*it)->IsTextureOK()) isOk = false;
1163// }
1164// return isOk;
1165//}
1166
1167rapidxml::xml_node<>* Text::SaveElement(rapidxml::xml_document<>& doc, rapidxml::xml_node<>* elementListNode)
1168{
1169 auto elementNode = XMLParser::AppendNode(doc, elementListNode, "Text");
1170 XMLParser::SetNodeAttribute(doc, elementNode, "ID", m_elementID);
1171
1172 SaveCADProperties(doc, elementNode);
1173
1174 auto textProperties = XMLParser::AppendNode(doc, elementNode, "TextProperties");
1175 auto elementType = XMLParser::AppendNode(doc, textProperties, "ElementType");
1176 XMLParser::SetNodeValue(doc, elementType, m_elementTypeText);
1177 auto elementNumber = XMLParser::AppendNode(doc, textProperties, "ElementNumber");
1178 XMLParser::SetNodeValue(doc, elementNumber, m_elementNumber);
1179 auto dataType = XMLParser::AppendNode(doc, textProperties, "DataType");
1180 XMLParser::SetNodeValue(doc, dataType, m_dataType);
1181 auto dataUnit = XMLParser::AppendNode(doc, textProperties, "DataUnit");
1182 XMLParser::SetNodeValue(doc, dataUnit, static_cast<int>(m_unit));
1183 auto direction = XMLParser::AppendNode(doc, textProperties, "Direction");
1184 XMLParser::SetNodeValue(doc, direction, m_direction);
1185 auto decimalPlaces = XMLParser::AppendNode(doc, textProperties, "DecimalPlaces");
1186 XMLParser::SetNodeValue(doc, decimalPlaces, m_decimalPlaces);
1187
1188 return elementNode;
1189}
1190
1191bool Text::OpenElement(rapidxml::xml_node<>* elementNode)
1192{
1193 if (!OpenCADProperties(elementNode)) return false;
1194
1195 auto textProperties = elementNode->first_node("TextProperties");
1196 if (!textProperties) return false;
1197
1198 SetElementTypeText(static_cast<ElementType>(XMLParser::GetNodeValueDouble(textProperties, "ElementType")));
1199 SetDataType(static_cast<DataType>(XMLParser::GetNodeValueDouble(textProperties, "DataType")));
1200 SetUnit(static_cast<ElectricalUnit>(XMLParser::GetNodeValueDouble(textProperties, "DataUnit")));
1201 SetDirection(XMLParser::GetNodeValueDouble(textProperties, "Direction"));
1202 SetDecimalPlaces(XMLParser::GetNodeValueDouble(textProperties, "DecimalPlaces"));
1203 SetElementNumber(XMLParser::GetNodeValueInt(textProperties, "ElementNumber"));
1204 SetInserted();
1205 return true;
1206}
ElectricalUnit
Electrical units.
Node for power elements. All others power elements are connected through this.
Definition Bus.h:86
Shunt capactior power element.
Definition Capacitor.h:39
Base class of all elements of the program. This class is responsible for manage graphical and his dat...
Definition Element.h:112
virtual bool RotatedRectanglesIntersects(wxRect2DDouble rect1, wxRect2DDouble rect2, double angle1, double angle2) const
Check if two roteted rectangles intersect.
Definition Element.cpp:359
virtual std::vector< Element * > GetParentList() const
Get the parent list.
Definition Element.h:559
bool IsOnline() const
Checks if the element is online or offline.
Definition Element.h:226
virtual void DrawDCRectangle(wxPoint2DDouble position, double width, double height, double angle, wxDC &dc) const
Draw a circle.
Definition Element.cpp:45
void SetPosition(const wxPoint2DDouble position)
Set the element position and update the rectangle.
Definition Element.cpp:27
virtual wxPoint2DDouble RotateAtPosition(wxPoint2DDouble pointToRotate, double angle, bool degrees=true) const
Rotate a point as element position being the origin.
Definition Element.cpp:292
void SetInserted(bool inserted=true)
Set if the element is properly inserted in the workspace.
Definition Element.h:631
Class to draw text on Graphics Context using wxWidgets.
Definition GCText.h:32
Abstract class for graphical elements shown with power elements in workspace.
Shunt Harmonic Corrent Source.
Definition HarmCurrent.h:24
Induction motor power element.
Definition IndMotor.h:119
Inductor shunt power element.
Definition Inductor.h:39
Power line element.
Definition Line.h:64
Loas shunt power element.
Definition Load.h:74
Synchronous generator power element.
Synchronous motor (synchronous compensator) power element.
Definition SyncMotor.h:135
Form to edit the text graphical data.
Definition TextForm.h:33
Element that shows power element informations in workspace.
Definition Text.h:70
virtual Element * GetCopy()
Get a the element copy.
Definition Text.cpp:1146
virtual bool Contains(wxPoint2DDouble position) const
Checks if the element contains a position.
Definition Text.cpp:62
virtual bool Intersects(wxRect2DDouble rect) const
Check if the element's rect intersects other rect.
Definition Text.cpp:242
virtual void Rotate(bool clockwise=true)
Rotate the element.
Definition Text.cpp:254
Two-winding transformer power element.
Definition Transformer.h:84