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