25#include "../elements/Element.h"
26#include "../elements/ElementDataObject.h"
27#include "../elements/Text.h"
28#include "../elements/powerElement/Capacitor.h"
29#include "../elements/powerElement/HarmCurrent.h"
30#include "../elements/powerElement/IndMotor.h"
31#include "../elements/powerElement/Inductor.h"
32#include "../elements/powerElement/Line.h"
33#include "../elements/powerElement/Load.h"
34#include "../elements/powerElement/SyncGenerator.h"
35#include "../elements/powerElement/SyncMotor.h"
36#include "../elements/powerElement/Transformer.h"
37#include "../elements/powerElement/EMTElement.h"
39#include "../simulation/Electromechanical.h"
40#include "../simulation/Fault.h"
41#include "../simulation/PowerFlow.h"
42#include "../simulation/PowerQuality.h"
44#include "../utils/ElementPlotData.h"
46#include "../forms/FrequencyResponseForm.h"
48#include "../utils/Camera.h"
49#include "../utils/PropertiesData.h"
50#include "../utils/HMPlane.h"
51#include "../utils/FileHanding.h"
52#include "../utils/Path.h"
54#include <wx/busyinfo.h>
62DWORD GetWorkspaceGDIObjects()
64 return GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
67void LogWorkspaceGDIDelta(
const wxString& tag, DWORD before)
69 const DWORD after = GetWorkspaceGDIObjects();
70 if (after != before) {
71 wxLogDebug(
"%s: GDI %lu -> %lu (%+ld)",
75 static_cast<long>(after) -
static_cast<long>(before));
82Workspace::Workspace() : WorkspaceBase(nullptr)
85#ifdef SHOW_DEBUG_PANEL
87 m_debugFrame->SetTitle(_(
"Debug window: ") + m_name);
92 SetBackgroundColour(wxColour(255, 255, 255));
93 SetBackgroundStyle(wxBG_STYLE_PAINT);
96Workspace::Workspace(wxWindow* parent, wxString name, wxStatusBar* statusBar, wxAuiNotebook* auiNotebook)
97 : WorkspaceBase(parent)
100#ifdef SHOW_DEBUG_PANEL
102 m_debugFrame->SetTitle(_(
"Debug window: ") + m_name);
103 m_debugFrame->Show();
110 m_statusBar = statusBar;
111 m_auiNotebook = auiNotebook;
114 m_selectionRect = wxRect2DDouble(0, 0, 0, 0);
116 for (
int i = 0; i < NUM_ELEMENTS; ++i) { m_elementNumber[i] = 1; }
118 const int widths[4] = { -3, -1, 100, 100 };
119 m_statusBar->SetStatusWidths(4, widths);
124 m_workspacePanel->SetBackgroundColour(wxColour(255, 255, 255));
125 m_workspacePanel->SetBackgroundStyle(wxBG_STYLE_PAINT);
129 m_width =
static_cast<float>(m_workspacePanel->GetSize().x) - 1.0;
130 m_height =
static_cast<float>(m_workspacePanel->GetSize().y) - 1.0;
136Workspace::~Workspace()
145 if (m_hmPlane)
delete m_hmPlane;
147 if (m_camera)
delete m_camera;
152 if (m_tipWindow)
delete m_tipWindow;
153 if (m_properties)
delete m_properties;
156void Workspace::OnPaint(wxPaintEvent& event)
159 const DWORD beforePaint = GetWorkspaceGDIObjects();
162 wxBufferedPaintDC dc(m_workspacePanel);
164 wxGraphicsContext* gc = wxGraphicsContext::Create(dc);
171 LogWorkspaceGDIDelta(
"Workspace::OnPaint", beforePaint);
177void Workspace::DrawScene(wxGraphicsContext* gc)
183 if (m_hmPlane && m_showHM) {
185 const DWORD beforeHMPlane = GetWorkspaceGDIObjects();
187 m_hmPlane->DrawDC(gc);
189 LogWorkspaceGDIDelta(
"Workspace::DrawScene HMPlane", beforeHMPlane);
193 gc->Scale(m_camera->GetScale(), m_camera->GetScale());
194 gc->Translate(m_camera->GetTranslation().m_x, m_camera->GetTranslation().m_y);
197 for (
auto& element : m_elementList) {
199 const DWORD beforeElement = GetWorkspaceGDIObjects();
201 element->DrawDC(m_camera->GetTranslation(), m_camera->GetScale(), gc);
203 LogWorkspaceGDIDelta(wxString::Format(
"Workspace::DrawScene element type %d", element->GetElementType()), beforeElement);
210 const DWORD beforeDummyText = GetWorkspaceGDIObjects();
212 Text* text =
new Text(wxPoint2DDouble(0.0, 0.0), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
214 text->DrawDC(m_camera->GetTranslation(), m_camera->GetScale(), gc);
217 LogWorkspaceGDIDelta(
"Workspace::DrawScene dummy text", beforeDummyText);
221 for (
auto& text : m_textList) {
223 const DWORD beforeText = GetWorkspaceGDIObjects();
225 text->DrawDC(m_camera->GetTranslation(), m_camera->GetScale(), gc);
227 LogWorkspaceGDIDelta(
"Workspace::DrawScene text", beforeText);
233 const DWORD beforeSelectionRect = GetWorkspaceGDIObjects();
235 gc->SetPen(wxPen(wxColour(0, 125, 255, 255)));
236 gc->SetBrush(wxBrush(wxColour(0, 125, 255, 125)));
237 gc->DrawRectangle(m_selectionRect.m_x, m_selectionRect.m_y, m_selectionRect.m_width, m_selectionRect.m_height);
239 LogWorkspaceGDIDelta(
"Workspace::DrawScene selection rectangle", beforeSelectionRect);
242 if (m_hmPlane && m_showHM) {
244 const DWORD beforeHMLabel = GetWorkspaceGDIObjects();
246 m_hmPlane->DrawLabelDC(gc);
248 LogWorkspaceGDIDelta(
"Workspace::DrawScene heatmap label", beforeHMLabel);
254void Workspace::DrawScene(wxDC& dc)
257 if (m_hmPlane && m_showHM) {
258 m_hmPlane->DrawDC(dc);
261 dc.SetUserScale(m_camera->GetScale(), m_camera->GetScale());
262 dc.SetDeviceOrigin(m_camera->GetTranslation().m_x * m_camera->GetScale(), m_camera->GetTranslation().m_y * m_camera->GetScale());
266 for (
auto& element : m_elementList) {
267 element->DrawDC(m_camera->GetTranslation(), m_camera->GetScale(), dc);
272 Text* text =
new Text(wxPoint2DDouble(0.0, 0.0), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
274 text->DrawDC(m_camera->GetTranslation(), m_camera->GetScale(), dc);
278 for (
auto& text : m_textList) {
279 text->DrawDC(m_camera->GetTranslation(), m_camera->GetScale(), dc);
282 if (m_hmPlane && m_showHM) {
283 m_hmPlane->DrawLabelDC(dc);
287void Workspace::CopyToClipboard()
289 wxSize size = GetClientSize();
293 wxBitmap bitmap(size.x * scale, size.y * scale);
294 wxMemoryDC memDC(bitmap);
296 memDC.SetBackground(*wxWHITE_BRUSH);
299 wxGraphicsContext* gc = wxGraphicsContext::Create(memDC);
301 gc->Scale(scale, scale);
306 memDC.SelectObject(wxNullBitmap);
308 if (wxTheClipboard->Open())
310 wxTheClipboard->SetData(
new wxBitmapDataObject(bitmap));
311 wxTheClipboard->Close();
315void Workspace::ExportAsSVG(wxString path)
317 wxSize size = GetClientSize();
319 wxSVGFileDC svgDC(path, size.x, size.y);
321 svgDC.SetClippingRegion(wxRect(0, 0, size.x, size.y));
326void Workspace::OnLeftClickDown(wxMouseEvent& event)
328 wxWindow* dialogParent = wxGetTopLevelParent(
this);
329 if (!dialogParent) dialogParent =
this;
332 wxPoint clickPoint =
event.GetPosition();
333 bool foundElement =
false;
335 bool showNewElementForm =
false;
336 bool clickOnSwitch =
false;
337 bool unselectAll =
true;
338 std::vector<Element*> notUnselectElementList;
339 std::vector<Text*> notUnselectTextList;
341 if (m_mode == WorkspaceMode::MODE_INSERT_TEXT || m_mode == WorkspaceMode::MODE_PASTE || m_mode == WorkspaceMode::MODE_DRAG_PASTE) {
342 m_mode = WorkspaceMode::MODE_EDIT;
345 else if (m_mode == WorkspaceMode::MODE_INSERT || m_mode == WorkspaceMode::MODE_DRAG_INSERT || m_mode == WorkspaceMode::MODE_DRAG_INSERT_TEXT) {
346 wxPoint2DDouble clickPointWorld = m_camera->ScreenToWorld(clickPoint);
348 if (!m_elementList.empty()) {
350 newElement = m_elementList.back().get();
351 for (
auto& element : m_elementList) {
353 if (element->Contains(clickPointWorld)) {
355 if (
auto bus =
dynamic_cast<Bus*
>(element.get())) {
361 if (newElement->
AddParent(bus, clickPointWorld)) {
362 ValidateElementsVoltages();
364 showNewElementForm =
true;
365 m_mode = WorkspaceMode::MODE_EDIT;
374 if (
auto line =
dynamic_cast<Line*
>(newElement)) { line->AddPoint(clickPointWorld); }
382 bool clickPickbox =
false;
384 for (
auto& element : m_elementList) {
385 element->ResetPickboxes();
388 element->StartMove(m_camera->ScreenToWorld(clickPoint));
391 if (element->NodeContains(m_camera->ScreenToWorld(clickPoint)) != 0 && element->IsSelected()) {
392 m_mode = WorkspaceMode::MODE_MOVE_NODE;
393 m_disconnectedElement =
true;
396 if (m_hmPlane && m_showHM) {
402 else if (element->Contains(m_camera->ScreenToWorld(clickPoint))) {
403 notUnselectElementList.emplace_back(element.get());
405 if (element->IsSelected()) unselectAll =
false;
407 element->SetSelected();
408 element->ShowPickbox();
411 for (
auto& text : m_textList) {
412 if (text->GetElement() == element.get()) {
413 notUnselectTextList.emplace_back(text.get());
415 text->SetAllowRotation(
false);
416 if (unselectAll) text->SetAltSelectionColour();
421 if (element->PickboxContains(m_camera->ScreenToWorld(clickPoint))) {
422 m_mode = WorkspaceMode::MODE_MOVE_PICKBOX;
426 if (!clickPickbox) { m_mode = WorkspaceMode::MODE_MOVE_ELEMENT; }
428 if (m_hmPlane && m_showHM) {
434 else if (element->SwitchesContains(m_camera->ScreenToWorld(clickPoint))) {
435 element->SetOnline(element->IsOnline() ?
false : true);
436 clickOnSwitch =
true;
441 for (
auto& text : m_textList) {
442 text->
StartMove(m_camera->ScreenToWorld(clickPoint));
444 if (text->
Contains(m_camera->ScreenToWorld(clickPoint))) {
445 notUnselectTextList.emplace_back(text.get());
449 text->SetAltSelectionColour(
false);
450 text->SetAllowRotation();
451 m_mode = WorkspaceMode::MODE_MOVE_ELEMENT;
453 if (m_hmPlane && m_showHM) {
462 if (!event.ControlDown() && unselectAll) {
463 for (
auto& element : m_elementList) {
465 for (
Element* notUnselectElement : notUnselectElementList) {
466 if (notUnselectElement == element.get()) select =
true;
468 element->SetSelected(select);
470 for (
auto& text : m_textList) {
472 for (
auto& notUnselectText : notUnselectTextList) {
473 if (notUnselectText == text.get()) select =
true;
479 if (!foundElement && !clickOnSwitch) {
480 m_mode = WorkspaceMode::MODE_SELECTION_RECT;
481 m_startSelRect = m_camera->ScreenToWorld(clickPoint);
482 if (m_hmPlane && m_showHM) {
490 if (showNewElementForm) {
492 newElement->
ShowForm(dialogParent, newElement,
this);
494 if (m_workspacePanel) m_workspacePanel->SetFocus();
495 CheckSlackBusDuplication(newElement);
497 if (m_continuousCalc) RunStaticStudies();
500 if (clickOnSwitch && m_continuousCalc) RunStaticStudies();
505void Workspace::OnLeftDoubleClick(wxMouseEvent& event)
507 wxWindow* dialogParent = wxGetTopLevelParent(
this);
508 if (!dialogParent) dialogParent =
this;
511 bool elementEdited =
false;
512 bool clickOnSwitch =
false;
515 for (
auto& element : m_elementList) {
517 if (element->Contains(m_camera->ScreenToWorld(event.GetPosition()))) {
518 bool elementIsBus =
false;
520 Bus* currentBus =
nullptr;
521 if ((currentBus =
dynamic_cast<Bus*
>(element.get()))) {
523 oldBus = *currentBus;
526 if (element->ShowForm(dialogParent, element.get(),
this)) {
527 CheckSlackBusDuplication(element.get());
531 if (m_workspacePanel) m_workspacePanel->SetFocus();
532 elementEdited =
true;
539 if (oldBus.GetElectricalData().nominalVoltage != currentBus->GetElectricalData().nominalVoltage ||
540 oldBus.GetElectricalData().nominalVoltageUnit !=
541 currentBus->GetElectricalData().nominalVoltageUnit) {
543 std::vector<Element*> childList = element->GetChildList();
544 for (
auto itc = childList.begin(), itcEnd = childList.end(); itc != itcEnd; ++itc) {
546 if (
typeid(*child) ==
typeid(
Line)) {
547 wxMessageDialog msgDialog(dialogParent, _(
"Do you want to change the rated voltage of the path?"),
548 _(
"Warning"), wxYES_NO | wxCENTRE | wxICON_WARNING);
549 if (msgDialog.ShowModal() == wxID_YES)
550 ValidateBusesVoltages(element.get());
552 auto data = currentBus->GetElectricalData();
553 data.nominalVoltage = oldBus.GetElectricalData().nominalVoltage;
554 data.nominalVoltageUnit = oldBus.GetElectricalData().nominalVoltageUnit;
555 currentBus->SetElectricalData(data);
561 ValidateElementsVoltages();
569 else if (element->SwitchesContains(m_camera->ScreenToWorld(event.GetPosition()))) {
570 element->SetOnline(element->IsOnline() ?
false : true);
571 clickOnSwitch =
true;
576 for (
auto& text : m_textList) {
577 if (text->
Contains(m_camera->ScreenToWorld(event.GetPosition()))) {
578 if (text->ShowForm(dialogParent, GetElementList())) SaveCurrentState();
580 if (m_workspacePanel) m_workspacePanel->SetFocus();
586 UpdateTextElements();
587 if (m_continuousCalc) RunStaticStudies();
589 if (clickOnSwitch && m_continuousCalc) RunStaticStudies();
591 if (redraw) Redraw();
595void Workspace::OnRightClickDown(wxMouseEvent& event)
598 if (m_mode == WorkspaceMode::MODE_EDIT) {
599 for (
auto& element : m_elementList) {
600 if (element->IsSelected()) {
602 if (element->Contains(m_camera->ScreenToWorld(event.GetPosition()))) {
603 element->ShowPickbox(
false);
605 menu.SetClientData(element.get());
606 if (element->GetContextMenu(menu)) {
608 menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &Workspace::OnPopupClick,
this);
611 if (m_workspacePanel) m_workspacePanel->SetFocus();
614 if (!menu.GetClientData())
break;
616 element->ResetPickboxes();
621 if (redraw) Redraw();
625void Workspace::OnLeftClickUp(wxMouseEvent& event)
629 bool foundPickbox =
false;
630 bool findNewParent =
false;
631 bool updateVoltages =
false;
632 bool saveCurrentState =
false;
633 auto itnp = m_elementList.begin();
635 for (
auto it = m_elementList.begin(); it != m_elementList.end(); ++it) {
638 if (m_mode == WorkspaceMode::MODE_MOVE_PICKBOX) {
640 if (element->IsPickboxShown()) {
641 saveCurrentState =
true;
643 if (
auto bus =
dynamic_cast<Bus*
>(element.get())) {
645 for (
auto child : m_elementList) {
646 for (
auto parent : child->GetParentList()) {
650 m_disconnectedElement =
true;
669 if (m_mode == WorkspaceMode::MODE_SELECTION_RECT) {
670 if (element->Intersects(m_selectionRect)) {
671 element->SetSelected();
673 for (
auto& text : m_textList) {
674 if (text->GetElement() == element.get()) {
676 text->SetAltSelectionColour(
false);
677 text->SetAllowRotation();
685 else if (m_mode == WorkspaceMode::MODE_MOVE_NODE) {
686 if (element->IsSelected()) {
687 saveCurrentState =
true;
688 for (
auto parent : m_elementList) {
689 if (
auto bus =
dynamic_cast<Bus*
>(parent.get())) {
690 if (element->SetNodeParent(bus)) {
691 parent->AddChild(element.get());
692 findNewParent =
true;
694 element->ResetNodes();
708 if (element->PickboxContains(m_camera->ScreenToWorld(event.GetPosition()))) {
712 element->ShowPickbox(
false);
713 element->ResetPickboxes();
719 for (
auto& text : m_textList) {
720 if (m_mode == WorkspaceMode::MODE_SELECTION_RECT) {
723 text->SetAltSelectionColour(
false);
724 text->SetAllowRotation();
734 if (m_mode == WorkspaceMode::MODE_MOVE_ELEMENT) saveCurrentState =
true;
737 std::rotate(itnp, itnp + 1, m_elementList.end());
738 updateVoltages =
true;
740 if (!foundPickbox) { SetCursor(wxCURSOR_ARROW); }
742 if (m_mode != WorkspaceMode::MODE_INSERT) { m_mode = WorkspaceMode::MODE_EDIT; }
744 if (updateVoltages) { ValidateElementsVoltages(); }
746 if (saveCurrentState) SaveCurrentState();
748 if (m_continuousCalc && m_disconnectedElement) {
749 m_disconnectedElement =
false;
753 m_selectionRect = wxRect2DDouble(0, 0, 0, 0);
755 if (m_hmPlane && m_showHM) {
756 m_showHMTimer =
true;
757 m_timerHeatMap->Start();
764void Workspace::OnMouseMotion(wxMouseEvent& event)
768 case WorkspaceMode::MODE_INSERT: {
769 auto& newElement = m_elementList.back();
770 newElement->
SetPosition(m_camera->ScreenToWorld(event.GetPosition()));
774 case WorkspaceMode::MODE_INSERT_TEXT: {
775 auto& newText = m_textList.back();
776 newText->SetPosition(m_camera->ScreenToWorld(event.GetPosition()));
780 case WorkspaceMode::MODE_DRAG:
781 case WorkspaceMode::MODE_DRAG_INSERT:
782 case WorkspaceMode::MODE_DRAG_INSERT_TEXT:
783 case WorkspaceMode::MODE_DRAG_PASTE: {
784 m_camera->SetTranslation(event.GetPosition());
788 case WorkspaceMode::MODE_EDIT: {
789 bool foundPickbox =
false;
790 for (
auto& element : m_elementList) {
791 if (element->IsSelected()) {
793 if (element->Contains(m_camera->ScreenToWorld(event.GetPosition()))) {
794 element->ShowPickbox();
798 if (element->PickboxContains(m_camera->ScreenToWorld(event.GetPosition()))) {
801 const DWORD beforeCursor = GetWorkspaceGDIObjects();
803 SetCursor(element->GetBestPickboxCursor());
805 LogWorkspaceGDIDelta(wxString::Format(
"Workspace::OnMouseMotion pickbox cursor type %d", element->GetElementType()), beforeCursor);
808 else if (!foundPickbox) {
810 const DWORD beforeCursor = GetWorkspaceGDIObjects();
812 SetCursor(wxCURSOR_ARROW);
814 LogWorkspaceGDIDelta(
"Workspace::OnMouseMotion arrow cursor", beforeCursor);
816 element->ResetPickboxes();
819 else if (!foundPickbox) {
820 if (element->IsPickboxShown()) redraw =
true;
822 element->ShowPickbox(
false);
823 element->ResetPickboxes();
825 const DWORD beforeCursor = GetWorkspaceGDIObjects();
827 SetCursor(wxCURSOR_ARROW);
829 LogWorkspaceGDIDelta(
"Workspace::OnMouseMotion arrow cursor", beforeCursor);
836 case WorkspaceMode::MODE_MOVE_NODE: {
837 for (
auto& element : m_elementList) {
838 if (element->IsSelected()) {
839 element->MoveNode(
nullptr, m_camera->ScreenToWorld(event.GetPosition()));
845 case WorkspaceMode::MODE_MOVE_PICKBOX: {
846 for (
auto& element : m_elementList) {
847 if (element->IsSelected()) {
848 element->MovePickbox(m_camera->ScreenToWorld(event.GetPosition()));
852 if (m_hmPlane && m_showHM) {
857 case WorkspaceMode::MODE_MOVE_ELEMENT:
858 case WorkspaceMode::MODE_PASTE: {
859 for (
auto it = m_elementList.begin(), itEnd = m_elementList.end(); it != itEnd; ++it) {
861 if (element->IsSelected()) {
862 element->Move(m_camera->ScreenToWorld(event.GetPosition()));
864 std::vector<Element*> childList = element->GetChildList();
865 for (
auto it = childList.begin(), itEnd = childList.end(); it != itEnd; ++it) {
866 (*it)->MoveNode(element.get(), m_camera->ScreenToWorld(event.GetPosition()));
872 for (
auto& text : m_textList) {
874 text->
Move(m_camera->ScreenToWorld(event.GetPosition()));
878 if (m_hmPlane && m_showHM) {
883 case WorkspaceMode::MODE_SELECTION_RECT: {
884 wxPoint2DDouble currentPos = m_camera->ScreenToWorld(event.GetPosition());
886 if (currentPos.m_x < m_startSelRect.m_x) {
888 w = m_startSelRect.m_x - currentPos.m_x;
891 x = m_startSelRect.m_x;
892 w = currentPos.m_x - m_startSelRect.m_x;
894 if (currentPos.m_y < m_startSelRect.m_y) {
896 h = m_startSelRect.m_y - currentPos.m_y;
899 y = m_startSelRect.m_y;
900 h = currentPos.m_y - m_startSelRect.m_y;
903 m_selectionRect = wxRect2DDouble(x, y, w, h);
911 m_camera->UpdateMousePosition(event.GetPosition());
917void Workspace::OnMiddleDown(wxMouseEvent& event)
921 case WorkspaceMode::MODE_INSERT: {
922 m_mode = WorkspaceMode::MODE_DRAG_INSERT;
924 case WorkspaceMode::MODE_INSERT_TEXT: {
925 m_mode = WorkspaceMode::MODE_DRAG_INSERT_TEXT;
927 case WorkspaceMode::MODE_PASTE: {
928 m_mode = WorkspaceMode::MODE_DRAG_PASTE;
931 m_mode = WorkspaceMode::MODE_DRAG;
934 m_camera->StartTranslation(m_camera->ScreenToWorld(event.GetPosition()));
937 if (m_hmPlane && m_showHM) {
944void Workspace::OnMiddleUp(wxMouseEvent& event)
947 case WorkspaceMode::MODE_DRAG_INSERT: {
948 m_mode = WorkspaceMode::MODE_INSERT;
950 case WorkspaceMode::MODE_DRAG_INSERT_TEXT: {
951 m_mode = WorkspaceMode::MODE_INSERT_TEXT;
953 case WorkspaceMode::MODE_DRAG_PASTE: {
954 m_mode = WorkspaceMode::MODE_PASTE;
956 case WorkspaceMode::MODE_INSERT:
957 case WorkspaceMode::MODE_INSERT_TEXT:
958 case WorkspaceMode::MODE_PASTE: {
962 m_mode = WorkspaceMode::MODE_EDIT;
967 if (m_hmPlane && m_showHM) {
975void Workspace::OnScroll(wxMouseEvent& event)
977 if (event.GetWheelRotation() > 0)
978 m_camera->SetScale(event.GetPosition(), +0.05);
980 m_camera->SetScale(event.GetPosition(), -0.05);
982 if (m_hmPlane && m_showHM) {
984 m_showHMTimer =
true;
985 m_timerHeatMap->Start();
992void Workspace::OnKeyDown(wxKeyEvent& event)
994 bool insertingElement =
false;
995 if (m_mode == WorkspaceMode::MODE_INSERT || m_mode == WorkspaceMode::MODE_INSERT_TEXT) insertingElement =
true;
997 char key =
event.GetUnicodeKey();
998 if (key != WXK_NONE) {
1002 if (m_mode == WorkspaceMode::MODE_INSERT) {
1004 auto elementToDelete = m_elementList.back();
1007 for (
auto& element : m_elementList) {
1008 element->RemoveChild(elementToDelete.get());
1011 m_elementList.pop_back();
1012 m_mode = WorkspaceMode::MODE_EDIT;
1015 else if (m_mode == WorkspaceMode::MODE_INSERT_TEXT) {
1016 m_textList.pop_back();
1017 m_mode = WorkspaceMode::MODE_EDIT;
1023 DeleteSelectedElements();
1026 if (!insertingElement) {
1028 auto newText = std::make_shared<Text>(
1029 m_camera->ScreenToWorld(event.GetPosition()),
1030 m_properties->GetGeneralPropertiesData().labelFont,
1031 m_properties->GetGeneralPropertiesData().labelFontSize);
1032 m_textList.push_back(newText);
1033 m_mode = WorkspaceMode::MODE_INSERT_TEXT;
1034 m_statusBar->SetStatusText(_(
"Insert Text: Click to insert, ESC to cancel."));
1035 if (m_hmPlane && m_showHM) {
1042 if (event.GetModifiers() == wxMOD_SHIFT) { Fit(); }
1046 RotateSelectedElements(event.GetModifiers() != wxMOD_SHIFT);
1050 if (!insertingElement) {
1051 auto newBus = std::make_shared<Bus>(m_camera->ScreenToWorld(event.GetPosition()),
1052 wxString::Format(_(
"Bus %d"), GetElementNumber(ID_BUS)));
1053 IncrementElementNumber(ID_BUS);
1054 m_elementList.push_back(newBus);
1055 m_mode = WorkspaceMode::MODE_INSERT;
1056 m_statusBar->SetStatusText(_(
"Insert Bus: Click to insert, ESC to cancel."));
1057 if (m_hmPlane && m_showHM) {
1064 if (!insertingElement) {
1065 if (!event.ControlDown() && event.ShiftDown()) {
1066 auto newLoad = std::make_shared<Load>(wxString::Format(_(
"Load %d"), GetElementNumber(ID_LOAD)));
1067 IncrementElementNumber(ID_LOAD);
1068 m_elementList.push_back(newLoad);
1069 m_mode = WorkspaceMode::MODE_INSERT;
1070 m_statusBar->SetStatusText(_(
"Insert Load: Click on a bus, ESC to cancel."));
1072 else if (!event.ControlDown() && !event.ShiftDown()) {
1073 auto newLine = std::make_shared<Line>(wxString::Format(_(
"Line %d"), GetElementNumber(ID_LINE)));
1074 IncrementElementNumber(ID_LINE);
1075 m_elementList.push_back(newLine);
1076 m_mode = WorkspaceMode::MODE_INSERT;
1077 m_statusBar->SetStatusText(_(
"Insert Line: Click on two buses, ESC to cancel."));
1079 if (m_hmPlane && m_showHM) {
1088 if (!insertingElement) {
1089 auto newTransformer = std::make_shared<Transformer>(wxString::Format(_(
"Transformer %d"), GetElementNumber(ID_TRANSFORMER)));
1090 IncrementElementNumber(ID_TRANSFORMER);
1091 m_elementList.push_back(newTransformer);
1092 m_mode = WorkspaceMode::MODE_INSERT;
1093 m_statusBar->SetStatusText(_(
"Insert Transformer: Click on two buses, ESC to cancel."));
1094 if (m_hmPlane && m_showHM) {
1102 if (!insertingElement) {
1103 auto newGenerator = std::make_shared<SyncGenerator>(wxString::Format(_(
"Generator %d"), GetElementNumber(ID_SYNCGENERATOR)));
1104 IncrementElementNumber(ID_SYNCGENERATOR);
1105 m_elementList.push_back(newGenerator);
1106 m_mode = WorkspaceMode::MODE_INSERT;
1107 m_statusBar->SetStatusText(_(
"Insert Generator: Click on a bus, ESC to cancel."));
1108 if (m_hmPlane && m_showHM) {
1115 if (!insertingElement) {
1116 if (event.GetModifiers() == wxMOD_SHIFT) {
1117 auto newInductor = std::make_shared<Inductor>(wxString::Format(_(
"Inductor %d"), GetElementNumber(ID_INDUCTOR)));
1118 IncrementElementNumber(ID_INDUCTOR);
1119 m_elementList.push_back(newInductor);
1120 m_mode = WorkspaceMode::MODE_INSERT;
1121 m_statusBar->SetStatusText(_(
"Insert Inductor: Click on a bus, ESC to cancel."));
1125 auto newIndMotor = std::make_shared<IndMotor>(wxString::Format(_(
"Induction motor %d"), GetElementNumber(ID_INDMOTOR)));
1126 IncrementElementNumber(ID_INDMOTOR);
1127 m_elementList.push_back(newIndMotor);
1128 m_mode = WorkspaceMode::MODE_INSERT;
1129 m_statusBar->SetStatusText(_(
"Insert Induction Motor: Click on a bus, ESC to cancel."));
1131 if (m_hmPlane && m_showHM) {
1139 if (!insertingElement) {
1140 auto newSyncCondenser = std::make_shared<SyncMotor>(wxString::Format(_(
"Synchronous condenser %d"), GetElementNumber(ID_SYNCMOTOR)));
1141 IncrementElementNumber(ID_SYNCMOTOR);
1142 m_elementList.push_back(newSyncCondenser);
1143 m_mode = WorkspaceMode::MODE_INSERT;
1144 m_statusBar->SetStatusText(_(
"Insert Synchronous Condenser: Click on a bus, ESC to cancel."));
1145 if (m_hmPlane && m_showHM) {
1152 if (!insertingElement) {
1153 if (event.GetModifiers() == wxMOD_SHIFT) {
1154 auto newCapacitor = std::make_shared<Capacitor>(wxString::Format(_(
"Capacitor %d"), GetElementNumber(ID_CAPACITOR)));
1155 IncrementElementNumber(ID_CAPACITOR);
1156 m_elementList.push_back(newCapacitor);
1157 m_mode = WorkspaceMode::MODE_INSERT;
1158 m_statusBar->SetStatusText(_(
"Insert Capacitor: Click on a bus, ESC to cancel."));
1159 if (m_hmPlane && m_showHM) {
1164 else if (event.GetModifiers() == wxMOD_CONTROL) {
1170 if (!insertingElement) {
1171 if (event.ShiftDown() && event.ControlDown()) {
1181 else if (event.GetModifiers() == wxMOD_SHIFT) {
1182 auto newHarmCurrent = std::make_shared<HarmCurrent>(
1183 wxString::Format(_(
"Harmonic Current %d"), GetElementNumber(ID_HARMCURRENT)));
1184 IncrementElementNumber(ID_HARMCURRENT);
1185 m_elementList.push_back(newHarmCurrent);
1186 m_mode = WorkspaceMode::MODE_INSERT;
1187 m_statusBar->SetStatusText(
1188 _(
"Insert Harmonic Current Source: Click on a bus, ESC to cancel."));
1190 if (m_hmPlane && m_showHM) {
1197 if (!insertingElement) {
1198 if (event.GetModifiers() == wxMOD_CONTROL) { Paste(); }
1202 if (!insertingElement) {
1203 if (event.GetModifiers() == wxMOD_CONTROL) {
1207 if (GetSavedPath().IsOk()) {
1208 fileHandling.SaveProject(GetSavedPath());
1211 wxFileDialog saveFileDialog(
this, _(
"Save PSP file"),
"",
"",
"PSP files (*.psp)|*.psp",
1212 wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
1213 if (saveFileDialog.ShowModal() == wxID_CANCEL)
return;
1215 fileHandling.SaveProject(saveFileDialog.GetPath());
1216 wxFileName fileName(saveFileDialog.GetPath());
1217 SetName(fileName.GetName());
1218 if (m_auiNotebook) m_auiNotebook->SetPageText(m_auiNotebook->GetPageIndex(
this), GetName());
1219 SetSavedPath(fileName);
1225 if (!insertingElement) {
1226 if (event.ControlDown() && !event.ShiftDown()) { SetPreviousState(); }
1227 if (event.ControlDown() && event.ShiftDown()) { SetNextState(); }
1231 if (!insertingElement) {
1232 if (event.GetModifiers() == wxMOD_CONTROL) { SetNextState(); }
1236 if (!insertingElement) {
1237 if (event.GetModifiers() == wxMOD_SHIFT) {
1239 if (!insertingElement) {
1240 auto newEMTElement = std::make_shared<EMTElement>(wxString::Format(_(
"Electromagnetic Element %d"), GetElementNumber(ID_EMTELEMENT)));
1241 IncrementElementNumber(ID_EMTELEMENT);
1242 m_elementList.push_back(newEMTElement);
1243 m_mode = WorkspaceMode::MODE_INSERT;
1244 m_statusBar->SetStatusText(_(
"Insert Electromagnetic Transient Element: Click on a bus, ESC to cancel."));
1245 if (m_hmPlane && m_showHM) {
1254#ifdef SHOW_DEBUG_PANEL
1256 if (event.GetModifiers() == wxMOD_CONTROL) {
1258 m_debugFrame->SetFocus();
1273void Workspace::UpdateStatusBar()
1276 case WorkspaceMode::MODE_DRAG: {
1277 m_statusBar->SetStatusText(_(
"MODE: DRAG"), 1);
1280 case WorkspaceMode::MODE_PASTE:
1281 case WorkspaceMode::MODE_DRAG_PASTE: {
1282 m_statusBar->SetStatusText(_(
"MODE: PASTE"), 1);
1285 case WorkspaceMode::MODE_INSERT:
1286 case WorkspaceMode::MODE_INSERT_TEXT:
1287 case WorkspaceMode::MODE_DRAG_INSERT:
1288 case WorkspaceMode::MODE_DRAG_INSERT_TEXT: {
1289 m_statusBar->SetStatusText(_(
"MODE: INSERT"), 1);
1292 case WorkspaceMode::MODE_MOVE_ELEMENT:
1293 case WorkspaceMode::MODE_MOVE_PICKBOX:
1294 case WorkspaceMode::MODE_MOVE_NODE:
1295 case WorkspaceMode::MODE_SELECTION_RECT:
1296 case WorkspaceMode::MODE_EDIT: {
1297 if (m_oldStatusMode != m_mode)
1298 m_statusBar->SetStatusText(wxT(
""));
1299 m_statusBar->SetStatusText(_(
"MODE: EDIT"), 1);
1303 m_statusBar->SetStatusText(wxString::Format(_(
"ZOOM: %d%%"), (
int)(m_camera->GetScale() * 100.0)), 2);
1304 m_statusBar->SetStatusText(
1305 wxString::Format(wxT(
"X: %.1f Y: %.1f"), m_camera->GetMousePosition().m_x, m_camera->GetMousePosition().m_y),
1307 m_oldStatusMode = m_mode;
1310int Workspace::GetElementNumberFromList(
Element* element)
1313 for (
auto& elementFromList : m_elementList) {
1314 if (element->GetElementType() == elementFromList->GetElementType()) {
1315 if (element == elementFromList.get())
return elementNum;
1322void Workspace::GetStateListsCopy(
const std::vector< std::shared_ptr<PowerElement> >& elementsList,
1323 const std::vector< std::shared_ptr<Text> >& textList,
1324 std::vector< std::shared_ptr<PowerElement> >& elementsListCopy,
1325 std::vector< std::shared_ptr<Text> >& textListCopy)
1330 elementsListCopy.clear();
1331 textListCopy.clear();
1333 std::map<Element*, Element*> elementMap;
1335 for (
auto& element : elementsList) {
1337 const DWORD beforeElementCopy = GetWorkspaceGDIObjects();
1341 LogWorkspaceGDIDelta(wxString::Format(
"Workspace::GetStateListsCopy element copy type %d", element->GetElementType()), beforeElementCopy);
1343 elementsListCopy.emplace_back(copyElement);
1344 elementMap[element.get()] = copyElement;
1347 for (
auto& copyElement : elementsListCopy) {
1350 for (
Element* parent : copyElement->GetParentList()) {
1351 auto it = elementMap.find(parent);
1353 if (it != elementMap.end())
1354 copyElement->SetParent(it->second, i);
1360 for (
Element* child : copyElement->GetChildList()) {
1361 auto it = elementMap.find(child);
1363 if (it != elementMap.end())
1364 copyElement->SetChild(it->second, i);
1370 for (
const auto& text : textList) {
1372 const DWORD beforeTextCopy = GetWorkspaceGDIObjects();
1374 auto copyText =
static_cast<Text*
>(text->
GetCopy());
1376 LogWorkspaceGDIDelta(
"Workspace::GetStateListsCopy text copy", beforeTextCopy);
1379 auto it = elementMap.find(copyText->GetElement());
1381 if (it != elementMap.end())
1382 copyText->SetElement(it->second);
1384 copyText->SetElement(
nullptr);
1385 textListCopy.emplace_back(copyText);
1389void Workspace::UpdateHeatMap()
1391 if (m_hmPlane && m_showHM) {
1394 wxRect2DDouble screenRect(-100, -100, m_width + 200.0, m_height + 200.0);
1397 std::vector<Bus*> busList;
1398 float minVoltage, maxVoltage;
1399 if (m_hmAutomaticLabel) {
1404 minVoltage = m_hmPlane->GetMinLimit();
1405 maxVoltage = m_hmPlane->GetMaxLimit();
1408 for (
auto& element : m_elementList) {
1409 if (
Bus* bus =
dynamic_cast<Bus*
>(element.get())) {
1410 if (m_hmAutomaticLabel) {
1411 const float voltage = std::abs(bus->GetElectricalData().voltage);
1412 if (minVoltage > voltage) minVoltage = voltage;
1413 if (maxVoltage < voltage) maxVoltage = voltage;
1415 busList.push_back(bus);
1418 if (m_hmAutomaticLabel) {
1419 m_hmPlane->SetLabelLimits(minVoltage, maxVoltage);
1422 for (
Bus* bus : busList) {
1423 const float voltage = std::abs(bus->GetElectricalData().voltage);
1424 float depth = 2.0f * (voltage - (maxVoltage + minVoltage) / 2.0f) / (maxVoltage - minVoltage);
1425 if (depth < -1.0) depth = -1.0;
1426 if (depth > 1.0) depth = 1.0;
1428 wxRect2DDouble rect = bus->GetRect();
1429 rect = wxRect2DDouble(
1430 (rect.m_x - 100.0f) * m_camera->GetScale() + m_camera->GetTranslation().m_x * m_camera->GetScale(),
1431 (rect.m_y - 50.0f) * m_camera->GetScale() + m_camera->GetTranslation().m_y * m_camera->GetScale(),
1432 (rect.m_width + 200.0f) * m_camera->GetScale(),
1433 (rect.m_height + 100.0f) * m_camera->GetScale());
1435 if (screenRect.Contains(rect))
1436 m_hmPlane->SetRectSlope(rect, M_PI *
static_cast<float>(bus->GetAngle()) / 180.0f, depth);
1442 int iterations = std::lround(10 * std::pow(m_camera->GetScale(), 2));
1445 if (iterations < 1) iterations = 1;
1446 m_hmPlane->SmoothPlane(iterations);
1450void Workspace::OnPopupClick(wxCommandEvent& event)
1452 wxWindow* dialogParent = wxGetTopLevelParent(
this);
1453 if (!dialogParent) dialogParent =
this;
1456 bool redrawHM =
false;
1458 wxMenu* menu =
static_cast<wxMenu*
>(
event.GetEventObject());
1460 int eventID =
event.GetId();
1463 if (element->
ShowForm(dialogParent, element,
this)) {
1464 CheckSlackBusDuplication(element);
1465 UpdateTextElements();
1469 if (m_workspacePanel) m_workspacePanel->SetFocus();
1472 Line* line =
static_cast<Line*
>(element);
1473 line->AddNode(m_camera->GetMousePosition());
1478 Line* line =
static_cast<Line*
>(element);
1479 line->RemoveNode(m_camera->GetMousePosition());
1485 for (
auto& iElement : m_elementList) {
1487 for (
int i = 0; i < (int)iElement->GetParentList().size(); i++) {
1489 if (parent == element) { iElement->RotateNode(parent); }
1498 for (
auto& iElement : m_elementList) {
1500 for (
int i = 0; i < (int)iElement->GetParentList().size(); i++) {
1502 if (parent == element) { iElement->RotateNode(parent,
false); }
1549 std::vector<Element*> childList = element->
GetChildList();
1550 for (
auto child : childList) {
1557 std::vector<Element*> parentList = element->
GetParentList();
1558 for (
auto parent : parentList) {
1563 std::erase_if(m_textList, [&](
const auto& text) {
1564 return text->GetElement() == element;
1567 std::erase_if(m_elementList, [&](
const auto& delElement) {
1568 return delElement.get() == element;
1571 menu->SetClientData(
nullptr);
1575 if (InsertTextElement(eventID, element)) {
1576 UpdateTextElements();
1581 if (redrawHM && m_hmPlane && m_showHM) {
1583 m_showHMTimer =
true;
1584 m_timerHeatMap->Start();
1588void Workspace::RotateSelectedElements(
bool clockwise)
1590 bool saveCurrrentState =
false;
1591 for (
auto& element : m_elementList) {
1593 for (
int i = 0; i < (int)element->
GetParentList().size(); i++) {
1600 element->
StartMove(m_camera->GetMousePosition());
1605 saveCurrrentState =
true;
1606 element->
Rotate(clockwise);
1607 element->
StartMove(m_camera->GetMousePosition());
1612 for (
auto& text : m_textList) {
1614 saveCurrrentState =
true;
1616 text->
StartMove(m_camera->GetMousePosition());
1619 if (saveCurrrentState) SaveCurrentState();
1621 if (m_hmPlane && m_showHM) {
1623 m_showHMTimer =
true;
1624 m_timerHeatMap->Start();
1629void Workspace::DeleteSelectedElements()
1632 for (
auto it = m_elementList.begin(); it != m_elementList.end();) {
1637 std::vector<Element*> childList = element->
GetChildList();
1638 for (
auto itc = childList.begin(), itEnd = childList.end(); itc != itEnd; ++itc) {
1645 std::vector<Element*> parentList = element->
GetParentList();
1646 for (
auto itp = parentList.begin(), itEnd = parentList.end(); itp != itEnd; ++itp) {
1661 std::erase_if(m_textList, [&](
const auto& text) {
1662 return text->GetElement() == element;
1665 it = m_elementList.erase(it);
1682 std::erase_if(m_textList, [](
const auto& text) {
1686 if (m_hmPlane && m_showHM) {
1688 m_showHMTimer =
true;
1689 m_timerHeatMap->Start();
1695bool Workspace::GetElementsCorners(wxPoint2DDouble& leftUpCorner,
1696 wxPoint2DDouble& rightDownCorner,
1697 std::vector<Element*> elementList)
1699 if (elementList.size() == 0)
return false;
1701 elementList[0]->CalculateBoundaries(leftUpCorner, rightDownCorner);
1703 for (
auto it = elementList.begin() + 1, itEnd = elementList.end(); it != itEnd; it++) {
1705 wxPoint2DDouble leftUp;
1706 wxPoint2DDouble rightDown;
1708 if (leftUp.m_x < leftUpCorner.m_x) leftUpCorner.m_x = leftUp.m_x;
1709 if (leftUp.m_y < leftUpCorner.m_y) leftUpCorner.m_y = leftUp.m_y;
1710 if (rightDown.m_x > rightDownCorner.m_x) rightDownCorner.m_x = rightDown.m_x;
1711 if (rightDown.m_y > rightDownCorner.m_y) rightDownCorner.m_y = rightDown.m_y;
1716void Workspace::Fit()
1718 wxPoint2DDouble leftUpCorner(0, 0);
1719 wxPoint2DDouble rightDownCorner(0, 0);
1720 std::vector<Element*> elementList = GetElementList();
1721 for (
const auto& text : m_textList) { elementList.push_back(text.get()); }
1723 if (!GetElementsCorners(leftUpCorner, rightDownCorner, elementList))
return;
1724 wxPoint2DDouble middleCoords = (leftUpCorner + rightDownCorner) / 2.0;
1728 GetSize(&width, &height);
1730 double scaleX = double(width) / (rightDownCorner.m_x - leftUpCorner.m_x);
1731 double scaleY = double(height) / (rightDownCorner.m_y - leftUpCorner.m_y);
1733 double scale = scaleX < scaleY ? scaleX : scaleY;
1734 if (scale > m_camera->GetZoomMax()) scale = m_camera->GetZoomMax();
1735 if (scale < m_camera->GetZoomMin()) scale = m_camera->GetZoomMin();
1737 m_camera->SetScale(scale);
1739 m_camera->StartTranslation(middleCoords);
1740 m_camera->SetTranslation(wxPoint2DDouble(width / 2, height / 2));
1742 if (m_hmPlane && m_showHM) {
1749bool Workspace::InsertTextElement(
int textID,
Element* parentElement,
ElectricalUnit unit,
int precision)
1753 if (FindTextElement(parentElement, DATA_NAME))
return false;
1755 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(40, -30), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1756 newText->SetElement(parentElement);
1757 newText->SetDataType(DATA_NAME);
1758 newText->SetElementTypeText(parentElement->GetElementType());
1759 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1761 m_textList.push_back(newText);
1763 case ID_TXT_VOLTAGE: {
1764 if (FindTextElement(parentElement, DATA_VOLTAGE))
return false;
1766 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(40, 15), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1767 newText->SetElement(parentElement);
1768 newText->SetDataType(DATA_VOLTAGE);
1769 if (unit == ElectricalUnit::UNIT_NONE)
1772 newText->SetUnit(unit);
1773 newText->SetDecimalPlaces(precision);
1774 newText->SetElementTypeText(parentElement->GetElementType());
1775 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1777 m_textList.emplace_back(newText);
1779 case ID_TXT_ANGLE: {
1780 if (FindTextElement(parentElement, DATA_ANGLE))
return false;
1782 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(40, 30), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1783 newText->SetElement(parentElement);
1784 newText->SetDataType(DATA_ANGLE);
1785 if (unit == ElectricalUnit::UNIT_NONE)
1788 newText->SetUnit(unit);
1789 newText->SetDecimalPlaces(precision);
1790 newText->SetElementTypeText(parentElement->GetElementType());
1791 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1793 m_textList.emplace_back(newText);
1795 case ID_TXT_FAULTCURRENT: {
1796 if (FindTextElement(parentElement, DATA_SC_CURRENT))
return false;
1798 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(-70, 30), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1799 newText->SetElement(parentElement);
1800 newText->SetDataType(DATA_SC_CURRENT);
1801 if (unit == ElectricalUnit::UNIT_NONE)
1804 newText->SetUnit(unit);
1805 newText->SetDecimalPlaces(precision);
1806 newText->SetElementTypeText(parentElement->GetElementType());
1807 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1809 m_textList.emplace_back(newText);
1811 case ID_TXT_FAULTVOLTAGE: {
1812 if (FindTextElement(parentElement, DATA_SC_VOLTAGE))
return false;
1814 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(-70, 75), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1815 newText->SetElement(parentElement);
1816 newText->SetDataType(DATA_SC_VOLTAGE);
1817 if (unit == ElectricalUnit::UNIT_NONE)
1820 newText->SetUnit(unit);
1821 newText->SetDecimalPlaces(precision);
1822 newText->SetElementTypeText(parentElement->GetElementType());
1823 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1825 m_textList.emplace_back(newText);
1828 if (FindTextElement(parentElement, DATA_SC_POWER))
return false;
1829 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(-50, -30), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1830 newText->SetElement(parentElement);
1831 newText->SetDataType(DATA_SC_POWER);
1833 if (unit == ElectricalUnit::UNIT_NONE)
1836 newText->SetUnit(unit);
1837 newText->SetDecimalPlaces(precision);
1838 newText->SetElementTypeText(parentElement->GetElementType());
1839 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1841 m_textList.emplace_back(newText);
1844 if (FindTextElement(parentElement, DATA_PQ_THD))
return false;
1845 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(-50, -15), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1846 newText->SetElement(parentElement);
1847 newText->SetDataType(DATA_PQ_THD);
1848 newText->SetDecimalPlaces(precision);
1849 newText->SetElementTypeText(parentElement->GetElementType());
1850 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1852 m_textList.emplace_back(newText);
1854 case ID_TXT_ACTIVE_POWER: {
1855 if (FindTextElement(parentElement, DATA_ACTIVE_POWER))
return false;
1856 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(0, 35), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1857 newText->SetElement(parentElement);
1858 newText->SetDataType(DATA_ACTIVE_POWER);
1859 if (unit == ElectricalUnit::UNIT_NONE)
1862 newText->SetUnit(unit);
1863 newText->SetDecimalPlaces(precision);
1864 newText->SetElementTypeText(parentElement->GetElementType());
1865 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1867 m_textList.emplace_back(newText);
1869 case ID_TXT_REACTIVE_POWER: {
1870 if (FindTextElement(parentElement, DATA_REACTIVE_POWER))
return false;
1871 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(0, 50), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1872 newText->SetElement(parentElement);
1873 newText->SetDataType(DATA_REACTIVE_POWER);
1874 if (unit == ElectricalUnit::UNIT_NONE)
1877 newText->SetUnit(unit);
1878 newText->SetDecimalPlaces(precision);
1879 newText->SetElementTypeText(parentElement->GetElementType());
1880 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1882 m_textList.emplace_back(newText);
1884 case ID_TXT_BRANCH_ACTIVE_POWER_1_2:
1885 case ID_TXT_BRANCH_ACTIVE_POWER_2_1: {
1886 if (FindTextElement(parentElement, DATA_PF_ACTIVE))
return false;
1887 wxPoint2DDouble position(0.0, -10.0);
1888 if (textID == ID_TXT_BRANCH_ACTIVE_POWER_1_2)
1893 auto newText = std::make_shared<Text>(position, m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1894 newText->SetElement(parentElement);
1895 newText->SetDataType(DATA_PF_ACTIVE);
1896 if (unit == ElectricalUnit::UNIT_NONE)
1899 newText->SetUnit(unit);
1900 newText->SetDecimalPlaces(precision);
1901 newText->SetElementTypeText(parentElement->GetElementType());
1902 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1903 if (textID == ID_TXT_BRANCH_ACTIVE_POWER_2_1)
1904 newText->SetDirection(1);
1906 m_textList.emplace_back(newText);
1908 case ID_TXT_BRANCH_REACTIVE_POWER_1_2:
1909 case ID_TXT_BRANCH_REACTIVE_POWER_2_1: {
1910 if (FindTextElement(parentElement, DATA_PF_REACTIVE))
return false;
1911 wxPoint2DDouble position(0.0, 10.0);
1912 if (textID == ID_TXT_BRANCH_REACTIVE_POWER_1_2)
1917 auto newText = std::make_shared<Text>(position, m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1918 newText->SetElement(parentElement);
1919 newText->SetDataType(DATA_PF_REACTIVE);
1920 if (unit == ElectricalUnit::UNIT_NONE)
1923 newText->SetUnit(unit);
1924 newText->SetDecimalPlaces(precision);
1925 newText->SetElementTypeText(parentElement->GetElementType());
1926 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1927 if (textID == ID_TXT_BRANCH_REACTIVE_POWER_2_1)
1928 newText->SetDirection(1);
1930 m_textList.emplace_back(newText);
1932 case ID_TXT_BRANCH_LOSSES: {
1933 if (FindTextElement(parentElement, DATA_PF_LOSSES))
return false;
1935 auto newText = std::make_shared<Text>(position, m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1936 newText->SetElement(parentElement);
1937 newText->SetDataType(DATA_PF_LOSSES);
1938 if (unit == ElectricalUnit::UNIT_NONE)
1941 newText->SetUnit(unit);
1942 newText->SetDecimalPlaces(precision);
1943 newText->SetElementTypeText(parentElement->GetElementType());
1944 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1946 m_textList.emplace_back(newText);
1948 case ID_TXT_BRANCH_CURRENT_1_2:
1949 case ID_TXT_BRANCH_CURRENT_2_1: {
1950 if (FindTextElement(parentElement, DATA_PF_CURRENT))
return false;
1951 wxPoint2DDouble position(0.0, 10.0);
1952 if (textID == ID_TXT_BRANCH_CURRENT_1_2)
1957 auto newText = std::make_shared<Text>(position, m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1958 newText->SetElement(parentElement);
1959 newText->SetDataType(DATA_PF_CURRENT);
1960 if (unit == ElectricalUnit::UNIT_NONE)
1963 newText->SetUnit(unit);
1964 newText->SetDecimalPlaces(precision);
1965 newText->SetElementTypeText(parentElement->GetElementType());
1966 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1967 if (textID == ID_TXT_BRANCH_CURRENT_2_1)
1968 newText->SetDirection(1);
1970 m_textList.emplace_back(newText);
1972 case ID_TXT_BRANCH_FAULT_CURRENT_1_2:
1973 case ID_TXT_BRANCH_FAULT_CURRENT_2_1: {
1974 if (FindTextElement(parentElement, DATA_SC_CURRENT))
return false;
1975 wxPoint2DDouble position(0.0, 25.0);
1976 if (textID == ID_TXT_BRANCH_FAULT_CURRENT_1_2)
1981 auto newText = std::make_shared<Text>(position, m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1982 newText->SetElement(parentElement);
1983 newText->SetDataType(DATA_SC_CURRENT);
1984 if (unit == ElectricalUnit::UNIT_NONE)
1987 newText->SetUnit(unit);
1988 newText->SetDecimalPlaces(precision);
1989 newText->SetElementTypeText(parentElement->GetElementType());
1990 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1991 if (textID == ID_TXT_BRANCH_FAULT_CURRENT_2_1)
1992 newText->SetDirection(1);
1994 m_textList.emplace_back(newText);
2003Element* Workspace::FindTextElement(
Element* parentElement,
int dataType)
2005 for (
auto& text : m_textList) {
2006 if (text->GetElement() == parentElement && text->GetDataType() == dataType)
2012void Workspace::RemoveAllTextElements()
2021void Workspace::CheckSlackBusDuplication(
Element* newSlackBus)
2023 Bus* newBus =
dynamic_cast<Bus*
>(newSlackBus);
2025 if (!newBus->GetElectricalData().slackBus)
return;
2027 for (
auto& element : m_elementList) {
2028 Bus* bus =
dynamic_cast<Bus*
>(element.get());
2030 if (bus->GetElectricalData().slackBus && bus != newSlackBus) {
2031 wxMessageDialog msgDialog(
this,
2032 wxString::Format(_(
"The system already has %s as the slack bus.\nDo you want to set %s as the new slack bus?"), bus->GetElectricalData().name, newBus->GetElectricalData().name),
2033 _(
"Warning"), wxYES_NO | wxCENTRE | wxICON_WARNING);
2034 if (msgDialog.ShowModal() == wxID_YES) {
2035 auto data = bus->GetElectricalData();
2036 data.slackBus =
false;
2037 bus->SetElectricalData(data);
2041 auto data = newBus->GetElectricalData();
2042 data.slackBus =
false;
2043 newBus->SetElectricalData(data);
2051void Workspace::ValidateBusesVoltages(
Element* initialBus)
2053 double nominalVoltage =
static_cast<Bus*
>(initialBus)->GetElectricalData().nominalVoltage;
2054 ElectricalUnit nominalVoltageUnit =
static_cast<Bus*
>(initialBus)->GetElectricalData().nominalVoltageUnit;
2056 for (
auto it = m_elementList.begin(); it != m_elementList.end(); it++) {
2059 if (
auto line =
dynamic_cast<Line*
>(child)) {
2064 if (data1.nominalVoltage != data2.nominalVoltage ||
2065 data1.nominalVoltageUnit != data2.nominalVoltageUnit) {
2066 data1.nominalVoltage = nominalVoltage;
2067 data2.nominalVoltage = nominalVoltage;
2068 data1.nominalVoltageUnit = nominalVoltageUnit;
2069 data2.nominalVoltageUnit = nominalVoltageUnit;
2074 it = m_elementList.begin();
2083void Workspace::ValidateElementsVoltages()
2085 for (
auto& child : m_elementList) {
2086 std::vector<double> nominalVoltage;
2087 std::vector<ElectricalUnit> nominalVoltageUnit;
2088 for (
int i = 0; i < (int)child->
GetParentList().size(); i++) {
2091 nominalVoltage.push_back(parent->GetElectricalData().nominalVoltage);
2092 nominalVoltageUnit.push_back(parent->GetElectricalData().nominalVoltageUnit);
2095 child->SetNominalVoltage(nominalVoltage, nominalVoltageUnit);
2099void Workspace::ResetAllVoltages()
2103 UpdateTextElements();
2107bool Workspace::RunPowerFlow(
bool resetVoltages,
bool showBusyInfo)
2109 auto simProp = m_properties->GetSimulationPropertiesData();
2110 double basePower = simProp.basePower;
2117 for (
auto& element : m_elementList) {
2118 if (
auto emtElement =
dynamic_cast<EMTElement*
>(element.get())) {
2119 if (emtElement->IsOnline()) {
2120 emtElement->UpdateData(m_properties,
true);
2124 bool result =
false;
2125 wxString errorMsg =
"";
2130 wxBusyInfo* info =
nullptr;
2132 info =
new wxBusyInfo(
2135 .Icon(wxIcon(Paths::GetDataPath() +
"/images/ribbon/powerFLow32.png", wxBITMAP_TYPE_PNG))
2136 .Title(_(
"<b>Calculating Power Flow</b>"))
2137 .
Text(_(
"Please wait..."))
2138 .Foreground(*wxWHITE)
2139 .Background(*wxBLACK)
2140 .Transparency(4 * wxALPHA_OPAQUE / 5)
2155 if (resetVoltages) pf.ResetVoltages();
2157 switch (simProp.powerFlowMethod) {
2158 case GAUSS_SEIDEL: {
2159 result = pf.RunGaussSeidel(basePower, simProp.powerFlowMaxIterations, simProp.powerFlowTolerance,
2160 simProp.initAngle, simProp.accFator);
2162 case NEWTON_RAPHSON: {
2163 result = pf.RunNewtonRaphson(basePower, simProp.powerFlowMaxIterations, simProp.powerFlowTolerance,
2164 simProp.initAngle, simProp.newtonInertia);
2166 case GAUSS_NEWTON: {
2168 pf.RunGaussNewton(basePower, simProp.powerFlowMaxIterations, simProp.powerFlowTolerance,
2169 simProp.initAngle, simProp.accFator, simProp.gaussTolerance, simProp.newtonInertia);
2173 errorMsg = pf.GetErrorMessage();
2174 numIt = pf.GetIterations();
2176 if (showBusyInfo)
delete info;
2181 wxMessageDialog msgDialog(
this, errorMsg, _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
2182 msgDialog.ShowModal();
2185 m_statusBar->SetStatusText(
2186 wxString::Format(_(
"Power flow converge with %d iterations (%ld ms)"), numIt, sw.Time()));
2189 UpdateTextElements();
2196bool Workspace::UpdateTextElements()
2198 bool isTexturesOK =
true;
2199 double basePower = m_properties->GetSimulationPropertiesData().basePower;
2204 for (
auto& text : m_textList) {
2205 text->SetFontName(m_properties->GetGeneralPropertiesData().labelFont);
2206 text->SetFontSize(m_properties->GetGeneralPropertiesData().labelFontSize);
2207 text->UpdateText(basePower);
2210 return isTexturesOK;
2213void Workspace::CopySelection()
2216 std::vector<Element*> selectedElements;
2219 for (
auto& element : m_elementList) {
2220 if (
auto bus =
dynamic_cast<Bus*
>(element.get())) {
2221 auto data = bus->GetElectricalData();
2222 data.number = busNumber;
2223 bus->SetElectricalData(data);
2226 if (element->
IsSelected()) { selectedElements.push_back(element.get()); }
2228 for (
auto& text : m_textList) {
2229 if (text->
IsSelected()) { selectedElements.push_back(text.get()); }
2232 if (wxTheClipboard->Open()) {
2233 wxTheClipboard->SetData(dataObject);
2234 wxTheClipboard->Close();
2238bool Workspace::Paste()
2240 if (wxTheClipboard->Open()) {
2243 if (wxTheClipboard->IsSupported(dataObject.GetFormat())) {
2244 if (!wxTheClipboard->GetData(dataObject)) {
2245 wxMessageDialog dialog(
this, _(
"It was not possible to paste from clipboard."), _(
"Error"),
2246 wxOK | wxCENTER | wxICON_ERROR, wxDefaultPosition);
2248 wxTheClipboard->Close();
2253 wxTheClipboard->Close();
2256 wxTheClipboard->Close();
2260 std::vector<Element*> pastedElements;
2261 ElementsLists* elementsLists = dataObject.GetElementsLists();
2264 auto parentList = elementsLists->parentList;
2265 std::vector<Bus*> pastedBusList;
2266 for (
auto it = parentList.begin(), itEnd = parentList.end(); it != itEnd; ++it) {
2269 pastedElements.push_back(copy);
2270 pastedBusList.push_back(
static_cast<Bus*
>(copy));
2271 m_elementList.emplace_back(
static_cast<PowerElement*
>(copy));
2276 auto elementLists = elementsLists->elementList;
2277 for (
auto it = elementLists.begin(), itEnd = elementLists.end(); it != itEnd; ++it) {
2281 if (
Text* text =
dynamic_cast<Text*
>(copy)) {
2283 bool elementExist =
false;
2284 for (
auto& element : m_elementList) {
2285 if (text->GetElement() == element.get()) {
2286 elementExist =
true;
2291 pastedElements.push_back(copy);
2292 m_textList.emplace_back(text);
2299 if (currentParent) {
2300 int parentID = currentParent->
GetID();
2301 bool parentCopied =
false;
2302 for (
size_t k = 0; k < pastedBusList.size(); k++) {
2303 Bus* newParent = pastedBusList[k];
2304 if (parentID == newParent->
GetID()) {
2305 parentCopied =
true;
2314 pastedElements.push_back(copy);
2315 m_elementList.emplace_back(
static_cast<PowerElement*
>(copy));
2321 for (
auto it = pastedBusList.begin(), itEnd = pastedBusList.end(); it != itEnd; ++it) {
2324 for (
auto it = childList.begin(), itEnd = childList.end(); it != itEnd; ++it) {
2326 int childID = currentChild->
GetID();
2327 bool childCopied =
false;
2328 for (
int i = 0; i < (int)pastedElements.size(); i++) {
2329 Element* newChild = pastedElements[i];
2330 if (childID == newChild->
GetID()) {
2342 wxPoint2DDouble leftUpCorner, rightDownCorner;
2343 GetElementsCorners(leftUpCorner, rightDownCorner, pastedElements);
2344 wxPoint2DDouble startPosition = (leftUpCorner + rightDownCorner) / 2.0;
2345 for (
auto it = pastedElements.begin(), itEnd = pastedElements.end(); it != itEnd; ++it) {
2348 element->
Move(m_camera->GetMousePosition());
2349 for (
int i = 0; i < (int)element->
GetParentList().size(); i++) {
2351 element->
MoveNode(parent, m_camera->GetMousePosition());
2356 wxMessageDialog dialog(
this, _(
"It was not possible to paste from clipboard."), _(
"Error"),
2357 wxOK | wxCENTER | wxICON_ERROR, wxDefaultPosition);
2363 m_mode = WorkspaceMode::MODE_PASTE;
2364 m_statusBar->SetStatusText(_(
"Click to paste."));
2370void Workspace::SaveCurrentState()
2374 const DWORD beforeSaveState = GetWorkspaceGDIObjects();
2377 std::vector< std::shared_ptr<PowerElement> > currentStateElementList;
2378 std::vector< std::shared_ptr<Text> > currentStateTextList;
2380 GetStateListsCopy(m_elementList, m_textList, currentStateElementList, currentStateTextList);
2382 LogWorkspaceGDIDelta(
"Workspace::SaveCurrentState after copy", beforeSaveState);
2393 m_elementListState.resize(m_currenteState + 1);
2402 m_textListState.resize(m_currenteState + 1);
2405 if (m_currenteState >= m_maxStates) {
2406 m_currenteState = m_maxStates - 1;
2419 m_elementListState.erase(m_elementListState.begin());
2420 m_textListState.erase(m_textListState.begin());
2423 m_elementListState.emplace_back(currentStateElementList);
2424 m_textListState.emplace_back(currentStateTextList);
2426 LogWorkspaceGDIDelta(wxString::Format(
"Workspace::SaveCurrentState stored states=%zu elements=%zu texts=%zu",
2427 m_elementListState.size(),
2428 currentStateElementList.size(),
2429 currentStateTextList.size()),
2435 wxString pointerStr;
2436 pointerStr.Printf(
"[%d S saved s%d] ", m_currenteState, m_elementListState.size());
2438 for (
auto& element : currentStateElementList) {
2439 pointerStr.Printf(
"%p ", element.get());
2443 pointerStr.Printf(
"[%d S curr s%d] ", m_currenteState, m_elementListState.size());
2445 for (
auto& element : m_elementList) {
2446 pointerStr.Printf(
"%p ", element.get());
2454void Workspace::SetNextState()
2457 if (m_currenteState >= 0 &&
2458 static_cast<size_t>(m_currenteState) < m_elementListState.size() &&
2459 static_cast<size_t>(m_currenteState) < m_textListState.size()) {
2462 GetStateListsCopy(m_elementListState[m_currenteState], m_textListState[m_currenteState], m_elementList, m_textList);
2465#ifdef SHOW_DEBUG_PANEL
2467 wxString pointerStr;
2468 pointerStr.Printf(
"[%d N curr s%d] ", m_currenteState, m_elementListState.size());
2470 for (
Element* element : m_elementList) {
2471 pointerStr.Printf(
"%p ", element);
2479 UpdateTextElements();
2487void Workspace::SetPreviousState()
2490 if (m_currenteState >= 0) {
2493 GetStateListsCopy(m_elementListState[m_currenteState], m_textListState[m_currenteState], m_elementList, m_textList);
2496#ifdef SHOW_DEBUG_PANEL
2498 wxString pointerStr;
2499 pointerStr.Printf(
"[%d P curr s%d] ", m_currenteState, m_elementListState.size());
2501 for (
Element* element : m_elementListState[m_currenteState]) {
2502 pointerStr.Printf(
"%p ", element);
2506 pointerStr.Printf(
"[%d P list s%d] ", m_currenteState, m_elementListState.size());
2508 for (
Element* element : m_elementList) {
2509 pointerStr.Printf(
"%p ", element);
2517 UpdateTextElements();
2525void Workspace::UnselectAll()
2527 for (
auto& element : m_elementList) {
2530 for (
auto& text : m_textList) {
2535void Workspace::EnableHeatMap(
const bool& enable)
2544void Workspace::UpdateElementsID()
2547 for (
auto& element : m_elementList) {
2551 for (
auto& text : m_textList) {
2556void Workspace::OnTimer(wxTimerEvent& event)
2559 m_tipWindow->Close();
2560 m_tipWindow =
nullptr;
2562 if (m_mode == WorkspaceMode::MODE_EDIT) {
2563 for (
auto& element : m_elementList) {
2564 if (element->
Contains(m_camera->GetMousePosition())) {
2566 if (!tipText.IsEmpty()) {
2567 m_tipWindow =
new wxTipWindow(
this, tipText, 10000, &m_tipWindow);
2569 m_tipWindow->SetBoundingRect(wxRect(wxGetMousePosition(), wxSize(1, 1)));
2579void Workspace::SetTextList(
const std::vector< std::shared_ptr<Text> >& textList)
2583 m_textList = std::move(textList);
2585 UpdateTextElements();
2588void Workspace::SetName(wxString name)
2592#ifdef SHOW_DEBUG_PANEL
2593 m_debugFrame->SetTitle(_(
"Debug window: ") + m_name);
2598void Workspace::SetElementList(std::vector< std::shared_ptr<PowerElement> > elementList)
2600 m_elementList = std::move(elementList);
2603void Workspace::SetElementList(std::vector<Element*> elementList)
2605 m_elementList.clear();
2606 for (
auto it = elementList.begin(), itEnd = elementList.end(); it != itEnd; ++it)
2607 m_elementList.emplace_back(
static_cast<PowerElement*
>(*it));
2610void Workspace::OnIdle(wxIdleEvent& event)
2632 m_justOpened =
false;
2633 double limits[2] = { 1.05, 0.95 };
2634 m_hmPlane =
new HMPlane(m_width, m_height, limits);
2640std::vector<Element*> Workspace::GetAllElements()
const
2642 std::vector<Element*> allElements;
2644 for (
auto& element : m_elementList) allElements.push_back(element.get());
2645 for (
auto& text : m_textList) allElements.push_back(text.get());
2650bool Workspace::RunFault()
2652 auto simProp = m_properties->GetSimulationPropertiesData();
2653 double basePower = simProp.basePower;
2659 Fault fault(GetElementList());
2660 bool result = fault.RunFaultCalculation(basePower);
2662 wxMessageDialog msgDialog(
this, fault.GetErrorMessage(), _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
2663 msgDialog.ShowModal();
2666 UpdateTextElements();
2672std::vector<Element*> Workspace::GetElementList()
const
2674 std::vector<Element*> elementList;
2675 for (
auto& element : m_elementList) elementList.push_back(element.get());
2679bool Workspace::RunSCPower()
2681 Fault fault(GetElementList());
2682 bool result = fault.RunSCPowerCalcutation(100e6);
2684 wxMessageDialog msgDialog(
this, fault.GetErrorMessage(), _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
2685 msgDialog.ShowModal();
2688 UpdateTextElements();
2694bool Workspace::RunStability()
2699 Electromechanical stability(
this, GetElementList(), m_properties->GetSimulationPropertiesData());
2701 bool result = stability.RunStabilityCalculation();
2703#ifdef SHOW_DEBUG_PANEL
2704 m_debugFrame->AppendDebugMessage(stability.GetDebugMessage());
2710 wxMessageDialog msgDialog(
this, stability.GetErrorMessage(), _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
2711 msgDialog.ShowModal();
2713 m_stabilityTimeVector.clear();
2714 m_stabilityTimeVector = stability.GetTimeVector();
2719 wxMessageDialog msgDialog(
2721 wxString::Format(_(
"The program took %ld ms to run this system.\nDo you wish to open the stability graphics?"),
2723 _(
"Question"), wxYES_NO | wxCENTRE | wxICON_QUESTION);
2724 if (msgDialog.ShowModal() == wxID_YES) {
2725 std::vector<ElementPlotData> plotDataList;
2726 for (
auto& element : m_elementList) {
2728 if (element->GetPlotData(plotData)) plotDataList.push_back(plotData);
2730#ifdef SHOW_SIMULATION_PARAMETERS
2732 plotData.SetName(_(
"Simulation parameters"));
2733 plotData.SetCurveType(ElementPlotData::CurveType::CT_TEST);
2734 plotData.AddData(stability.GetIterationVector(), _(
"Iterations number"));
2735 plotDataList.push_back(plotData);
2737 ChartView* cView =
new ChartView(
this, plotDataList, m_stabilityTimeVector, m_properties->GetGeneralPropertiesData().plotLib);
2743void Workspace::OnMiddleDoubleClick(wxMouseEvent& event)
2749bool Workspace::RunStaticStudies()
2751 bool pfStatus, faultStatus, scStatus, harmStatus;
2752 pfStatus = faultStatus = scStatus = harmStatus =
false;
2754 bool runHarmDistortion = m_properties->GetSimulationPropertiesData().harmDistortionAfterPowerFlow;
2756 pfStatus = RunPowerFlow(runHarmDistortion);
2758 if (m_properties->GetSimulationPropertiesData().faultAfterPowerFlow) {
2759 if (pfStatus) faultStatus = RunFault();
2765 if (m_properties->GetSimulationPropertiesData().scPowerAfterPowerFlow) {
2766 if (pfStatus) scStatus = RunSCPower();
2772 if (runHarmDistortion) {
2773 if (pfStatus) harmStatus = RunHarmonicDistortion(
false);
2779 if (pfStatus && faultStatus && scStatus && harmStatus)
return true;
2784bool Workspace::RunHarmonicDistortion(
bool runPowerFlowBefore)
2786 auto simProp = m_properties->GetSimulationPropertiesData();
2787 double basePower = simProp.basePower;
2792 if (runPowerFlowBefore) {
2793 if (!RunPowerFlow(
true))
return false;
2796 bool hasEMTElement =
false;
2797 for (
auto& element : m_elementList) {
2798 if (
auto emtElement =
dynamic_cast<EMTElement*
>(element.get())) {
2799 if (emtElement->IsOnline()) hasEMTElement =
true;
2803 HarmLoadConnection loadConnection = simProp.harmLoadConnection;
2806 bool result = pq.CalculateDistortions(basePower, loadConnection);
2809 if (hasEMTElement && result) {
2813 .Icon(wxIcon(Paths::GetDataPath() +
"/images/ribbon/harmDist32.png", wxBITMAP_TYPE_PNG))
2814 .Title(_(
"<b>Calculating Harmonic Flow</b>"))
2815 .
Text(_(
"Please wait..."))
2816 .Foreground(*wxWHITE)
2817 .Background(*wxBLACK)
2818 .Transparency(4 * wxALPHA_OPAQUE / 5)
2820 std::vector<double> thdList;
2821 for (
auto const& bus : pq.GetBusList())
2822 thdList.emplace_back(bus->GetElectricalData().thd);
2824 while (error > 1e-3) {
2826 if (!RunPowerFlow(
false,
false))
return false;
2829 bool result = pq.CalculateDistortions(basePower, loadConnection);
2834 for (
auto const& bus : pq.GetBusList()) {
2835 double errorBus = std::abs(bus->GetElectricalData().thd - thdList[i]);
2838 else if (errorBus > error)
2840 thdList[i] = bus->GetElectricalData().thd;
2847 wxMessageDialog msgDialog(
this, pq.GetErrorMessage(), _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
2848 msgDialog.ShowModal();
2851 UpdateTextElements();
2858bool Workspace::RunFrequencyResponse()
2861 std::vector<Bus*> busList;
2862 for (
auto& element : m_elementList) {
2863 if (
Bus* bus =
dynamic_cast<Bus*
>(element.get())) { busList.push_back(bus); }
2866 auto data = m_properties->GetFreqRespData();
2868 FrequencyResponseForm frForm(
this, busList, data.injBusNumber, data.initFreq, data.finalFreq, data.stepFreq);
2870 if (frForm.ShowModal() == wxID_OK) {
2871 data.initFreq = frForm.GetInitFreq();
2872 data.finalFreq = frForm.GetEndFreq();
2873 data.stepFreq = frForm.GetStepFreq();
2874 data.injBusNumber = frForm.GetInjBusNumber();
2875 m_properties->SetFreqRespData(data);
2880 auto simProp = m_properties->GetSimulationPropertiesData();
2881 double basePower = simProp.basePower;
2887 bool result = pq.CalculateFrequencyResponse(simProp.stabilityFrequency, data.initFreq, data.finalFreq,
2888 data.stepFreq, data.injBusNumber, basePower, simProp.harmLoadConnection);
2890 wxMessageDialog msgDialog(
2891 this, wxString::Format(_(
"Calculations done.\nDo you wish to open the frequency response graphics?")),
2892 _(
"Question"), wxYES_NO | wxCENTRE | wxICON_QUESTION);
2893 if (msgDialog.ShowModal() == wxID_YES) {
2894 std::vector<ElementPlotData> plotDataList;
2895 for (
auto& element : m_elementList) {
2900 ChartView* cView =
new ChartView(
this, plotDataList, pq.GetFrequencies(), m_properties->GetGeneralPropertiesData().plotLib);
2904 UpdateTextElements();
2909void Workspace::OnResize(wxSizeEvent& event)
2921 m_width =
static_cast<float>(GetSize().x) - 1.0f;
2922 m_height =
static_cast<float>(GetSize().y) - 1.0f;
2924 if (m_hmPlane && m_showHM) {
2925 m_hmPlane->ResizeDC(m_width, m_height);
2926 m_showHMTimer =
true;
2927 m_timerHeatMap->Start();
2932void Workspace::OnHeatMapTime(wxTimerEvent& event)
2934 if (m_showHMTimer) {
2937 m_showHMTimer =
false;
ElectricalUnit
Electrical units.
Node for power elements. All others power elements are connected through this.
Class responsible for the correct visualization of the elements on screen.
This class is responsible to manage the charts generated in the transient electromechanical studies.
Element to connect ATP-EMTP.
Calculates the electromechanical transient based on disturbances (e.g. system fault).
Class to store the elements in the clipboard.
Base class of all elements of the program. This class is responsible for manage graphical and his dat...
virtual Element * GetCopy()
Get a the element copy.
virtual std::vector< Element * > GetParentList() const
Get the parent list.
virtual int GetID() const
Get the element ID.
virtual void UpdateNodes()
Update the nodes according to the parents. If a parent is removed, use this method.
virtual std::vector< wxPoint2DDouble > GetPointList() const
Get the list of points that connect the element to bus.
void SetSelected(bool selected=true)
Set element selection.
wxPoint2DDouble GetPosition() const
Get the element position.
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,...
virtual void CalculateBoundaries(wxPoint2DDouble &leftUp, wxPoint2DDouble &rightBottom) const
Calculate the element boundaries.
virtual std::vector< Element * > GetChildList() const
Get the Child list.
virtual void RemoveChild(Element *child)
Remove a child from the list.
virtual bool ShowForm(wxWindow *parent, Element *element, wxWindow *workspace=nullptr)
Show element data form.
virtual void ReplaceParent(Element *oldParent, Element *newParent)
Replace a parent.
virtual void StartMove(wxPoint2DDouble position)
Update the element attributes related to the movement.
virtual void MoveNode(Element *parent, wxPoint2DDouble position)
Move a node. StartMove(wxPoint2DDouble position) before start moving.
void SetPosition(const wxPoint2DDouble position)
Set the element position and update the rectangle.
virtual void RemoveParent(Element *parent)
Remove a parent.
virtual void SetID(int id)
Set the element ID.
virtual wxString GetTipText() const
Get the tip text.
virtual void Move(wxPoint2DDouble position)
Move the element other position.
virtual bool Contains(wxPoint2DDouble position) const =0
Checks if the element contains a position.
bool IsSelected() const
Checks if the element is selected.
virtual void ReplaceChild(Element *oldChild, Element *newChild)
Replace a child from the list.
virtual void RotateNode(Element *parent, bool clockwise=true)
Rotate a node.
virtual void Rotate(bool clockwise=true)
Rotate the element.
Calculate the fault of the system and update the elements data.
Save and opens the projects created on disk.
Abstract class of power elements.
Calculate the power flow.
Responsible for the power quality calculations.
General and simulation data manager.
Element that shows power element informations in workspace.
virtual Element * GetCopy()
Get a the element copy.
virtual bool Contains(wxPoint2DDouble position) const
Checks if the element contains a position.
virtual bool Intersects(wxRect2DDouble rect) const
Check if the element's rect intersects other rect.
virtual void Rotate(bool clockwise=true)
Rotate the element.