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>
62 DWORD GetWorkspaceGDIObjects()
64 return GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
67 void 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);
93 m_workspacePanel->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);
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;
133 m_CEMngr = std::make_unique<ControlEditorManager>(
this);
134 m_CWMngr = std::make_unique<ChartWindowManager>(
this);
139Workspace::~Workspace()
141 if (m_hmPlane)
delete m_hmPlane;
142 if (m_camera)
delete m_camera;
143 if (m_properties)
delete m_properties;
146void Workspace::SetColourTheme()
148 m_properties->SetGUIColourTheme();
149 m_workspacePanel->SetBackgroundColour(m_properties->GetGUIColour()->background);
152void Workspace::OnPaint(wxPaintEvent& event)
158 wxAutoBufferedPaintDC dc(m_workspacePanel);
160 wxGraphicsContext* gc = wxGraphicsContext::Create(dc);
173void Workspace::DrawScene(wxGraphicsContext* gc)
179 if (m_hmPlane && m_showHM) {
183 m_hmPlane->DrawDC(gc);
189 gc->Scale(m_camera->GetScale(), m_camera->GetScale());
190 gc->Translate(m_camera->GetTranslation().m_x, m_camera->GetTranslation().m_y);
193 for (
auto& element : m_elementList) {
197 element->DrawDC(m_properties->GetGUIColour(), m_camera->GetTranslation(), m_camera->GetScale(), gc);
208 Text* text =
new Text(wxPoint2DDouble(0.0, 0.0), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
210 text->DrawDC(m_properties->GetGUIColour(), m_camera->GetTranslation(), m_camera->GetScale(), gc);
217 for (
auto& text : m_textList) {
221 text->DrawDC(m_properties->GetGUIColour(), m_camera->GetTranslation(), m_camera->GetScale(), gc);
231 wxColour selectionColour = m_properties->GetGUIColour()->selection;
232 gc->SetPen(wxPen(selectionColour));
233 gc->SetBrush(wxBrush(selectionColour));
234 gc->DrawRectangle(m_selectionRect.m_x, m_selectionRect.m_y, m_selectionRect.m_width, m_selectionRect.m_height);
239 if (m_hmPlane && m_showHM) {
243 m_hmPlane->DrawLabelDC(gc);
251void Workspace::DrawScene(wxDC& dc)
254 if (m_hmPlane && m_showHM) {
255 m_hmPlane->DrawDC(dc);
258 dc.SetUserScale(m_camera->GetScale(), m_camera->GetScale());
259 dc.SetDeviceOrigin(m_camera->GetTranslation().m_x * m_camera->GetScale(), m_camera->GetTranslation().m_y * m_camera->GetScale());
263 for (
auto& element : m_elementList) {
264 element->DrawDC(m_properties->GetGUIColour(), m_camera->GetTranslation(), m_camera->GetScale(), dc);
269 Text* text =
new Text(wxPoint2DDouble(0.0, 0.0), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
271 text->DrawDC(m_properties->GetGUIColour(), m_camera->GetTranslation(), m_camera->GetScale(), dc);
275 for (
auto& text : m_textList) {
276 text->DrawDC(m_properties->GetGUIColour(), m_camera->GetTranslation(), m_camera->GetScale(), dc);
279 if (m_hmPlane && m_showHM) {
280 m_hmPlane->DrawLabelDC(dc);
284void Workspace::CopyToClipboard()
286 wxSize size = GetClientSize();
290 wxBitmap bitmap(size.x * scale, size.y * scale);
291 wxMemoryDC memDC(bitmap);
293 memDC.SetBackground(*wxWHITE_BRUSH);
296 wxGraphicsContext* gc = wxGraphicsContext::Create(memDC);
298 gc->Scale(scale, scale);
303 memDC.SelectObject(wxNullBitmap);
305 if (wxTheClipboard->Open())
307 wxTheClipboard->SetData(
new wxBitmapDataObject(bitmap));
308 wxTheClipboard->Close();
312void Workspace::ExportAsSVG(wxString path)
314 wxSize size = GetClientSize();
316 wxSVGFileDC svgDC(path, size.x, size.y);
318 svgDC.SetClippingRegion(wxRect(0, 0, size.x, size.y));
323void Workspace::OnLeftClickDown(wxMouseEvent& event)
325 wxWindow* dialogParent = wxGetTopLevelParent(
this);
326 if (!dialogParent) dialogParent =
this;
329 wxPoint clickPoint =
event.GetPosition();
330 bool foundElement =
false;
332 bool showNewElementForm =
false;
333 bool clickOnSwitch =
false;
334 bool unselectAll =
true;
335 std::vector<Element*> notUnselectElementList;
336 std::vector<Text*> notUnselectTextList;
338 if (m_mode == WorkspaceMode::MODE_INSERT_TEXT || m_mode == WorkspaceMode::MODE_PASTE || m_mode == WorkspaceMode::MODE_DRAG_PASTE) {
339 m_mode = WorkspaceMode::MODE_EDIT;
342 else if (m_mode == WorkspaceMode::MODE_INSERT || m_mode == WorkspaceMode::MODE_DRAG_INSERT || m_mode == WorkspaceMode::MODE_DRAG_INSERT_TEXT) {
343 wxPoint2DDouble clickPointWorld = m_camera->ScreenToWorld(clickPoint);
345 if (!m_elementList.empty()) {
347 newElement = m_elementList.back().get();
348 for (
auto& element : m_elementList) {
350 if (element->Contains(clickPointWorld)) {
352 if (
auto bus =
dynamic_cast<Bus*
>(element.get())) {
358 if (newElement->
AddParent(bus, clickPointWorld)) {
359 ValidateElementsVoltages();
361 showNewElementForm =
true;
362 m_mode = WorkspaceMode::MODE_EDIT;
371 if (
auto line =
dynamic_cast<Line*
>(newElement)) { line->AddPoint(clickPointWorld); }
379 bool clickPickbox =
false;
381 for (
auto& element : m_elementList) {
382 element->ResetPickboxes();
385 element->StartMove(m_camera->ScreenToWorld(clickPoint));
388 if (element->NodeContains(m_camera->ScreenToWorld(clickPoint)) != 0 && element->IsSelected()) {
389 m_mode = WorkspaceMode::MODE_MOVE_NODE;
390 m_disconnectedElement =
true;
393 if (m_hmPlane && m_showHM) {
399 else if (element->Contains(m_camera->ScreenToWorld(clickPoint))) {
400 notUnselectElementList.emplace_back(element.get());
402 if (element->IsSelected()) unselectAll =
false;
404 element->SetSelected();
405 element->ShowPickbox();
408 for (
auto& text : m_textList) {
409 if (text->GetElement() == element.get()) {
410 notUnselectTextList.emplace_back(text.get());
412 text->SetAllowRotation(
false);
413 if (unselectAll) text->SetAltSelectionColour();
418 if (element->PickboxContains(m_camera->ScreenToWorld(clickPoint))) {
419 m_mode = WorkspaceMode::MODE_MOVE_PICKBOX;
423 if (!clickPickbox) { m_mode = WorkspaceMode::MODE_MOVE_ELEMENT; }
425 if (m_hmPlane && m_showHM) {
431 else if (element->SwitchesContains(m_camera->ScreenToWorld(clickPoint))) {
432 element->SetOnline(element->IsOnline() ?
false : true);
433 clickOnSwitch =
true;
438 for (
auto& text : m_textList) {
439 text->
StartMove(m_camera->ScreenToWorld(clickPoint));
441 if (text->
Contains(m_camera->ScreenToWorld(clickPoint))) {
442 notUnselectTextList.emplace_back(text.get());
446 text->SetAltSelectionColour(
false);
447 text->SetAllowRotation();
448 m_mode = WorkspaceMode::MODE_MOVE_ELEMENT;
450 if (m_hmPlane && m_showHM) {
459 if (!event.ControlDown() && unselectAll) {
460 for (
auto& element : m_elementList) {
462 for (
Element* notUnselectElement : notUnselectElementList) {
463 if (notUnselectElement == element.get()) select =
true;
465 element->SetSelected(select);
467 for (
auto& text : m_textList) {
469 for (
auto& notUnselectText : notUnselectTextList) {
470 if (notUnselectText == text.get()) select =
true;
476 if (!foundElement && !clickOnSwitch) {
477 m_mode = WorkspaceMode::MODE_SELECTION_RECT;
478 m_startSelRect = m_camera->ScreenToWorld(clickPoint);
479 if (m_hmPlane && m_showHM) {
487 if (showNewElementForm) {
489 newElement->
ShowForm(dialogParent, newElement,
this);
491 if (m_workspacePanel) m_workspacePanel->SetFocus();
492 CheckSlackBusDuplication(newElement);
494 if (m_continuousCalc) RunStaticStudies();
497 if (clickOnSwitch && m_continuousCalc) RunStaticStudies();
502void Workspace::OnLeftDoubleClick(wxMouseEvent& event)
504 wxWindow* dialogParent = wxGetTopLevelParent(
this);
505 if (!dialogParent) dialogParent =
this;
508 bool elementEdited =
false;
509 bool clickOnSwitch =
false;
512 for (
auto& element : m_elementList) {
514 if (element->Contains(m_camera->ScreenToWorld(event.GetPosition()))) {
515 bool elementIsBus =
false;
517 Bus* currentBus =
nullptr;
518 if ((currentBus =
dynamic_cast<Bus*
>(element.get()))) {
520 oldBus = *currentBus;
523 if (element->ShowForm(dialogParent, element.get(),
this)) {
524 CheckSlackBusDuplication(element.get());
528 if (m_workspacePanel) m_workspacePanel->SetFocus();
529 elementEdited =
true;
536 if (oldBus.GetElectricalData().nominalVoltage != currentBus->GetElectricalData().nominalVoltage ||
537 oldBus.GetElectricalData().nominalVoltageUnit !=
538 currentBus->GetElectricalData().nominalVoltageUnit) {
540 std::vector<Element*> childList = element->GetChildList();
541 for (
auto itc = childList.begin(), itcEnd = childList.end(); itc != itcEnd; ++itc) {
543 if (
typeid(*child) ==
typeid(
Line)) {
544 wxMessageDialog msgDialog(dialogParent, _(
"Do you want to change the rated voltage of the path?"),
545 _(
"Warning"), wxYES_NO | wxCENTRE | wxICON_WARNING);
546 if (msgDialog.ShowModal() == wxID_YES)
547 ValidateBusesVoltages(element.get());
549 auto data = currentBus->GetElectricalData();
550 data.nominalVoltage = oldBus.GetElectricalData().nominalVoltage;
551 data.nominalVoltageUnit = oldBus.GetElectricalData().nominalVoltageUnit;
552 currentBus->SetElectricalData(data);
558 ValidateElementsVoltages();
566 else if (element->SwitchesContains(m_camera->ScreenToWorld(event.GetPosition()))) {
567 element->SetOnline(element->IsOnline() ?
false : true);
568 clickOnSwitch =
true;
573 for (
auto& text : m_textList) {
574 if (text->
Contains(m_camera->ScreenToWorld(event.GetPosition()))) {
575 if (text->ShowForm(dialogParent, GetElementList())) SaveCurrentState();
577 if (m_workspacePanel) m_workspacePanel->SetFocus();
583 UpdateTextElements();
584 if (m_continuousCalc) RunStaticStudies();
586 if (clickOnSwitch && m_continuousCalc) RunStaticStudies();
588 if (redraw) Redraw();
592void Workspace::OnRightClickDown(wxMouseEvent& event)
595 if (m_mode == WorkspaceMode::MODE_EDIT) {
596 for (
auto& element : m_elementList) {
597 if (element->IsSelected()) {
599 if (element->Contains(m_camera->ScreenToWorld(event.GetPosition()))) {
600 element->ShowPickbox(
false);
602 menu.SetClientData(element.get());
603 if (element->GetContextMenu(menu)) {
605 menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &Workspace::OnPopupClick,
this);
608 if (m_workspacePanel) m_workspacePanel->SetFocus();
611 if (!menu.GetClientData())
break;
613 element->ResetPickboxes();
618 if (redraw) Redraw();
622void Workspace::OnLeftClickUp(wxMouseEvent& event)
626 bool foundPickbox =
false;
627 bool findNewParent =
false;
628 bool updateVoltages =
false;
629 bool saveCurrentState =
false;
630 auto itnp = m_elementList.begin();
632 for (
auto it = m_elementList.begin(); it != m_elementList.end(); ++it) {
635 if (m_mode == WorkspaceMode::MODE_MOVE_PICKBOX) {
637 if (element->IsPickboxShown()) {
638 saveCurrentState =
true;
640 if (
auto bus =
dynamic_cast<Bus*
>(element.get())) {
642 for (
auto child : m_elementList) {
643 for (
auto parent : child->GetParentList()) {
647 m_disconnectedElement =
true;
666 if (m_mode == WorkspaceMode::MODE_SELECTION_RECT) {
667 if (element->Intersects(m_selectionRect)) {
668 element->SetSelected();
670 for (
auto& text : m_textList) {
671 if (text->GetElement() == element.get()) {
673 text->SetAltSelectionColour(
false);
674 text->SetAllowRotation();
682 else if (m_mode == WorkspaceMode::MODE_MOVE_NODE) {
683 if (element->IsSelected()) {
684 saveCurrentState =
true;
685 for (
auto parent : m_elementList) {
686 if (
auto bus =
dynamic_cast<Bus*
>(parent.get())) {
687 if (element->SetNodeParent(bus)) {
688 parent->AddChild(element.get());
689 findNewParent =
true;
691 element->ResetNodes();
705 if (element->PickboxContains(m_camera->ScreenToWorld(event.GetPosition()))) {
709 element->ShowPickbox(
false);
710 element->ResetPickboxes();
716 for (
auto& text : m_textList) {
717 if (m_mode == WorkspaceMode::MODE_SELECTION_RECT) {
720 text->SetAltSelectionColour(
false);
721 text->SetAllowRotation();
731 if (m_mode == WorkspaceMode::MODE_MOVE_ELEMENT) saveCurrentState =
true;
734 std::rotate(itnp, itnp + 1, m_elementList.end());
735 updateVoltages =
true;
737 if (!foundPickbox) { SetCursor(wxCURSOR_ARROW); }
739 if (m_mode != WorkspaceMode::MODE_INSERT) { m_mode = WorkspaceMode::MODE_EDIT; }
741 if (updateVoltages) { ValidateElementsVoltages(); }
743 if (saveCurrentState) SaveCurrentState();
745 if (m_continuousCalc && m_disconnectedElement) {
746 m_disconnectedElement =
false;
750 m_selectionRect = wxRect2DDouble(0, 0, 0, 0);
752 if (m_hmPlane && m_showHM) {
753 m_showHMTimer =
true;
754 m_timerHeatMap->Start();
761void Workspace::OnMouseMotion(wxMouseEvent& event)
765 case WorkspaceMode::MODE_INSERT: {
766 auto& newElement = m_elementList.back();
767 newElement->
SetPosition(m_camera->ScreenToWorld(event.GetPosition()));
771 case WorkspaceMode::MODE_INSERT_TEXT: {
772 auto& newText = m_textList.back();
773 newText->SetPosition(m_camera->ScreenToWorld(event.GetPosition()));
777 case WorkspaceMode::MODE_DRAG:
778 case WorkspaceMode::MODE_DRAG_INSERT:
779 case WorkspaceMode::MODE_DRAG_INSERT_TEXT:
780 case WorkspaceMode::MODE_DRAG_PASTE: {
781 m_camera->SetTranslation(event.GetPosition());
785 case WorkspaceMode::MODE_EDIT: {
786 bool foundPickbox =
false;
787 for (
auto& element : m_elementList) {
788 if (element->IsSelected()) {
790 if (element->Contains(m_camera->ScreenToWorld(event.GetPosition()))) {
791 element->ShowPickbox();
795 if (element->PickboxContains(m_camera->ScreenToWorld(event.GetPosition()))) {
800 SetCursor(element->GetBestPickboxCursor());
805 else if (!foundPickbox) {
809 SetCursor(wxCURSOR_ARROW);
813 element->ResetPickboxes();
816 else if (!foundPickbox) {
817 if (element->IsPickboxShown()) redraw =
true;
819 element->ShowPickbox(
false);
820 element->ResetPickboxes();
824 SetCursor(wxCURSOR_ARROW);
833 case WorkspaceMode::MODE_MOVE_NODE: {
834 for (
auto& element : m_elementList) {
835 if (element->IsSelected()) {
836 element->MoveNode(
nullptr, m_camera->ScreenToWorld(event.GetPosition()));
842 case WorkspaceMode::MODE_MOVE_PICKBOX: {
843 for (
auto& element : m_elementList) {
844 if (element->IsSelected()) {
845 element->MovePickbox(m_camera->ScreenToWorld(event.GetPosition()));
849 if (m_hmPlane && m_showHM) {
854 case WorkspaceMode::MODE_MOVE_ELEMENT:
855 case WorkspaceMode::MODE_PASTE: {
856 for (
auto it = m_elementList.begin(), itEnd = m_elementList.end(); it != itEnd; ++it) {
858 if (element->IsSelected()) {
859 element->Move(m_camera->ScreenToWorld(event.GetPosition()));
861 std::vector<Element*> childList = element->GetChildList();
862 for (
auto it = childList.begin(), itEnd = childList.end(); it != itEnd; ++it) {
863 (*it)->MoveNode(element.get(), m_camera->ScreenToWorld(event.GetPosition()));
869 for (
auto& text : m_textList) {
871 text->
Move(m_camera->ScreenToWorld(event.GetPosition()));
875 if (m_hmPlane && m_showHM) {
880 case WorkspaceMode::MODE_SELECTION_RECT: {
881 wxPoint2DDouble currentPos = m_camera->ScreenToWorld(event.GetPosition());
883 if (currentPos.m_x < m_startSelRect.m_x) {
885 w = m_startSelRect.m_x - currentPos.m_x;
888 x = m_startSelRect.m_x;
889 w = currentPos.m_x - m_startSelRect.m_x;
891 if (currentPos.m_y < m_startSelRect.m_y) {
893 h = m_startSelRect.m_y - currentPos.m_y;
896 y = m_startSelRect.m_y;
897 h = currentPos.m_y - m_startSelRect.m_y;
900 m_selectionRect = wxRect2DDouble(x, y, w, h);
908 m_camera->UpdateMousePosition(event.GetPosition());
914void Workspace::OnMiddleDown(wxMouseEvent& event)
918 case WorkspaceMode::MODE_INSERT: {
919 m_mode = WorkspaceMode::MODE_DRAG_INSERT;
921 case WorkspaceMode::MODE_INSERT_TEXT: {
922 m_mode = WorkspaceMode::MODE_DRAG_INSERT_TEXT;
924 case WorkspaceMode::MODE_PASTE: {
925 m_mode = WorkspaceMode::MODE_DRAG_PASTE;
928 m_mode = WorkspaceMode::MODE_DRAG;
931 m_camera->StartTranslation(m_camera->ScreenToWorld(event.GetPosition()));
934 if (m_hmPlane && m_showHM) {
941void Workspace::OnMiddleUp(wxMouseEvent& event)
944 case WorkspaceMode::MODE_DRAG_INSERT: {
945 m_mode = WorkspaceMode::MODE_INSERT;
947 case WorkspaceMode::MODE_DRAG_INSERT_TEXT: {
948 m_mode = WorkspaceMode::MODE_INSERT_TEXT;
950 case WorkspaceMode::MODE_DRAG_PASTE: {
951 m_mode = WorkspaceMode::MODE_PASTE;
953 case WorkspaceMode::MODE_INSERT:
954 case WorkspaceMode::MODE_INSERT_TEXT:
955 case WorkspaceMode::MODE_PASTE: {
959 m_mode = WorkspaceMode::MODE_EDIT;
964 if (m_hmPlane && m_showHM) {
972void Workspace::OnScroll(wxMouseEvent& event)
974 if (event.GetWheelRotation() > 0)
975 m_camera->SetScale(event.GetPosition(), +0.05);
977 m_camera->SetScale(event.GetPosition(), -0.05);
979 if (m_hmPlane && m_showHM) {
981 m_showHMTimer =
true;
982 m_timerHeatMap->Start();
989void Workspace::OnKeyDown(wxKeyEvent& event)
991 bool insertingElement =
false;
992 if (m_mode == WorkspaceMode::MODE_INSERT || m_mode == WorkspaceMode::MODE_INSERT_TEXT) insertingElement =
true;
994 char key =
event.GetUnicodeKey();
995 if (key != WXK_NONE) {
999 if (m_mode == WorkspaceMode::MODE_INSERT) {
1001 auto elementToDelete = m_elementList.back();
1004 for (
auto& element : m_elementList) {
1005 element->RemoveChild(elementToDelete.get());
1008 m_elementList.pop_back();
1009 m_mode = WorkspaceMode::MODE_EDIT;
1012 else if (m_mode == WorkspaceMode::MODE_INSERT_TEXT) {
1013 m_textList.pop_back();
1014 m_mode = WorkspaceMode::MODE_EDIT;
1020 DeleteSelectedElements();
1023 if (!insertingElement) {
1025 auto newText = std::make_shared<Text>(
1026 m_camera->ScreenToWorld(event.GetPosition()),
1027 m_properties->GetGeneralPropertiesData().labelFont,
1028 m_properties->GetGeneralPropertiesData().labelFontSize);
1029 m_textList.push_back(newText);
1030 m_mode = WorkspaceMode::MODE_INSERT_TEXT;
1031 m_statusBar->SetStatusText(_(
"Insert Text: Click to insert, ESC to cancel."));
1032 if (m_hmPlane && m_showHM) {
1039 if (event.GetModifiers() == wxMOD_SHIFT) { Fit(); }
1043 RotateSelectedElements(event.GetModifiers() != wxMOD_SHIFT);
1047 if (!insertingElement) {
1048 auto newBus = std::make_shared<Bus>(m_camera->ScreenToWorld(event.GetPosition()),
1049 wxString::Format(_(
"Bus %d"), GetElementNumber(ID_BUS)));
1050 IncrementElementNumber(ID_BUS);
1051 m_elementList.push_back(newBus);
1052 m_mode = WorkspaceMode::MODE_INSERT;
1053 m_statusBar->SetStatusText(_(
"Insert Bus: Click to insert, ESC to cancel."));
1054 if (m_hmPlane && m_showHM) {
1061 if (!insertingElement) {
1062 if (!event.ControlDown() && event.ShiftDown()) {
1063 auto newLoad = std::make_shared<Load>(wxString::Format(_(
"Load %d"), GetElementNumber(ID_LOAD)));
1064 IncrementElementNumber(ID_LOAD);
1065 m_elementList.push_back(newLoad);
1066 m_mode = WorkspaceMode::MODE_INSERT;
1067 m_statusBar->SetStatusText(_(
"Insert Load: Click on a bus, ESC to cancel."));
1069 else if (!event.ControlDown() && !event.ShiftDown()) {
1070 auto newLine = std::make_shared<Line>(wxString::Format(_(
"Line %d"), GetElementNumber(ID_LINE)));
1071 IncrementElementNumber(ID_LINE);
1072 m_elementList.push_back(newLine);
1073 m_mode = WorkspaceMode::MODE_INSERT;
1074 m_statusBar->SetStatusText(_(
"Insert Line: Click on two buses, ESC to cancel."));
1076 if (m_hmPlane && m_showHM) {
1085 if (!insertingElement) {
1086 auto newTransformer = std::make_shared<Transformer>(wxString::Format(_(
"Transformer %d"), GetElementNumber(ID_TRANSFORMER)));
1087 IncrementElementNumber(ID_TRANSFORMER);
1088 m_elementList.push_back(newTransformer);
1089 m_mode = WorkspaceMode::MODE_INSERT;
1090 m_statusBar->SetStatusText(_(
"Insert Transformer: Click on two buses, ESC to cancel."));
1091 if (m_hmPlane && m_showHM) {
1099 if (!insertingElement) {
1100 auto newGenerator = std::make_shared<SyncGenerator>(wxString::Format(_(
"Generator %d"), GetElementNumber(ID_SYNCGENERATOR)));
1101 IncrementElementNumber(ID_SYNCGENERATOR);
1102 m_elementList.push_back(newGenerator);
1103 m_mode = WorkspaceMode::MODE_INSERT;
1104 m_statusBar->SetStatusText(_(
"Insert Generator: Click on a bus, ESC to cancel."));
1105 if (m_hmPlane && m_showHM) {
1112 if (!insertingElement) {
1113 if (event.GetModifiers() == wxMOD_SHIFT) {
1114 auto newInductor = std::make_shared<Inductor>(wxString::Format(_(
"Inductor %d"), GetElementNumber(ID_INDUCTOR)));
1115 IncrementElementNumber(ID_INDUCTOR);
1116 m_elementList.push_back(newInductor);
1117 m_mode = WorkspaceMode::MODE_INSERT;
1118 m_statusBar->SetStatusText(_(
"Insert Inductor: Click on a bus, ESC to cancel."));
1122 auto newIndMotor = std::make_shared<IndMotor>(wxString::Format(_(
"Induction motor %d"), GetElementNumber(ID_INDMOTOR)));
1123 IncrementElementNumber(ID_INDMOTOR);
1124 m_elementList.push_back(newIndMotor);
1125 m_mode = WorkspaceMode::MODE_INSERT;
1126 m_statusBar->SetStatusText(_(
"Insert Induction Motor: Click on a bus, ESC to cancel."));
1128 if (m_hmPlane && m_showHM) {
1136 if (!insertingElement) {
1137 auto newSyncCondenser = std::make_shared<SyncMotor>(wxString::Format(_(
"Synchronous condenser %d"), GetElementNumber(ID_SYNCMOTOR)));
1138 IncrementElementNumber(ID_SYNCMOTOR);
1139 m_elementList.push_back(newSyncCondenser);
1140 m_mode = WorkspaceMode::MODE_INSERT;
1141 m_statusBar->SetStatusText(_(
"Insert Synchronous Condenser: Click on a bus, ESC to cancel."));
1142 if (m_hmPlane && m_showHM) {
1149 if (!insertingElement) {
1150 if (event.GetModifiers() == wxMOD_SHIFT) {
1151 auto newCapacitor = std::make_shared<Capacitor>(wxString::Format(_(
"Capacitor %d"), GetElementNumber(ID_CAPACITOR)));
1152 IncrementElementNumber(ID_CAPACITOR);
1153 m_elementList.push_back(newCapacitor);
1154 m_mode = WorkspaceMode::MODE_INSERT;
1155 m_statusBar->SetStatusText(_(
"Insert Capacitor: Click on a bus, ESC to cancel."));
1156 if (m_hmPlane && m_showHM) {
1161 else if (event.GetModifiers() == wxMOD_CONTROL) {
1167 if (!insertingElement) {
1168 if (event.ShiftDown() && event.ControlDown()) {
1178 else if (event.GetModifiers() == wxMOD_SHIFT) {
1179 auto newHarmCurrent = std::make_shared<HarmCurrent>(
1180 wxString::Format(_(
"Harmonic Current %d"), GetElementNumber(ID_HARMCURRENT)));
1181 IncrementElementNumber(ID_HARMCURRENT);
1182 m_elementList.push_back(newHarmCurrent);
1183 m_mode = WorkspaceMode::MODE_INSERT;
1184 m_statusBar->SetStatusText(
1185 _(
"Insert Harmonic Current Source: Click on a bus, ESC to cancel."));
1187 if (m_hmPlane && m_showHM) {
1194 if (!insertingElement) {
1195 if (event.GetModifiers() == wxMOD_CONTROL) { Paste(); }
1199 if (!insertingElement) {
1200 if (event.GetModifiers() == wxMOD_CONTROL) {
1204 if (GetSavedPath().IsOk()) {
1205 fileHandling.SaveProject(GetSavedPath());
1208 wxFileDialog saveFileDialog(
this, _(
"Save PSP file"),
"",
"",
"PSP files (*.psp)|*.psp",
1209 wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
1210 if (saveFileDialog.ShowModal() == wxID_CANCEL)
return;
1212 fileHandling.SaveProject(saveFileDialog.GetPath());
1213 wxFileName fileName(saveFileDialog.GetPath());
1214 SetName(fileName.GetName());
1215 if (m_auiNotebook) m_auiNotebook->SetPageText(m_auiNotebook->GetPageIndex(
this), GetName());
1216 SetSavedPath(fileName);
1222 if (!insertingElement) {
1223 if (event.ControlDown() && !event.ShiftDown()) { SetPreviousState(); }
1224 if (event.ControlDown() && event.ShiftDown()) { SetNextState(); }
1228 if (!insertingElement) {
1229 if (event.GetModifiers() == wxMOD_CONTROL) { SetNextState(); }
1233 if (!insertingElement) {
1234 if (event.GetModifiers() == wxMOD_SHIFT) {
1236 if (!insertingElement) {
1237 auto newEMTElement = std::make_shared<EMTElement>(wxString::Format(_(
"Electromagnetic Element %d"), GetElementNumber(ID_EMTELEMENT)));
1238 IncrementElementNumber(ID_EMTELEMENT);
1239 m_elementList.push_back(newEMTElement);
1240 m_mode = WorkspaceMode::MODE_INSERT;
1241 m_statusBar->SetStatusText(_(
"Insert Electromagnetic Transient Element: Click on a bus, ESC to cancel."));
1242 if (m_hmPlane && m_showHM) {
1251#ifdef SHOW_DEBUG_PANEL
1253 if (event.GetModifiers() == wxMOD_CONTROL) {
1255 m_debugFrame->SetFocus();
1270void Workspace::UpdateStatusBar()
1273 case WorkspaceMode::MODE_DRAG: {
1274 m_statusBar->SetStatusText(_(
"MODE: DRAG"), 1);
1277 case WorkspaceMode::MODE_PASTE:
1278 case WorkspaceMode::MODE_DRAG_PASTE: {
1279 m_statusBar->SetStatusText(_(
"MODE: PASTE"), 1);
1282 case WorkspaceMode::MODE_INSERT:
1283 case WorkspaceMode::MODE_INSERT_TEXT:
1284 case WorkspaceMode::MODE_DRAG_INSERT:
1285 case WorkspaceMode::MODE_DRAG_INSERT_TEXT: {
1286 m_statusBar->SetStatusText(_(
"MODE: INSERT"), 1);
1289 case WorkspaceMode::MODE_MOVE_ELEMENT:
1290 case WorkspaceMode::MODE_MOVE_PICKBOX:
1291 case WorkspaceMode::MODE_MOVE_NODE:
1292 case WorkspaceMode::MODE_SELECTION_RECT:
1293 case WorkspaceMode::MODE_EDIT: {
1294 if (m_oldStatusMode != m_mode)
1295 m_statusBar->SetStatusText(wxT(
""));
1296 m_statusBar->SetStatusText(_(
"MODE: EDIT"), 1);
1300 m_statusBar->SetStatusText(wxString::Format(_(
"ZOOM: %d%%"), (
int)(m_camera->GetScale() * 100.0)), 2);
1301 m_statusBar->SetStatusText(
1302 wxString::Format(wxT(
"X: %.1f Y: %.1f"), m_camera->GetMousePosition().m_x, m_camera->GetMousePosition().m_y),
1304 m_oldStatusMode = m_mode;
1307int Workspace::GetElementNumberFromList(
Element* element)
1310 for (
auto& elementFromList : m_elementList) {
1311 if (element->GetElementType() == elementFromList->GetElementType()) {
1312 if (element == elementFromList.get())
return elementNum;
1319void Workspace::GetStateListsCopy(
const std::vector< std::shared_ptr<PowerElement> >& elementsList,
1320 const std::vector< std::shared_ptr<Text> >& textList,
1321 std::vector< std::shared_ptr<PowerElement> >& elementsListCopy,
1322 std::vector< std::shared_ptr<Text> >& textListCopy)
1327 elementsListCopy.clear();
1328 textListCopy.clear();
1330 std::map<Element*, Element*> elementMap;
1332 for (
auto& element : elementsList) {
1340 elementsListCopy.emplace_back(copyElement);
1341 elementMap[element.get()] = copyElement;
1344 for (
auto& copyElement : elementsListCopy) {
1347 for (
Element* parent : copyElement->GetParentList()) {
1348 auto it = elementMap.find(parent);
1350 if (it != elementMap.end())
1351 copyElement->SetParent(it->second, i);
1357 for (
Element* child : copyElement->GetChildList()) {
1358 auto it = elementMap.find(child);
1360 if (it != elementMap.end())
1361 copyElement->SetChild(it->second, i);
1367 for (
const auto& text : textList) {
1371 auto copyText =
static_cast<Text*
>(text->
GetCopy());
1376 auto it = elementMap.find(copyText->GetElement());
1378 if (it != elementMap.end())
1379 copyText->SetElement(it->second);
1381 copyText->SetElement(
nullptr);
1382 textListCopy.emplace_back(copyText);
1386void Workspace::UpdateHeatMap()
1388 if (m_hmPlane && m_showHM) {
1391 wxRect2DDouble screenRect(-100, -100, m_width + 200.0, m_height + 200.0);
1394 std::vector<Bus*> busList;
1395 float minVoltage, maxVoltage;
1396 if (m_hmAutomaticLabel) {
1401 minVoltage = m_hmPlane->GetMinLimit();
1402 maxVoltage = m_hmPlane->GetMaxLimit();
1405 for (
auto& element : m_elementList) {
1406 if (
Bus* bus =
dynamic_cast<Bus*
>(element.get())) {
1407 if (m_hmAutomaticLabel) {
1408 const float voltage = std::abs(bus->GetElectricalData().voltage);
1409 if (minVoltage > voltage) minVoltage = voltage;
1410 if (maxVoltage < voltage) maxVoltage = voltage;
1412 busList.push_back(bus);
1415 if (m_hmAutomaticLabel) {
1416 m_hmPlane->SetLabelLimits(minVoltage, maxVoltage);
1419 for (
Bus* bus : busList) {
1420 const float voltage = std::abs(bus->GetElectricalData().voltage);
1421 float depth = 2.0f * (voltage - (maxVoltage + minVoltage) / 2.0f) / (maxVoltage - minVoltage);
1422 if (depth < -1.0) depth = -1.0;
1423 if (depth > 1.0) depth = 1.0;
1425 wxRect2DDouble rect = bus->GetRect();
1426 rect = wxRect2DDouble(
1427 (rect.m_x - 100.0f) * m_camera->GetScale() + m_camera->GetTranslation().m_x * m_camera->GetScale(),
1428 (rect.m_y - 50.0f) * m_camera->GetScale() + m_camera->GetTranslation().m_y * m_camera->GetScale(),
1429 (rect.m_width + 200.0f) * m_camera->GetScale(),
1430 (rect.m_height + 100.0f) * m_camera->GetScale());
1432 if (screenRect.Contains(rect))
1433 m_hmPlane->SetRectSlope(rect, M_PI *
static_cast<float>(bus->GetAngle()) / 180.0f, depth);
1439 int iterations = std::lround(10 * std::pow(m_camera->GetScale(), 2));
1442 if (iterations < 1) iterations = 1;
1443 m_hmPlane->SmoothPlane(iterations);
1447void Workspace::OnPopupClick(wxCommandEvent& event)
1449 wxWindow* dialogParent = wxGetTopLevelParent(
this);
1450 if (!dialogParent) dialogParent =
this;
1453 bool redrawHM =
false;
1455 wxMenu* menu =
static_cast<wxMenu*
>(
event.GetEventObject());
1457 int eventID =
event.GetId();
1460 if (element->
ShowForm(dialogParent, element,
this)) {
1461 CheckSlackBusDuplication(element);
1462 UpdateTextElements();
1466 if (m_workspacePanel) m_workspacePanel->SetFocus();
1469 Line* line =
static_cast<Line*
>(element);
1470 line->AddNode(m_camera->GetMousePosition());
1475 Line* line =
static_cast<Line*
>(element);
1476 line->RemoveNode(m_camera->GetMousePosition());
1482 for (
auto& iElement : m_elementList) {
1484 for (
int i = 0; i < (int)iElement->GetParentList().size(); i++) {
1486 if (parent == element) { iElement->RotateNode(parent); }
1495 for (
auto& iElement : m_elementList) {
1497 for (
int i = 0; i < (int)iElement->GetParentList().size(); i++) {
1499 if (parent == element) { iElement->RotateNode(parent,
false); }
1546 std::vector<Element*> childList = element->
GetChildList();
1547 for (
auto child : childList) {
1554 std::vector<Element*> parentList = element->
GetParentList();
1555 for (
auto parent : parentList) {
1560 std::erase_if(m_textList, [&](
const auto& text) {
1561 return text->GetElement() == element;
1564 std::erase_if(m_elementList, [&](
const auto& delElement) {
1565 return delElement.get() == element;
1568 menu->SetClientData(
nullptr);
1572 if (InsertTextElement(eventID, element)) {
1573 UpdateTextElements();
1578 if (redrawHM && m_hmPlane && m_showHM) {
1580 m_showHMTimer =
true;
1581 m_timerHeatMap->Start();
1585void Workspace::RotateSelectedElements(
bool clockwise)
1587 bool saveCurrrentState =
false;
1588 for (
auto& element : m_elementList) {
1590 for (
int i = 0; i < (int)element->
GetParentList().size(); i++) {
1597 element->
StartMove(m_camera->GetMousePosition());
1602 saveCurrrentState =
true;
1603 element->
Rotate(clockwise);
1604 element->
StartMove(m_camera->GetMousePosition());
1609 for (
auto& text : m_textList) {
1611 saveCurrrentState =
true;
1613 text->
StartMove(m_camera->GetMousePosition());
1616 if (saveCurrrentState) SaveCurrentState();
1618 if (m_hmPlane && m_showHM) {
1620 m_showHMTimer =
true;
1621 m_timerHeatMap->Start();
1626void Workspace::DeleteSelectedElements()
1629 for (
auto it = m_elementList.begin(); it != m_elementList.end();) {
1634 std::vector<Element*> childList = element->
GetChildList();
1635 for (
auto itc = childList.begin(), itEnd = childList.end(); itc != itEnd; ++itc) {
1642 std::vector<Element*> parentList = element->
GetParentList();
1643 for (
auto itp = parentList.begin(), itEnd = parentList.end(); itp != itEnd; ++itp) {
1658 std::erase_if(m_textList, [&](
const auto& text) {
1659 return text->GetElement() == element;
1662 it = m_elementList.erase(it);
1679 std::erase_if(m_textList, [](
const auto& text) {
1683 if (m_hmPlane && m_showHM) {
1685 m_showHMTimer =
true;
1686 m_timerHeatMap->Start();
1692bool Workspace::GetElementsCorners(wxPoint2DDouble& leftUpCorner,
1693 wxPoint2DDouble& rightDownCorner,
1694 std::vector<Element*> elementList)
1696 if (elementList.size() == 0)
return false;
1698 elementList[0]->CalculateBoundaries(leftUpCorner, rightDownCorner);
1700 for (
auto it = elementList.begin() + 1, itEnd = elementList.end(); it != itEnd; it++) {
1702 wxPoint2DDouble leftUp;
1703 wxPoint2DDouble rightDown;
1705 if (leftUp.m_x < leftUpCorner.m_x) leftUpCorner.m_x = leftUp.m_x;
1706 if (leftUp.m_y < leftUpCorner.m_y) leftUpCorner.m_y = leftUp.m_y;
1707 if (rightDown.m_x > rightDownCorner.m_x) rightDownCorner.m_x = rightDown.m_x;
1708 if (rightDown.m_y > rightDownCorner.m_y) rightDownCorner.m_y = rightDown.m_y;
1713void Workspace::Fit()
1715 wxPoint2DDouble leftUpCorner(0, 0);
1716 wxPoint2DDouble rightDownCorner(0, 0);
1717 std::vector<Element*> elementList = GetElementList();
1718 for (
const auto& text : m_textList) { elementList.push_back(text.get()); }
1720 if (!GetElementsCorners(leftUpCorner, rightDownCorner, elementList))
return;
1721 wxPoint2DDouble middleCoords = (leftUpCorner + rightDownCorner) / 2.0;
1725 GetSize(&width, &height);
1727 const double scaleX =
static_cast<double>(width) / (rightDownCorner.m_x - leftUpCorner.m_x);
1728 const double scaleY =
static_cast<double>(height) / (rightDownCorner.m_y - leftUpCorner.m_y);
1730 double scale = scaleX < scaleY ? scaleX : scaleY;
1731 scale = std::min(scale, m_camera->GetZoomMax());
1732 scale = std::max(scale, m_camera->GetZoomMin());
1734 m_camera->SetScale(scale);
1736 m_camera->StartTranslation(middleCoords);
1737 m_camera->SetTranslation(wxPoint2DDouble(width / 2.0, height / 2.0));
1739 if (m_hmPlane && m_showHM) {
1746bool Workspace::InsertTextElement(
int textID,
Element* parentElement, ElectricalUnit unit,
int precision)
1750 if (FindTextElement(parentElement, DATA_NAME))
return false;
1752 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(40, -30), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1753 newText->SetElement(parentElement);
1754 newText->SetDataType(DATA_NAME);
1755 newText->SetElementTypeText(parentElement->GetElementType());
1756 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1758 m_textList.push_back(newText);
1760 case ID_TXT_VOLTAGE: {
1761 if (FindTextElement(parentElement, DATA_VOLTAGE))
return false;
1763 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(40, 15), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1764 newText->SetElement(parentElement);
1765 newText->SetDataType(DATA_VOLTAGE);
1766 if (unit == ElectricalUnit::UNIT_NONE)
1767 newText->SetUnit(ElectricalUnit::UNIT_PU);
1769 newText->SetUnit(unit);
1770 newText->SetDecimalPlaces(precision);
1771 newText->SetElementTypeText(parentElement->GetElementType());
1772 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1774 m_textList.emplace_back(newText);
1776 case ID_TXT_ANGLE: {
1777 if (FindTextElement(parentElement, DATA_ANGLE))
return false;
1779 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(40, 30), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1780 newText->SetElement(parentElement);
1781 newText->SetDataType(DATA_ANGLE);
1782 if (unit == ElectricalUnit::UNIT_NONE)
1783 newText->SetUnit(ElectricalUnit::UNIT_DEGREE);
1785 newText->SetUnit(unit);
1786 newText->SetDecimalPlaces(precision);
1787 newText->SetElementTypeText(parentElement->GetElementType());
1788 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1790 m_textList.emplace_back(newText);
1792 case ID_TXT_FAULTCURRENT: {
1793 if (FindTextElement(parentElement, DATA_SC_CURRENT))
return false;
1795 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(-70, 30), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1796 newText->SetElement(parentElement);
1797 newText->SetDataType(DATA_SC_CURRENT);
1798 if (unit == ElectricalUnit::UNIT_NONE)
1799 newText->SetUnit(ElectricalUnit::UNIT_A);
1801 newText->SetUnit(unit);
1802 newText->SetDecimalPlaces(precision);
1803 newText->SetElementTypeText(parentElement->GetElementType());
1804 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1806 m_textList.emplace_back(newText);
1808 case ID_TXT_FAULTVOLTAGE: {
1809 if (FindTextElement(parentElement, DATA_SC_VOLTAGE))
return false;
1811 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(-70, 75), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1812 newText->SetElement(parentElement);
1813 newText->SetDataType(DATA_SC_VOLTAGE);
1814 if (unit == ElectricalUnit::UNIT_NONE)
1815 newText->SetUnit(ElectricalUnit::UNIT_PU);
1817 newText->SetUnit(unit);
1818 newText->SetDecimalPlaces(precision);
1819 newText->SetElementTypeText(parentElement->GetElementType());
1820 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1822 m_textList.emplace_back(newText);
1825 if (FindTextElement(parentElement, DATA_SC_POWER))
return false;
1826 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(-50, -30), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1827 newText->SetElement(parentElement);
1828 newText->SetDataType(DATA_SC_POWER);
1829 newText->SetUnit(ElectricalUnit::UNIT_MVA);
1830 if (unit == ElectricalUnit::UNIT_NONE)
1831 newText->SetUnit(ElectricalUnit::UNIT_MVA);
1833 newText->SetUnit(unit);
1834 newText->SetDecimalPlaces(precision);
1835 newText->SetElementTypeText(parentElement->GetElementType());
1836 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1838 m_textList.emplace_back(newText);
1841 if (FindTextElement(parentElement, DATA_PQ_THD))
return false;
1842 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(-50, -15), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1843 newText->SetElement(parentElement);
1844 newText->SetDataType(DATA_PQ_THD);
1845 newText->SetDecimalPlaces(precision);
1846 newText->SetElementTypeText(parentElement->GetElementType());
1847 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1849 m_textList.emplace_back(newText);
1851 case ID_TXT_ACTIVE_POWER: {
1852 if (FindTextElement(parentElement, DATA_ACTIVE_POWER))
return false;
1853 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(0, 35), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1854 newText->SetElement(parentElement);
1855 newText->SetDataType(DATA_ACTIVE_POWER);
1856 if (unit == ElectricalUnit::UNIT_NONE)
1857 newText->SetUnit(ElectricalUnit::UNIT_MW);
1859 newText->SetUnit(unit);
1860 newText->SetDecimalPlaces(precision);
1861 newText->SetElementTypeText(parentElement->GetElementType());
1862 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1864 m_textList.emplace_back(newText);
1866 case ID_TXT_REACTIVE_POWER: {
1867 if (FindTextElement(parentElement, DATA_REACTIVE_POWER))
return false;
1868 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(0, 50), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1869 newText->SetElement(parentElement);
1870 newText->SetDataType(DATA_REACTIVE_POWER);
1871 if (unit == ElectricalUnit::UNIT_NONE)
1872 newText->SetUnit(ElectricalUnit::UNIT_Mvar);
1874 newText->SetUnit(unit);
1875 newText->SetDecimalPlaces(precision);
1876 newText->SetElementTypeText(parentElement->GetElementType());
1877 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1879 m_textList.emplace_back(newText);
1881 case ID_TXT_BRANCH_ACTIVE_POWER_1_2:
1882 case ID_TXT_BRANCH_ACTIVE_POWER_2_1: {
1883 if (FindTextElement(parentElement, DATA_PF_ACTIVE))
return false;
1884 wxPoint2DDouble position(0.0, -10.0);
1885 if (textID == ID_TXT_BRANCH_ACTIVE_POWER_1_2)
1890 auto newText = std::make_shared<Text>(position, m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1891 newText->SetElement(parentElement);
1892 newText->SetDataType(DATA_PF_ACTIVE);
1893 if (unit == ElectricalUnit::UNIT_NONE)
1894 newText->SetUnit(ElectricalUnit::UNIT_MW);
1896 newText->SetUnit(unit);
1897 newText->SetDecimalPlaces(precision);
1898 newText->SetElementTypeText(parentElement->GetElementType());
1899 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1900 if (textID == ID_TXT_BRANCH_ACTIVE_POWER_2_1)
1901 newText->SetDirection(1);
1903 m_textList.emplace_back(newText);
1905 case ID_TXT_BRANCH_REACTIVE_POWER_1_2:
1906 case ID_TXT_BRANCH_REACTIVE_POWER_2_1: {
1907 if (FindTextElement(parentElement, DATA_PF_REACTIVE))
return false;
1908 wxPoint2DDouble position(0.0, 10.0);
1909 if (textID == ID_TXT_BRANCH_REACTIVE_POWER_1_2)
1914 auto newText = std::make_shared<Text>(position, m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1915 newText->SetElement(parentElement);
1916 newText->SetDataType(DATA_PF_REACTIVE);
1917 if (unit == ElectricalUnit::UNIT_NONE)
1918 newText->SetUnit(ElectricalUnit::UNIT_Mvar);
1920 newText->SetUnit(unit);
1921 newText->SetDecimalPlaces(precision);
1922 newText->SetElementTypeText(parentElement->GetElementType());
1923 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1924 if (textID == ID_TXT_BRANCH_REACTIVE_POWER_2_1)
1925 newText->SetDirection(1);
1927 m_textList.emplace_back(newText);
1929 case ID_TXT_BRANCH_LOSSES: {
1930 if (FindTextElement(parentElement, DATA_PF_LOSSES))
return false;
1932 auto newText = std::make_shared<Text>(position, m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1933 newText->SetElement(parentElement);
1934 newText->SetDataType(DATA_PF_LOSSES);
1935 if (unit == ElectricalUnit::UNIT_NONE)
1936 newText->SetUnit(ElectricalUnit::UNIT_MW);
1938 newText->SetUnit(unit);
1939 newText->SetDecimalPlaces(precision);
1940 newText->SetElementTypeText(parentElement->GetElementType());
1941 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1943 m_textList.emplace_back(newText);
1945 case ID_TXT_BRANCH_CURRENT_1_2:
1946 case ID_TXT_BRANCH_CURRENT_2_1: {
1947 if (FindTextElement(parentElement, DATA_PF_CURRENT))
return false;
1948 wxPoint2DDouble position(0.0, 10.0);
1949 if (textID == ID_TXT_BRANCH_CURRENT_1_2)
1954 auto newText = std::make_shared<Text>(position, m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1955 newText->SetElement(parentElement);
1956 newText->SetDataType(DATA_PF_CURRENT);
1957 if (unit == ElectricalUnit::UNIT_NONE)
1958 newText->SetUnit(ElectricalUnit::UNIT_A);
1960 newText->SetUnit(unit);
1961 newText->SetDecimalPlaces(precision);
1962 newText->SetElementTypeText(parentElement->GetElementType());
1963 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1964 if (textID == ID_TXT_BRANCH_CURRENT_2_1)
1965 newText->SetDirection(1);
1967 m_textList.emplace_back(newText);
1969 case ID_TXT_BRANCH_FAULT_CURRENT_1_2:
1970 case ID_TXT_BRANCH_FAULT_CURRENT_2_1: {
1971 if (FindTextElement(parentElement, DATA_SC_CURRENT))
return false;
1972 wxPoint2DDouble position(0.0, 25.0);
1973 if (textID == ID_TXT_BRANCH_FAULT_CURRENT_1_2)
1978 auto newText = std::make_shared<Text>(position, m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1979 newText->SetElement(parentElement);
1980 newText->SetDataType(DATA_SC_CURRENT);
1981 if (unit == ElectricalUnit::UNIT_NONE)
1982 newText->SetUnit(ElectricalUnit::UNIT_kA);
1984 newText->SetUnit(unit);
1985 newText->SetDecimalPlaces(precision);
1986 newText->SetElementTypeText(parentElement->GetElementType());
1987 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1988 if (textID == ID_TXT_BRANCH_FAULT_CURRENT_2_1)
1989 newText->SetDirection(1);
1991 m_textList.emplace_back(newText);
2000Element* Workspace::FindTextElement(
Element* parentElement,
int dataType)
2002 for (
auto& text : m_textList) {
2003 if (text->GetElement() == parentElement && text->GetDataType() == dataType)
2009void Workspace::RemoveAllTextElements()
2018void Workspace::CheckSlackBusDuplication(
Element* newSlackBus)
2020 Bus* newBus =
dynamic_cast<Bus*
>(newSlackBus);
2022 if (!newBus->GetElectricalData().slackBus)
return;
2024 for (
auto& element : m_elementList) {
2025 Bus* bus =
dynamic_cast<Bus*
>(element.get());
2027 if (bus->GetElectricalData().slackBus && bus != newSlackBus) {
2028 wxMessageDialog msgDialog(
this,
2029 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),
2030 _(
"Warning"), wxYES_NO | wxCENTRE | wxICON_WARNING);
2031 if (msgDialog.ShowModal() == wxID_YES) {
2032 auto data = bus->GetElectricalData();
2033 data.slackBus =
false;
2034 bus->SetElectricalData(data);
2038 auto data = newBus->GetElectricalData();
2039 data.slackBus =
false;
2040 newBus->SetElectricalData(data);
2048void Workspace::ValidateBusesVoltages(
Element* initialBus)
2050 double nominalVoltage =
static_cast<Bus*
>(initialBus)->GetElectricalData().nominalVoltage;
2051 ElectricalUnit nominalVoltageUnit =
static_cast<Bus*
>(initialBus)->GetElectricalData().nominalVoltageUnit;
2053 for (
auto it = m_elementList.begin(); it != m_elementList.end(); it++) {
2056 if (
auto line =
dynamic_cast<Line*
>(child)) {
2061 if (data1.nominalVoltage != data2.nominalVoltage ||
2062 data1.nominalVoltageUnit != data2.nominalVoltageUnit) {
2063 data1.nominalVoltage = nominalVoltage;
2064 data2.nominalVoltage = nominalVoltage;
2065 data1.nominalVoltageUnit = nominalVoltageUnit;
2066 data2.nominalVoltageUnit = nominalVoltageUnit;
2071 it = m_elementList.begin();
2080void Workspace::ValidateElementsVoltages()
2082 for (
auto& child : m_elementList) {
2083 std::vector<double> nominalVoltage;
2084 std::vector<ElectricalUnit> nominalVoltageUnit;
2085 for (
int i = 0; i < (int)child->
GetParentList().size(); i++) {
2088 nominalVoltage.push_back(parent->GetElectricalData().nominalVoltage);
2089 nominalVoltageUnit.push_back(parent->GetElectricalData().nominalVoltageUnit);
2092 child->SetNominalVoltage(nominalVoltage, nominalVoltageUnit);
2096void Workspace::ResetAllVoltages()
2100 UpdateTextElements();
2104bool Workspace::RunPowerFlow(
bool resetVoltages,
bool showBusyInfo)
2106 auto simProp = m_properties->GetSimulationPropertiesData();
2107 double basePower = simProp.basePower;
2108 if (simProp.basePowerUnit == ElectricalUnit::UNIT_MVA)
2110 else if (simProp.basePowerUnit == ElectricalUnit::UNIT_kVA)
2114 for (
auto& element : m_elementList) {
2115 if (
auto emtElement =
dynamic_cast<EMTElement*
>(element.get())) {
2116 if (emtElement->IsOnline()) {
2117 emtElement->UpdateData(m_properties,
true);
2121 bool result =
false;
2122 wxString errorMsg =
"";
2127 wxBusyInfo* info =
nullptr;
2129 info =
new wxBusyInfo(
2132 .Icon(wxIcon(Paths::GetDataPath() +
"/images/ribbon/powerFLow32.png", wxBITMAP_TYPE_PNG))
2133 .Title(_(
"<b>Calculating Power Flow</b>"))
2134 .
Text(_(
"Please wait..."))
2135 .Foreground(*wxWHITE)
2136 .Background(*wxBLACK)
2137 .Transparency(4 * wxALPHA_OPAQUE / 5)
2152 if (resetVoltages) pf.ResetVoltages();
2154 switch (simProp.powerFlowMethod) {
2155 case GAUSS_SEIDEL: {
2156 result = pf.RunGaussSeidel(basePower, simProp.powerFlowMaxIterations, simProp.powerFlowTolerance,
2157 simProp.initAngle, simProp.accFator);
2159 case NEWTON_RAPHSON: {
2160 result = pf.RunNewtonRaphson(basePower, simProp.powerFlowMaxIterations, simProp.powerFlowTolerance,
2161 simProp.initAngle, simProp.newtonInertia);
2163 case GAUSS_NEWTON: {
2165 pf.RunGaussNewton(basePower, simProp.powerFlowMaxIterations, simProp.powerFlowTolerance,
2166 simProp.initAngle, simProp.accFator, simProp.gaussTolerance, simProp.newtonInertia);
2170 errorMsg = pf.GetErrorMessage();
2171 numIt = pf.GetIterations();
2173 if (showBusyInfo)
delete info;
2178 wxMessageDialog msgDialog(
this, errorMsg, _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
2179 msgDialog.ShowModal();
2182 m_statusBar->SetStatusText(
2183 wxString::Format(_(
"Power flow converge with %d iterations (%ld ms)"), numIt, sw.Time()));
2186 UpdateTextElements();
2193bool Workspace::UpdateTextElements()
2195 bool isTexturesOK =
true;
2196 double basePower = m_properties->GetSimulationPropertiesData().basePower;
2197 if (m_properties->GetSimulationPropertiesData().basePowerUnit == ElectricalUnit::UNIT_kVA)
2199 else if (m_properties->GetSimulationPropertiesData().basePowerUnit == ElectricalUnit::UNIT_MVA)
2201 for (
auto& text : m_textList) {
2202 text->SetFontName(m_properties->GetGeneralPropertiesData().labelFont);
2203 text->SetFontSize(m_properties->GetGeneralPropertiesData().labelFontSize);
2204 text->UpdateText(basePower);
2207 return isTexturesOK;
2210void Workspace::CopySelection()
2213 std::vector<Element*> selectedElements;
2216 for (
auto& element : m_elementList) {
2217 if (
auto bus =
dynamic_cast<Bus*
>(element.get())) {
2218 auto data = bus->GetElectricalData();
2219 data.number = busNumber;
2220 bus->SetElectricalData(data);
2223 if (element->
IsSelected()) { selectedElements.push_back(element.get()); }
2225 for (
auto& text : m_textList) {
2226 if (text->
IsSelected()) { selectedElements.push_back(text.get()); }
2229 if (wxTheClipboard->Open()) {
2230 wxTheClipboard->SetData(dataObject);
2231 wxTheClipboard->Close();
2235bool Workspace::Paste()
2237 if (wxTheClipboard->Open()) {
2240 if (wxTheClipboard->IsSupported(dataObject.GetFormat())) {
2241 if (!wxTheClipboard->GetData(dataObject)) {
2242 wxMessageDialog dialog(
this, _(
"It was not possible to paste from clipboard."), _(
"Error"),
2243 wxOK | wxCENTER | wxICON_ERROR, wxDefaultPosition);
2245 wxTheClipboard->Close();
2250 wxTheClipboard->Close();
2253 wxTheClipboard->Close();
2257 std::vector<Element*> pastedElements;
2258 ElementsLists* elementsLists = dataObject.GetElementsLists();
2261 auto parentList = elementsLists->parentList;
2262 std::vector<Bus*> pastedBusList;
2263 for (
auto it = parentList.begin(), itEnd = parentList.end(); it != itEnd; ++it) {
2266 pastedElements.push_back(copy);
2267 pastedBusList.push_back(
static_cast<Bus*
>(copy));
2268 m_elementList.emplace_back(
static_cast<PowerElement*
>(copy));
2273 auto elementLists = elementsLists->elementList;
2274 for (
auto it = elementLists.begin(), itEnd = elementLists.end(); it != itEnd; ++it) {
2278 if (
Text* text =
dynamic_cast<Text*
>(copy)) {
2280 bool elementExist =
false;
2281 for (
auto& element : m_elementList) {
2282 if (text->GetElement() == element.get()) {
2283 elementExist =
true;
2288 pastedElements.push_back(copy);
2289 m_textList.emplace_back(text);
2296 if (currentParent) {
2297 int parentID = currentParent->
GetID();
2298 bool parentCopied =
false;
2299 for (
size_t k = 0; k < pastedBusList.size(); k++) {
2300 Bus* newParent = pastedBusList[k];
2301 if (parentID == newParent->
GetID()) {
2302 parentCopied =
true;
2311 pastedElements.push_back(copy);
2312 m_elementList.emplace_back(
static_cast<PowerElement*
>(copy));
2318 for (
auto it = pastedBusList.begin(), itEnd = pastedBusList.end(); it != itEnd; ++it) {
2321 for (
auto it = childList.begin(), itEnd = childList.end(); it != itEnd; ++it) {
2323 int childID = currentChild->
GetID();
2324 bool childCopied =
false;
2325 for (
int i = 0; i < (int)pastedElements.size(); i++) {
2326 Element* newChild = pastedElements[i];
2327 if (childID == newChild->
GetID()) {
2339 wxPoint2DDouble leftUpCorner, rightDownCorner;
2340 GetElementsCorners(leftUpCorner, rightDownCorner, pastedElements);
2341 wxPoint2DDouble startPosition = (leftUpCorner + rightDownCorner) / 2.0;
2342 for (
auto it = pastedElements.begin(), itEnd = pastedElements.end(); it != itEnd; ++it) {
2345 element->
Move(m_camera->GetMousePosition());
2346 for (
int i = 0; i < (int)element->
GetParentList().size(); i++) {
2348 element->
MoveNode(parent, m_camera->GetMousePosition());
2353 wxMessageDialog dialog(
this, _(
"It was not possible to paste from clipboard."), _(
"Error"),
2354 wxOK | wxCENTER | wxICON_ERROR, wxDefaultPosition);
2360 m_mode = WorkspaceMode::MODE_PASTE;
2361 m_statusBar->SetStatusText(_(
"Click to paste."));
2367void Workspace::SaveCurrentState()
2374 std::vector< std::shared_ptr<PowerElement> > currentStateElementList;
2375 std::vector< std::shared_ptr<Text> > currentStateTextList;
2377 GetStateListsCopy(m_elementList, m_textList, currentStateElementList, currentStateTextList);
2390 m_elementListState.resize(m_currenteState + 1);
2399 m_textListState.resize(m_currenteState + 1);
2402 if (m_currenteState >= m_maxStates) {
2403 m_currenteState = m_maxStates - 1;
2416 m_elementListState.erase(m_elementListState.begin());
2417 m_textListState.erase(m_textListState.begin());
2420 m_elementListState.emplace_back(currentStateElementList);
2421 m_textListState.emplace_back(currentStateTextList);
2432 wxString pointerStr;
2433 pointerStr.Printf(
"[%d S saved s%d] ", m_currenteState, m_elementListState.size());
2435 for (
auto& element : currentStateElementList) {
2436 pointerStr.Printf(
"%p ", element.get());
2440 pointerStr.Printf(
"[%d S curr s%d] ", m_currenteState, m_elementListState.size());
2442 for (
auto& element : m_elementList) {
2443 pointerStr.Printf(
"%p ", element.get());
2451void Workspace::SetNextState()
2454 if (m_currenteState >= 0 &&
2455 static_cast<size_t>(m_currenteState) < m_elementListState.size() &&
2456 static_cast<size_t>(m_currenteState) < m_textListState.size()) {
2459 GetStateListsCopy(m_elementListState[m_currenteState], m_textListState[m_currenteState], m_elementList, m_textList);
2462#ifdef SHOW_DEBUG_PANEL
2464 wxString pointerStr;
2465 pointerStr.Printf(
"[%d N curr s%d] ", m_currenteState, m_elementListState.size());
2467 for (
Element* element : m_elementList) {
2468 pointerStr.Printf(
"%p ", element);
2476 UpdateTextElements();
2484void Workspace::SetPreviousState()
2487 if (m_currenteState >= 0) {
2490 GetStateListsCopy(m_elementListState[m_currenteState], m_textListState[m_currenteState], m_elementList, m_textList);
2493#ifdef SHOW_DEBUG_PANEL
2495 wxString pointerStr;
2496 pointerStr.Printf(
"[%d P curr s%d] ", m_currenteState, m_elementListState.size());
2498 for (
Element* element : m_elementListState[m_currenteState]) {
2499 pointerStr.Printf(
"%p ", element);
2503 pointerStr.Printf(
"[%d P list s%d] ", m_currenteState, m_elementListState.size());
2505 for (
Element* element : m_elementList) {
2506 pointerStr.Printf(
"%p ", element);
2514 UpdateTextElements();
2522void Workspace::UnselectAll()
2524 for (
auto& element : m_elementList) {
2527 for (
auto& text : m_textList) {
2532void Workspace::EnableHeatMap(
const bool& enable)
2541void Workspace::UpdateElementsID()
2544 for (
auto& element : m_elementList) {
2548 for (
auto& text : m_textList) {
2553void Workspace::OnTimer(wxTimerEvent& event)
2556 m_tipWindow->Close();
2557 m_tipWindow =
nullptr;
2559 if (m_mode == WorkspaceMode::MODE_EDIT) {
2560 for (
auto& element : m_elementList) {
2561 if (element->
Contains(m_camera->GetMousePosition())) {
2563 if (!tipText.IsEmpty()) {
2565 m_tipWindow = wxTipWindow::New(
this, tipText, 10000);
2567 m_tipWindow->SetBoundingRect(wxRect(wxGetMousePosition(), wxSize(1, 1)));
2577void Workspace::SetTextList(
const std::vector< std::shared_ptr<Text> >& textList)
2581 m_textList = std::move(textList);
2583 UpdateTextElements();
2586void Workspace::SetName(wxString name)
2590#ifdef SHOW_DEBUG_PANEL
2591 m_debugFrame->SetTitle(_(
"Debug window: ") + m_name);
2596void Workspace::SetElementList(std::vector< std::shared_ptr<PowerElement> > elementList)
2598 m_elementList = std::move(elementList);
2601void Workspace::SetElementList(std::vector<Element*> elementList)
2603 m_elementList.clear();
2604 for (
auto it = elementList.begin(), itEnd = elementList.end(); it != itEnd; ++it)
2605 m_elementList.emplace_back(
static_cast<PowerElement*
>(*it));
2608void Workspace::OnIdle(wxIdleEvent& event)
2630 m_justOpened =
false;
2631 double limits[2] = { 1.05, 0.95 };
2632 m_hmPlane =
new HMPlane(m_width, m_height, limits);
2638std::vector<Element*> Workspace::GetAllElements()
const
2640 std::vector<Element*> allElements;
2642 for (
auto& element : m_elementList) allElements.push_back(element.get());
2643 for (
auto& text : m_textList) allElements.push_back(text.get());
2648bool Workspace::RunFault()
2650 auto simProp = m_properties->GetSimulationPropertiesData();
2651 double basePower = simProp.basePower;
2652 if (simProp.basePowerUnit == ElectricalUnit::UNIT_MVA)
2654 else if (simProp.basePowerUnit == ElectricalUnit::UNIT_kVA)
2657 Fault fault(GetElementList());
2658 bool result = fault.RunFaultCalculation(basePower);
2660 wxMessageDialog msgDialog(
this, fault.GetErrorMessage(), _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
2661 msgDialog.ShowModal();
2664 UpdateTextElements();
2670std::vector<Element*> Workspace::GetElementList()
const
2672 std::vector<Element*> elementList;
2673 for (
auto& element : m_elementList) elementList.push_back(element.get());
2677bool Workspace::RunSCPower()
2679 Fault fault(GetElementList());
2680 bool result = fault.RunSCPowerCalcutation(100e6);
2682 wxMessageDialog msgDialog(
this, fault.GetErrorMessage(), _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
2683 msgDialog.ShowModal();
2686 UpdateTextElements();
2692bool Workspace::RunStability()
2697 Electromechanical stability(
this, GetElementList(), m_properties->GetSimulationPropertiesData());
2699 bool result = stability.RunStabilityCalculation();
2701#ifdef SHOW_DEBUG_PANEL
2702 m_debugFrame->AppendDebugMessage(stability.GetDebugMessage());
2708 wxMessageDialog msgDialog(
this, stability.GetErrorMessage(), _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
2709 msgDialog.ShowModal();
2711 m_stabilityTimeVector.clear();
2712 m_stabilityTimeVector = stability.GetTimeVector();
2717 wxMessageDialog msgDialog(
2719 wxString::Format(_(
"The program took %ld ms to run this system.\nDo you wish to open the stability graphics?"),
2721 _(
"Question"), wxYES_NO | wxCENTRE | wxICON_QUESTION);
2722 if (msgDialog.ShowModal() == wxID_YES) {
2723 std::vector<ElementPlotData> plotDataList;
2724 for (
auto& element : m_elementList) {
2726 if (element->GetPlotData(plotData)) plotDataList.push_back(plotData);
2728#ifdef SHOW_SIMULATION_PARAMETERS
2730 plotData.SetName(_(
"Simulation parameters"));
2731 plotData.SetCurveType(ElementPlotData::CurveType::CT_TEST);
2732 plotData.AddData(stability.GetIterationVector(), _(
"Iterations number"));
2733 plotDataList.push_back(plotData);
2735 ChartView* cView =
new ChartView(
this, plotDataList, m_stabilityTimeVector, m_properties->GetGeneralPropertiesData().plotLib);
2741void Workspace::OnMiddleDoubleClick(wxMouseEvent& event)
2747bool Workspace::RunStaticStudies()
2749 bool pfStatus, faultStatus, scStatus, harmStatus;
2750 pfStatus = faultStatus = scStatus = harmStatus =
false;
2752 bool runHarmDistortion = m_properties->GetSimulationPropertiesData().harmDistortionAfterPowerFlow;
2754 pfStatus = RunPowerFlow(runHarmDistortion);
2756 if (m_properties->GetSimulationPropertiesData().faultAfterPowerFlow) {
2757 if (pfStatus) faultStatus = RunFault();
2763 if (m_properties->GetSimulationPropertiesData().scPowerAfterPowerFlow) {
2764 if (pfStatus) scStatus = RunSCPower();
2770 if (runHarmDistortion) {
2771 if (pfStatus) harmStatus = RunHarmonicDistortion(
false);
2777 if (pfStatus && faultStatus && scStatus && harmStatus)
return true;
2782bool Workspace::RunHarmonicDistortion(
bool runPowerFlowBefore)
2784 auto simProp = m_properties->GetSimulationPropertiesData();
2785 double basePower = simProp.basePower;
2786 if (simProp.basePowerUnit == ElectricalUnit::UNIT_MVA)
2788 else if (simProp.basePowerUnit == ElectricalUnit::UNIT_kVA)
2790 if (runPowerFlowBefore) {
2791 if (!RunPowerFlow(
true))
return false;
2794 bool hasEMTElement =
false;
2795 for (
auto& element : m_elementList) {
2796 if (
auto emtElement =
dynamic_cast<EMTElement*
>(element.get())) {
2797 if (emtElement->IsOnline()) hasEMTElement =
true;
2801 HarmLoadConnection loadConnection = simProp.harmLoadConnection;
2804 bool result = pq.CalculateDistortions(basePower, loadConnection);
2807 if (hasEMTElement && result) {
2811 .Icon(wxIcon(Paths::GetDataPath() +
"/images/ribbon/harmDist32.png", wxBITMAP_TYPE_PNG))
2812 .Title(_(
"<b>Calculating Harmonic Flow</b>"))
2813 .
Text(_(
"Please wait..."))
2814 .Foreground(*wxWHITE)
2815 .Background(*wxBLACK)
2816 .Transparency(4 * wxALPHA_OPAQUE / 5)
2818 std::vector<double> thdList;
2819 for (
auto const& bus : pq.GetBusList())
2820 thdList.emplace_back(bus->GetElectricalData().thd);
2822 while (error > 1e-3) {
2824 if (!RunPowerFlow(
false,
false))
return false;
2827 bool result = pq.CalculateDistortions(basePower, loadConnection);
2832 for (
auto const& bus : pq.GetBusList()) {
2833 double errorBus = std::abs(bus->GetElectricalData().thd - thdList[i]);
2836 else if (errorBus > error)
2838 thdList[i] = bus->GetElectricalData().thd;
2845 wxMessageDialog msgDialog(
this, pq.GetErrorMessage(), _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
2846 msgDialog.ShowModal();
2849 UpdateTextElements();
2856bool Workspace::RunFrequencyResponse()
2859 std::vector<Bus*> busList;
2860 for (
auto& element : m_elementList) {
2861 if (
Bus* bus =
dynamic_cast<Bus*
>(element.get())) { busList.push_back(bus); }
2864 auto data = m_properties->GetFreqRespData();
2866 FrequencyResponseForm frForm(
this, busList, data.injBusNumber, data.initFreq, data.finalFreq, data.stepFreq);
2868 if (frForm.ShowModal() == wxID_OK) {
2869 data.initFreq = frForm.GetInitFreq();
2870 data.finalFreq = frForm.GetEndFreq();
2871 data.stepFreq = frForm.GetStepFreq();
2872 data.injBusNumber = frForm.GetInjBusNumber();
2873 m_properties->SetFreqRespData(data);
2878 auto simProp = m_properties->GetSimulationPropertiesData();
2879 double basePower = simProp.basePower;
2880 if (simProp.basePowerUnit == ElectricalUnit::UNIT_MVA)
2882 else if (simProp.basePowerUnit == ElectricalUnit::UNIT_kVA)
2885 bool result = pq.CalculateFrequencyResponse(simProp.stabilityFrequency, data.initFreq, data.finalFreq,
2886 data.stepFreq, data.injBusNumber, basePower, simProp.harmLoadConnection);
2888 wxMessageDialog msgDialog(
2889 this, wxString::Format(_(
"Calculations done.\nDo you wish to open the frequency response graphics?")),
2890 _(
"Question"), wxYES_NO | wxCENTRE | wxICON_QUESTION);
2891 if (msgDialog.ShowModal() == wxID_YES) {
2892 std::vector<ElementPlotData> plotDataList;
2893 for (
auto& element : m_elementList) {
2898 ChartView* cView =
new ChartView(
this, plotDataList, pq.GetFrequencies(), m_properties->GetGeneralPropertiesData().plotLib);
2902 UpdateTextElements();
2907void Workspace::OnResize(wxSizeEvent& event)
2909 m_width =
static_cast<float>(GetSize().x) - 1.0f;
2910 m_height =
static_cast<float>(GetSize().y) - 1.0f;
2912 if (m_hmPlane && m_showHM) {
2913 m_hmPlane->ResizeDC(m_width, m_height);
2914 m_showHMTimer =
true;
2915 m_timerHeatMap->Start();
2920void Workspace::OnHeatMapTime(wxTimerEvent& event)
2922 if (m_showHMTimer) {
2925 m_showHMTimer =
false;
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.