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);
195 for (
auto& element : m_elementList) {
199 element->DrawDC(m_properties->GetGUIColour(), m_camera->GetTranslation(), m_camera->GetScale(), gc);
210 Text* text =
new Text(wxPoint2DDouble(0.0, 0.0), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
212 text->DrawDC(m_properties->GetGUIColour(), m_camera->GetTranslation(), m_camera->GetScale(), gc);
219 for (
auto& text : m_textList) {
223 text->DrawDC(m_properties->GetGUIColour(), m_camera->GetTranslation(), m_camera->GetScale(), gc);
233 wxColour selectionColour = m_properties->GetGUIColour()->selection;
234 gc->SetPen(wxPen(selectionColour));
235 gc->SetBrush(wxBrush(selectionColour));
236 gc->DrawRectangle(m_selectionRect.m_x, m_selectionRect.m_y, m_selectionRect.m_width, m_selectionRect.m_height);
241 if (m_hmPlane && m_showHM) {
245 m_hmPlane->DrawLabelDC(gc);
253void Workspace::DrawScene(wxDC& dc)
256 if (m_hmPlane && m_showHM) {
257 m_hmPlane->DrawDC(dc);
260 dc.SetUserScale(m_camera->GetScale(), m_camera->GetScale());
261 dc.SetDeviceOrigin(m_camera->GetTranslation().m_x * m_camera->GetScale(), m_camera->GetTranslation().m_y * m_camera->GetScale());
267 for (
auto& element : m_elementList) {
268 element->DrawDC(m_properties->GetGUIColour(), m_camera->GetTranslation(), m_camera->GetScale(), dc);
273 Text* text =
new Text(wxPoint2DDouble(0.0, 0.0), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
275 text->DrawDC(m_properties->GetGUIColour(), m_camera->GetTranslation(), m_camera->GetScale(), dc);
279 for (
auto& text : m_textList) {
280 text->DrawDC(m_properties->GetGUIColour(), m_camera->GetTranslation(), m_camera->GetScale(), dc);
283 if (m_hmPlane && m_showHM) {
284 m_hmPlane->DrawLabelDC(dc);
288wxPoint2DDouble Workspace::SnapToGrid(
const wxPoint2DDouble& pt)
const
290 if (!m_showGrid || m_gridSize <= 0.0)
return pt;
291 return wxPoint2DDouble(
292 std::round(pt.m_x / m_gridSize) * m_gridSize,
293 std::round(pt.m_y / m_gridSize) * m_gridSize
297void Workspace::DrawGrid(wxGraphicsContext* gc)
299 if (!m_showGrid || !gc)
return;
301 wxSize clientSize = m_workspacePanel ? m_workspacePanel->GetClientSize() : wxSize(wxRound(m_width), wxRound(m_height));
302 if (clientSize.GetWidth() <= 0 || clientSize.GetHeight() <= 0)
return;
304 wxPoint2DDouble tl = m_camera->ScreenToWorld(wxPoint2DDouble(0, 0));
305 wxPoint2DDouble br = m_camera->ScreenToWorld(wxPoint2DDouble(clientSize.GetWidth(), clientSize.GetHeight()));
307 double minX = std::min(tl.m_x, br.m_x);
308 double maxX = std::max(tl.m_x, br.m_x);
309 double minY = std::min(tl.m_y, br.m_y);
310 double maxY = std::max(tl.m_y, br.m_y);
312 double step = m_gridSize;
313 if (step <= 0.0) step = 20.0;
314 while (step * m_camera->GetScale() < 12.0) {
318 double startX = std::floor(minX / step) * step;
319 double endX = std::ceil(maxX / step) * step;
320 double startY = std::floor(minY / step) * step;
321 double endY = std::ceil(maxY / step) * step;
323 wxGraphicsPath path = gc->CreatePath();
325 for (
double x = startX; x <= endX + (step * 0.1); x += step) {
326 path.MoveToPoint(x, minY);
327 path.AddLineToPoint(x, maxY);
330 for (
double y = startY; y <= endY + (step * 0.1); y += step) {
331 path.MoveToPoint(minX, y);
332 path.AddLineToPoint(maxX, y);
335 wxColour gridColour = m_properties->GetGUIColour()->grid;
336 gc->SetPen(wxPen(gridColour, 1.0));
337 gc->StrokePath(path);
340void Workspace::DrawGrid(wxDC& dc)
342 if (!m_showGrid)
return;
344 wxSize clientSize = m_workspacePanel ? m_workspacePanel->GetClientSize() : wxSize(wxRound(m_width), wxRound(m_height));
345 if (clientSize.GetWidth() <= 0 || clientSize.GetHeight() <= 0)
return;
347 wxPoint2DDouble tl = m_camera->ScreenToWorld(wxPoint2DDouble(0, 0));
348 wxPoint2DDouble br = m_camera->ScreenToWorld(wxPoint2DDouble(clientSize.GetWidth(), clientSize.GetHeight()));
350 double minX = std::min(tl.m_x, br.m_x);
351 double maxX = std::max(tl.m_x, br.m_x);
352 double minY = std::min(tl.m_y, br.m_y);
353 double maxY = std::max(tl.m_y, br.m_y);
355 double step = m_gridSize;
356 if (step <= 0.0) step = 20.0;
357 while (step * m_camera->GetScale() < 12.0) {
361 double startX = std::floor(minX / step) * step;
362 double endX = std::ceil(maxX / step) * step;
363 double startY = std::floor(minY / step) * step;
364 double endY = std::ceil(maxY / step) * step;
366 wxColour gridColour = m_properties->GetGUIColour()->grid;
367 dc.SetPen(wxPen(gridColour, 1, wxPENSTYLE_SOLID));
369 for (
double x = startX; x <= endX + (step * 0.1); x += step) {
370 dc.DrawLine(wxRound(x), wxRound(minY), wxRound(x), wxRound(maxY));
373 for (
double y = startY; y <= endY + (step * 0.1); y += step) {
374 dc.DrawLine(wxRound(minX), wxRound(y), wxRound(maxX), wxRound(y));
378void Workspace::AlignSelectedToGrid()
380 bool anySelected =
false;
381 for (
const auto& elem : m_elementList) {
382 if (elem->IsSelected()) {
388 for (
const auto& txt : m_textList) {
389 if (txt->IsSelected()) {
396 double gs = m_gridSize > 0.0 ? m_gridSize : 20.0;
399 for (
auto& elem : m_elementList) {
400 if (elem->GetElementType() == TYPE_BUS) {
401 if (!anySelected || elem->IsSelected()) {
402 wxPoint2DDouble oldPos = elem->GetPosition();
403 elem->AlignToGrid(gs);
404 wxPoint2DDouble delta = elem->GetPosition() - oldPos;
405 if (std::abs(delta.m_x) > 1e-4 || std::abs(delta.m_y) > 1e-4) {
406 for (
auto& child : elem->GetChildList()) {
407 if (
auto powerElem =
dynamic_cast<PowerElement*
>(child)) {
408 auto& ptList = powerElem->GetPointListRef();
409 auto parentList = powerElem->GetParentList();
410 if (parentList.size() > 0 && parentList[0] == elem.get() && ptList.size() > 0) {
413 if (parentList.size() > 1 && parentList[1] == elem.get() && ptList.size() > 1) {
414 ptList.back() += delta;
416 powerElem->UpdateSwitchesPosition();
417 powerElem->UpdatePowerFlowArrowsPosition();
426 for (
auto& elem : m_elementList) {
427 if (elem->GetElementType() != TYPE_BUS) {
428 if (!anySelected || elem->IsSelected()) {
429 elem->AlignToGrid(gs);
435 for (
auto& txt : m_textList) {
436 if (!anySelected || txt->IsSelected()) {
437 txt->AlignToGrid(gs);
445void Workspace::CopyToClipboard()
447 wxSize size = GetClientSize();
451 wxBitmap bitmap(size.x * scale, size.y * scale);
452 wxMemoryDC memDC(bitmap);
454 memDC.SetBackground(*wxWHITE_BRUSH);
457 wxGraphicsContext* gc = wxGraphicsContext::Create(memDC);
459 gc->Scale(scale, scale);
464 memDC.SelectObject(wxNullBitmap);
466 if (wxTheClipboard->Open())
468 wxTheClipboard->SetData(
new wxBitmapDataObject(bitmap));
469 wxTheClipboard->Close();
473void Workspace::ExportAsSVG(wxString path)
475 wxSize size = GetClientSize();
477 wxSVGFileDC svgDC(path, size.x, size.y);
479 svgDC.SetClippingRegion(wxRect(0, 0, size.x, size.y));
484void Workspace::OnLeftClickDown(wxMouseEvent& event)
486 wxWindow* dialogParent = wxGetTopLevelParent(
this);
487 if (!dialogParent) dialogParent =
this;
490 wxPoint clickPoint =
event.GetPosition();
491 bool foundElement =
false;
493 bool showNewElementForm =
false;
494 bool clickOnSwitch =
false;
495 bool unselectAll =
true;
496 std::vector<Element*> notUnselectElementList;
497 std::vector<Text*> notUnselectTextList;
499 if (m_mode == WorkspaceMode::MODE_INSERT_TEXT || m_mode == WorkspaceMode::MODE_PASTE || m_mode == WorkspaceMode::MODE_DRAG_PASTE) {
500 m_mode = WorkspaceMode::MODE_EDIT;
503 else if (m_mode == WorkspaceMode::MODE_INSERT || m_mode == WorkspaceMode::MODE_DRAG_INSERT || m_mode == WorkspaceMode::MODE_DRAG_INSERT_TEXT) {
504 wxPoint2DDouble clickPointWorld = m_camera->ScreenToWorld(clickPoint);
505 if (m_showGrid) clickPointWorld = SnapToGrid(clickPointWorld);
507 if (!m_elementList.empty()) {
509 newElement = m_elementList.back().get();
510 for (
auto& element : m_elementList) {
512 if (element->Contains(clickPointWorld)) {
514 if (
auto bus =
dynamic_cast<Bus*
>(element.get())) {
520 if (newElement->
AddParent(bus, clickPointWorld)) {
521 ValidateElementsVoltages();
523 showNewElementForm =
true;
524 m_mode = WorkspaceMode::MODE_EDIT;
533 if (
auto line =
dynamic_cast<Line*
>(newElement)) { line->AddPoint(clickPointWorld); }
541 bool clickPickbox =
false;
543 for (
auto& element : m_elementList) {
544 element->ResetPickboxes();
547 element->StartMove(m_camera->ScreenToWorld(clickPoint));
550 if (element->NodeContains(m_camera->ScreenToWorld(clickPoint)) != 0 && element->IsSelected()) {
551 m_mode = WorkspaceMode::MODE_MOVE_NODE;
552 m_disconnectedElement =
true;
555 if (m_hmPlane && m_showHM) {
561 else if (element->Contains(m_camera->ScreenToWorld(clickPoint))) {
562 notUnselectElementList.emplace_back(element.get());
564 if (element->IsSelected()) unselectAll =
false;
566 element->SetSelected();
567 element->ShowPickbox();
570 for (
auto& text : m_textList) {
571 if (text->GetElement() == element.get()) {
572 notUnselectTextList.emplace_back(text.get());
574 text->SetAllowRotation(
false);
575 if (unselectAll) text->SetAltSelectionColour();
580 if (element->PickboxContains(m_camera->ScreenToWorld(clickPoint))) {
581 m_mode = WorkspaceMode::MODE_MOVE_PICKBOX;
585 if (!clickPickbox) { m_mode = WorkspaceMode::MODE_MOVE_ELEMENT; }
587 if (m_hmPlane && m_showHM) {
593 else if (element->SwitchesContains(m_camera->ScreenToWorld(clickPoint))) {
594 element->SetOnline(element->IsOnline() ?
false : true);
595 clickOnSwitch =
true;
600 for (
auto& text : m_textList) {
601 text->
StartMove(m_camera->ScreenToWorld(clickPoint));
603 if (text->
Contains(m_camera->ScreenToWorld(clickPoint))) {
604 notUnselectTextList.emplace_back(text.get());
608 text->SetAltSelectionColour(
false);
609 text->SetAllowRotation();
610 m_mode = WorkspaceMode::MODE_MOVE_ELEMENT;
612 if (m_hmPlane && m_showHM) {
621 if (!event.ControlDown() && unselectAll) {
622 for (
auto& element : m_elementList) {
624 for (
Element* notUnselectElement : notUnselectElementList) {
625 if (notUnselectElement == element.get()) select =
true;
627 element->SetSelected(select);
629 for (
auto& text : m_textList) {
631 for (
auto& notUnselectText : notUnselectTextList) {
632 if (notUnselectText == text.get()) select =
true;
638 if (!foundElement && !clickOnSwitch) {
639 m_mode = WorkspaceMode::MODE_SELECTION_RECT;
640 m_startSelRect = m_camera->ScreenToWorld(clickPoint);
641 if (m_hmPlane && m_showHM) {
649 if (showNewElementForm) {
651 newElement->
ShowForm(dialogParent, newElement,
this);
653 if (m_workspacePanel) m_workspacePanel->SetFocus();
654 CheckSlackBusDuplication(newElement);
656 if (m_continuousCalc) RunStaticStudies();
659 if (clickOnSwitch && m_continuousCalc) RunStaticStudies();
664void Workspace::OnLeftDoubleClick(wxMouseEvent& event)
666 wxWindow* dialogParent = wxGetTopLevelParent(
this);
667 if (!dialogParent) dialogParent =
this;
670 bool elementEdited =
false;
671 bool clickOnSwitch =
false;
674 for (
auto& element : m_elementList) {
676 if (element->Contains(m_camera->ScreenToWorld(event.GetPosition()))) {
677 bool elementIsBus =
false;
679 Bus* currentBus =
nullptr;
680 if ((currentBus =
dynamic_cast<Bus*
>(element.get()))) {
682 oldBus = *currentBus;
685 if (element->ShowForm(dialogParent, element.get(),
this)) {
686 CheckSlackBusDuplication(element.get());
690 if (m_workspacePanel) m_workspacePanel->SetFocus();
691 elementEdited =
true;
698 if (oldBus.GetElectricalData().nominalVoltage != currentBus->GetElectricalData().nominalVoltage ||
699 oldBus.GetElectricalData().nominalVoltageUnit !=
700 currentBus->GetElectricalData().nominalVoltageUnit) {
702 std::vector<Element*> childList = element->GetChildList();
703 for (
auto itc = childList.begin(), itcEnd = childList.end(); itc != itcEnd; ++itc) {
705 if (
typeid(*child) ==
typeid(
Line)) {
706 wxMessageDialog msgDialog(dialogParent, _(
"Do you want to change the rated voltage of the path?"),
707 _(
"Warning"), wxYES_NO | wxCENTRE | wxICON_WARNING);
708 if (msgDialog.ShowModal() == wxID_YES)
709 ValidateBusesVoltages(element.get());
711 auto data = currentBus->GetElectricalData();
712 data.nominalVoltage = oldBus.GetElectricalData().nominalVoltage;
713 data.nominalVoltageUnit = oldBus.GetElectricalData().nominalVoltageUnit;
714 currentBus->SetElectricalData(data);
720 ValidateElementsVoltages();
728 else if (element->SwitchesContains(m_camera->ScreenToWorld(event.GetPosition()))) {
729 element->SetOnline(element->IsOnline() ?
false : true);
730 clickOnSwitch =
true;
735 for (
auto& text : m_textList) {
736 if (text->
Contains(m_camera->ScreenToWorld(event.GetPosition()))) {
737 if (text->ShowForm(dialogParent, GetElementList(),
this)) SaveCurrentState();
739 if (m_workspacePanel) m_workspacePanel->SetFocus();
745 UpdateTextElements();
746 if (m_continuousCalc) RunStaticStudies();
748 if (clickOnSwitch && m_continuousCalc) RunStaticStudies();
750 if (redraw) Redraw();
754void Workspace::OnRightClickDown(wxMouseEvent& event)
757 if (m_mode == WorkspaceMode::MODE_EDIT) {
758 for (
auto& element : m_elementList) {
759 if (element->IsSelected()) {
761 if (element->Contains(m_camera->ScreenToWorld(event.GetPosition()))) {
762 element->ShowPickbox(
false);
764 menu.SetClientData(element.get());
765 if (element->GetContextMenu(menu)) {
767 menu.Bind(wxEVT_COMMAND_MENU_SELECTED, &Workspace::OnPopupClick,
this);
770 if (m_workspacePanel) m_workspacePanel->SetFocus();
773 if (!menu.GetClientData())
break;
775 element->ResetPickboxes();
780 if (redraw) Redraw();
784void Workspace::OnLeftClickUp(wxMouseEvent& event)
788 bool foundPickbox =
false;
789 bool findNewParent =
false;
790 bool updateVoltages =
false;
791 bool saveCurrentState =
false;
792 auto itnp = m_elementList.begin();
794 for (
auto it = m_elementList.begin(); it != m_elementList.end(); ++it) {
797 if (m_mode == WorkspaceMode::MODE_MOVE_PICKBOX) {
799 if (element->IsPickboxShown()) {
800 saveCurrentState =
true;
802 if (
auto bus =
dynamic_cast<Bus*
>(element.get())) {
804 for (
auto child : m_elementList) {
805 for (
auto parent : child->GetParentList()) {
809 m_disconnectedElement =
true;
828 if (m_mode == WorkspaceMode::MODE_SELECTION_RECT) {
829 if (element->Intersects(m_selectionRect)) {
830 element->SetSelected();
832 for (
auto& text : m_textList) {
833 if (text->GetElement() == element.get()) {
835 text->SetAltSelectionColour(
false);
836 text->SetAllowRotation();
844 else if (m_mode == WorkspaceMode::MODE_MOVE_NODE) {
845 if (element->IsSelected()) {
846 saveCurrentState =
true;
847 for (
auto parent : m_elementList) {
848 if (
auto bus =
dynamic_cast<Bus*
>(parent.get())) {
849 if (element->SetNodeParent(bus)) {
850 parent->AddChild(element.get());
851 findNewParent =
true;
853 element->ResetNodes();
867 if (element->PickboxContains(m_camera->ScreenToWorld(event.GetPosition()))) {
871 element->ShowPickbox(
false);
872 element->ResetPickboxes();
878 for (
auto& text : m_textList) {
879 if (m_mode == WorkspaceMode::MODE_SELECTION_RECT) {
882 text->SetAltSelectionColour(
false);
883 text->SetAllowRotation();
893 if (m_mode == WorkspaceMode::MODE_MOVE_ELEMENT) saveCurrentState =
true;
896 std::rotate(itnp, itnp + 1, m_elementList.end());
897 updateVoltages =
true;
899 if (!foundPickbox) { SetCursor(wxCURSOR_ARROW); }
901 if (m_mode != WorkspaceMode::MODE_INSERT) { m_mode = WorkspaceMode::MODE_EDIT; }
903 if (updateVoltages) { ValidateElementsVoltages(); }
905 if (saveCurrentState) SaveCurrentState();
907 if (m_continuousCalc && m_disconnectedElement) {
908 m_disconnectedElement =
false;
912 m_selectionRect = wxRect2DDouble(0, 0, 0, 0);
914 if (m_hmPlane && m_showHM) {
915 m_showHMTimer =
true;
916 m_timerHeatMap->Start();
923void Workspace::OnMouseMotion(wxMouseEvent& event)
927 case WorkspaceMode::MODE_INSERT: {
928 auto& newElement = m_elementList.back();
929 wxPoint2DDouble pos = m_camera->ScreenToWorld(event.GetPosition());
930 if (m_showGrid) pos = SnapToGrid(pos);
935 case WorkspaceMode::MODE_INSERT_TEXT: {
936 auto& newText = m_textList.back();
937 wxPoint2DDouble pos = m_camera->ScreenToWorld(event.GetPosition());
938 if (m_showGrid) pos = SnapToGrid(pos);
939 newText->SetPosition(pos);
943 case WorkspaceMode::MODE_DRAG:
944 case WorkspaceMode::MODE_DRAG_INSERT:
945 case WorkspaceMode::MODE_DRAG_INSERT_TEXT:
946 case WorkspaceMode::MODE_DRAG_PASTE: {
947 m_camera->SetTranslation(event.GetPosition());
951 case WorkspaceMode::MODE_EDIT: {
952 bool foundPickbox =
false;
953 for (
auto& element : m_elementList) {
954 if (element->IsSelected()) {
956 if (element->Contains(m_camera->ScreenToWorld(event.GetPosition()))) {
957 element->ShowPickbox();
961 if (element->PickboxContains(m_camera->ScreenToWorld(event.GetPosition()))) {
966 SetCursor(element->GetBestPickboxCursor());
971 else if (!foundPickbox) {
975 SetCursor(wxCURSOR_ARROW);
979 element->ResetPickboxes();
982 else if (!foundPickbox) {
983 if (element->IsPickboxShown()) redraw =
true;
985 element->ShowPickbox(
false);
986 element->ResetPickboxes();
990 SetCursor(wxCURSOR_ARROW);
999 case WorkspaceMode::MODE_MOVE_NODE: {
1000 wxPoint2DDouble pos = m_camera->ScreenToWorld(event.GetPosition());
1001 if (m_showGrid) pos = SnapToGrid(pos);
1002 for (
auto& element : m_elementList) {
1003 if (element->IsSelected()) {
1004 element->MoveNode(
nullptr, pos);
1010 case WorkspaceMode::MODE_MOVE_PICKBOX: {
1011 wxPoint2DDouble pos = m_camera->ScreenToWorld(event.GetPosition());
1012 if (m_showGrid) pos = SnapToGrid(pos);
1013 for (
auto& element : m_elementList) {
1014 if (element->IsSelected()) {
1015 element->MovePickbox(pos);
1019 if (m_hmPlane && m_showHM) {
1024 case WorkspaceMode::MODE_MOVE_ELEMENT:
1025 case WorkspaceMode::MODE_PASTE: {
1026 wxPoint2DDouble mouseWorld = m_camera->ScreenToWorld(event.GetPosition());
1027 wxPoint2DDouble effectivePos = mouseWorld;
1029 Element* refElement =
nullptr;
1030 for (
auto& element : m_elementList) {
1031 if (element->IsSelected()) {
1032 refElement = element.get();
1037 for (
auto& text : m_textList) {
1039 refElement = text.get();
1045 wxPoint2DDouble rawTarget = refElement->
GetPosition() + (mouseWorld - refElement->GetMoveStartPosition());
1046 wxPoint2DDouble snappedTarget = SnapToGrid(rawTarget);
1047 effectivePos = refElement->GetMoveStartPosition() + (snappedTarget - refElement->
GetPosition());
1050 effectivePos = SnapToGrid(mouseWorld);
1054 for (
auto it = m_elementList.begin(), itEnd = m_elementList.end(); it != itEnd; ++it) {
1056 if (element->IsSelected()) {
1057 element->Move(effectivePos);
1059 std::vector<Element*> childList = element->GetChildList();
1060 for (
auto it = childList.begin(), itEnd = childList.end(); it != itEnd; ++it) {
1061 (*it)->MoveNode(element.get(), effectivePos);
1067 for (
auto& text : m_textList) {
1069 text->
Move(effectivePos);
1073 if (m_hmPlane && m_showHM) {
1078 case WorkspaceMode::MODE_SELECTION_RECT: {
1079 wxPoint2DDouble currentPos = m_camera->ScreenToWorld(event.GetPosition());
1081 if (currentPos.m_x < m_startSelRect.m_x) {
1083 w = m_startSelRect.m_x - currentPos.m_x;
1086 x = m_startSelRect.m_x;
1087 w = currentPos.m_x - m_startSelRect.m_x;
1089 if (currentPos.m_y < m_startSelRect.m_y) {
1091 h = m_startSelRect.m_y - currentPos.m_y;
1094 y = m_startSelRect.m_y;
1095 h = currentPos.m_y - m_startSelRect.m_y;
1098 m_selectionRect = wxRect2DDouble(x, y, w, h);
1106 m_camera->UpdateMousePosition(event.GetPosition());
1112void Workspace::OnMiddleDown(wxMouseEvent& event)
1116 case WorkspaceMode::MODE_INSERT: {
1117 m_mode = WorkspaceMode::MODE_DRAG_INSERT;
1119 case WorkspaceMode::MODE_INSERT_TEXT: {
1120 m_mode = WorkspaceMode::MODE_DRAG_INSERT_TEXT;
1122 case WorkspaceMode::MODE_PASTE: {
1123 m_mode = WorkspaceMode::MODE_DRAG_PASTE;
1126 m_mode = WorkspaceMode::MODE_DRAG;
1129 m_camera->StartTranslation(m_camera->ScreenToWorld(event.GetPosition()));
1132 if (m_hmPlane && m_showHM) {
1139void Workspace::OnMiddleUp(wxMouseEvent& event)
1142 case WorkspaceMode::MODE_DRAG_INSERT: {
1143 m_mode = WorkspaceMode::MODE_INSERT;
1145 case WorkspaceMode::MODE_DRAG_INSERT_TEXT: {
1146 m_mode = WorkspaceMode::MODE_INSERT_TEXT;
1148 case WorkspaceMode::MODE_DRAG_PASTE: {
1149 m_mode = WorkspaceMode::MODE_PASTE;
1151 case WorkspaceMode::MODE_INSERT:
1152 case WorkspaceMode::MODE_INSERT_TEXT:
1153 case WorkspaceMode::MODE_PASTE: {
1157 m_mode = WorkspaceMode::MODE_EDIT;
1162 if (m_hmPlane && m_showHM) {
1170void Workspace::OnScroll(wxMouseEvent& event)
1172 if (event.GetWheelRotation() > 0)
1173 m_camera->SetScale(event.GetPosition(), +0.05);
1175 m_camera->SetScale(event.GetPosition(), -0.05);
1177 if (m_hmPlane && m_showHM) {
1179 m_showHMTimer =
true;
1180 m_timerHeatMap->Start();
1187void Workspace::OnKeyDown(wxKeyEvent& event)
1189 bool insertingElement =
false;
1190 if (m_mode == WorkspaceMode::MODE_INSERT || m_mode == WorkspaceMode::MODE_INSERT_TEXT) insertingElement =
true;
1192 int key =
event.GetKeyCode();
1193 if (key != WXK_NONE) {
1197 if (m_mode == WorkspaceMode::MODE_INSERT) {
1199 auto elementToDelete = m_elementList.back();
1202 for (
auto& element : m_elementList) {
1203 element->RemoveChild(elementToDelete.get());
1206 m_elementList.pop_back();
1207 m_mode = WorkspaceMode::MODE_EDIT;
1210 else if (m_mode == WorkspaceMode::MODE_INSERT_TEXT) {
1211 m_textList.pop_back();
1212 m_mode = WorkspaceMode::MODE_EDIT;
1219 DeleteSelectedElements();
1222 if (!insertingElement) {
1223 if (!event.ControlDown()) {
1224 auto newText = std::make_shared<Text>(
1225 m_camera->ScreenToWorld(event.GetPosition()),
1226 m_properties->GetGeneralPropertiesData().labelFont,
1227 m_properties->GetGeneralPropertiesData().labelFontSize);
1228 m_textList.push_back(newText);
1229 m_mode = WorkspaceMode::MODE_INSERT_TEXT;
1230 m_statusBar->SetStatusText(_(
"Insert Text: Click to insert, ESC to cancel."));
1231 if (m_hmPlane && m_showHM) {
1237 AlignSelectedToGrid();
1242 if (event.GetModifiers() == wxMOD_SHIFT) { Fit(); }
1246 RotateSelectedElements(event.GetModifiers() != wxMOD_SHIFT);
1250 if (!insertingElement) {
1251 auto newBus = std::make_shared<Bus>(m_camera->ScreenToWorld(event.GetPosition()),
1252 wxString::Format(_(
"Bus %d"), GetElementNumber(ID_BUS)));
1253 IncrementElementNumber(ID_BUS);
1254 m_elementList.push_back(newBus);
1255 m_mode = WorkspaceMode::MODE_INSERT;
1256 m_statusBar->SetStatusText(_(
"Insert Bus: Click to insert, ESC to cancel."));
1257 if (m_hmPlane && m_showHM) {
1264 if (!insertingElement) {
1265 if (!event.ControlDown() && event.ShiftDown()) {
1266 auto newLoad = std::make_shared<Load>(wxString::Format(_(
"Load %d"), GetElementNumber(ID_LOAD)));
1267 IncrementElementNumber(ID_LOAD);
1268 m_elementList.push_back(newLoad);
1269 m_mode = WorkspaceMode::MODE_INSERT;
1270 m_statusBar->SetStatusText(_(
"Insert Load: Click on a bus, ESC to cancel."));
1272 else if (!event.ControlDown() && !event.ShiftDown()) {
1273 auto newLine = std::make_shared<Line>(wxString::Format(_(
"Line %d"), GetElementNumber(ID_LINE)));
1274 IncrementElementNumber(ID_LINE);
1275 m_elementList.push_back(newLine);
1276 m_mode = WorkspaceMode::MODE_INSERT;
1277 m_statusBar->SetStatusText(_(
"Insert Line: Click on two buses, ESC to cancel."));
1279 if (m_hmPlane && m_showHM) {
1288 if (!insertingElement) {
1289 auto newTransformer = std::make_shared<Transformer>(wxString::Format(_(
"Transformer %d"), GetElementNumber(ID_TRANSFORMER)));
1290 IncrementElementNumber(ID_TRANSFORMER);
1291 m_elementList.push_back(newTransformer);
1292 m_mode = WorkspaceMode::MODE_INSERT;
1293 m_statusBar->SetStatusText(_(
"Insert Transformer: Click on two buses, ESC to cancel."));
1294 if (m_hmPlane && m_showHM) {
1302 if (!insertingElement) {
1303 auto newGenerator = std::make_shared<SyncGenerator>(wxString::Format(_(
"Generator %d"), GetElementNumber(ID_SYNCGENERATOR)));
1304 IncrementElementNumber(ID_SYNCGENERATOR);
1305 m_elementList.push_back(newGenerator);
1306 m_mode = WorkspaceMode::MODE_INSERT;
1307 m_statusBar->SetStatusText(_(
"Insert Generator: Click on a bus, ESC to cancel."));
1308 if (m_hmPlane && m_showHM) {
1315 if (!insertingElement) {
1316 if (event.GetModifiers() == wxMOD_SHIFT) {
1317 auto newInductor = std::make_shared<Inductor>(wxString::Format(_(
"Inductor %d"), GetElementNumber(ID_INDUCTOR)));
1318 IncrementElementNumber(ID_INDUCTOR);
1319 m_elementList.push_back(newInductor);
1320 m_mode = WorkspaceMode::MODE_INSERT;
1321 m_statusBar->SetStatusText(_(
"Insert Inductor: Click on a bus, ESC to cancel."));
1325 auto newIndMotor = std::make_shared<IndMotor>(wxString::Format(_(
"Induction motor %d"), GetElementNumber(ID_INDMOTOR)));
1326 IncrementElementNumber(ID_INDMOTOR);
1327 m_elementList.push_back(newIndMotor);
1328 m_mode = WorkspaceMode::MODE_INSERT;
1329 m_statusBar->SetStatusText(_(
"Insert Induction Motor: Click on a bus, ESC to cancel."));
1331 if (m_hmPlane && m_showHM) {
1339 if (!insertingElement) {
1340 auto newSyncCondenser = std::make_shared<SyncMotor>(wxString::Format(_(
"Synchronous condenser %d"), GetElementNumber(ID_SYNCMOTOR)));
1341 IncrementElementNumber(ID_SYNCMOTOR);
1342 m_elementList.push_back(newSyncCondenser);
1343 m_mode = WorkspaceMode::MODE_INSERT;
1344 m_statusBar->SetStatusText(_(
"Insert Synchronous Condenser: Click on a bus, ESC to cancel."));
1345 if (m_hmPlane && m_showHM) {
1352 if (!insertingElement) {
1353 if (event.GetModifiers() == wxMOD_SHIFT) {
1354 auto newCapacitor = std::make_shared<Capacitor>(wxString::Format(_(
"Capacitor %d"), GetElementNumber(ID_CAPACITOR)));
1355 IncrementElementNumber(ID_CAPACITOR);
1356 m_elementList.push_back(newCapacitor);
1357 m_mode = WorkspaceMode::MODE_INSERT;
1358 m_statusBar->SetStatusText(_(
"Insert Capacitor: Click on a bus, ESC to cancel."));
1359 if (m_hmPlane && m_showHM) {
1364 else if (event.GetModifiers() == wxMOD_CONTROL) {
1370 if (!insertingElement) {
1371 if (event.ShiftDown() && event.ControlDown()) {
1381 else if (event.GetModifiers() == wxMOD_SHIFT) {
1382 auto newHarmCurrent = std::make_shared<HarmCurrent>(
1383 wxString::Format(_(
"Harmonic Current %d"), GetElementNumber(ID_HARMCURRENT)));
1384 IncrementElementNumber(ID_HARMCURRENT);
1385 m_elementList.push_back(newHarmCurrent);
1386 m_mode = WorkspaceMode::MODE_INSERT;
1387 m_statusBar->SetStatusText(
1388 _(
"Insert Harmonic Current Source: Click on a bus, ESC to cancel."));
1390 if (m_hmPlane && m_showHM) {
1397 if (!insertingElement) {
1398 if (event.GetModifiers() == wxMOD_CONTROL) { Paste(); }
1402 if (!insertingElement) {
1403 if (event.GetModifiers() == wxMOD_CONTROL) {
1407 if (GetSavedPath().IsOk()) {
1408 fileHandling.SaveProject(GetSavedPath());
1411 wxFileDialog saveFileDialog(
this, _(
"Save PSP file"),
"",
"",
"PSP files (*.psp)|*.psp",
1412 wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
1413 if (saveFileDialog.ShowModal() == wxID_CANCEL)
return;
1415 fileHandling.SaveProject(saveFileDialog.GetPath());
1416 wxFileName fileName(saveFileDialog.GetPath());
1417 SetName(fileName.GetName());
1418 if (m_auiNotebook) m_auiNotebook->SetPageText(m_auiNotebook->GetPageIndex(
this), GetName());
1419 SetSavedPath(fileName);
1425 if (!insertingElement) {
1426 if (event.ControlDown() && !event.ShiftDown()) { SetPreviousState(); }
1427 if (event.ControlDown() && event.ShiftDown()) { SetNextState(); }
1431 if (!insertingElement) {
1432 if (event.GetModifiers() == wxMOD_CONTROL) { SetNextState(); }
1436 if (!insertingElement) {
1437 if (event.GetModifiers() == wxMOD_SHIFT) {
1439 if (!insertingElement) {
1440 auto newEMTElement = std::make_shared<EMTElement>(wxString::Format(_(
"Electromagnetic Element %d"), GetElementNumber(ID_EMTELEMENT)));
1441 IncrementElementNumber(ID_EMTELEMENT);
1442 m_elementList.push_back(newEMTElement);
1443 m_mode = WorkspaceMode::MODE_INSERT;
1444 m_statusBar->SetStatusText(_(
"Insert Electromagnetic Transient Element: Click on a bus, ESC to cancel."));
1445 if (m_hmPlane && m_showHM) {
1454#ifdef SHOW_DEBUG_PANEL
1456 if (event.GetModifiers() == wxMOD_CONTROL) {
1458 m_debugFrame->SetFocus();
1473void Workspace::UpdateStatusBar()
1476 case WorkspaceMode::MODE_DRAG: {
1477 m_statusBar->SetStatusText(_(
"MODE: DRAG"), 1);
1480 case WorkspaceMode::MODE_PASTE:
1481 case WorkspaceMode::MODE_DRAG_PASTE: {
1482 m_statusBar->SetStatusText(_(
"MODE: PASTE"), 1);
1485 case WorkspaceMode::MODE_INSERT:
1486 case WorkspaceMode::MODE_INSERT_TEXT:
1487 case WorkspaceMode::MODE_DRAG_INSERT:
1488 case WorkspaceMode::MODE_DRAG_INSERT_TEXT: {
1489 m_statusBar->SetStatusText(_(
"MODE: INSERT"), 1);
1492 case WorkspaceMode::MODE_MOVE_ELEMENT:
1493 case WorkspaceMode::MODE_MOVE_PICKBOX:
1494 case WorkspaceMode::MODE_MOVE_NODE:
1495 case WorkspaceMode::MODE_SELECTION_RECT:
1496 case WorkspaceMode::MODE_EDIT: {
1497 if (m_oldStatusMode != m_mode)
1498 m_statusBar->SetStatusText(wxT(
""));
1499 m_statusBar->SetStatusText(_(
"MODE: EDIT"), 1);
1503 m_statusBar->SetStatusText(wxString::Format(_(
"ZOOM: %d%%"), (
int)(m_camera->GetScale() * 100.0)), 2);
1504 m_statusBar->SetStatusText(
1505 wxString::Format(wxT(
"X: %.1f Y: %.1f"), m_camera->GetMousePosition().m_x, m_camera->GetMousePosition().m_y),
1507 m_oldStatusMode = m_mode;
1510int Workspace::GetElementNumberFromList(
Element* element)
1513 for (
auto& elementFromList : m_elementList) {
1514 if (element->GetElementType() == elementFromList->GetElementType()) {
1515 if (element == elementFromList.get())
return elementNum;
1522void Workspace::GetStateListsCopy(
const std::vector< std::shared_ptr<PowerElement> >& elementsList,
1523 const std::vector< std::shared_ptr<Text> >& textList,
1524 std::vector< std::shared_ptr<PowerElement> >& elementsListCopy,
1525 std::vector< std::shared_ptr<Text> >& textListCopy)
1530 elementsListCopy.clear();
1531 textListCopy.clear();
1533 std::map<Element*, Element*> elementMap;
1535 for (
auto& element : elementsList) {
1543 elementsListCopy.emplace_back(copyElement);
1544 elementMap[element.get()] = copyElement;
1547 for (
auto& copyElement : elementsListCopy) {
1550 for (
Element* parent : copyElement->GetParentList()) {
1551 auto it = elementMap.find(parent);
1553 if (it != elementMap.end())
1554 copyElement->SetParent(it->second, i);
1560 for (
Element* child : copyElement->GetChildList()) {
1561 auto it = elementMap.find(child);
1563 if (it != elementMap.end())
1564 copyElement->SetChild(it->second, i);
1570 for (
const auto& text : textList) {
1574 auto copyText =
static_cast<Text*
>(text->
GetCopy());
1579 auto it = elementMap.find(copyText->GetElement());
1581 if (it != elementMap.end())
1582 copyText->SetElement(it->second);
1584 copyText->SetElement(
nullptr);
1585 textListCopy.emplace_back(copyText);
1589void Workspace::UpdateHeatMap()
1591 if (m_hmPlane && m_showHM) {
1594 wxRect2DDouble screenRect(-100, -100, m_width + 200.0, m_height + 200.0);
1597 std::vector<Bus*> busList;
1598 float minVoltage, maxVoltage;
1599 if (m_hmAutomaticLabel) {
1604 minVoltage = m_hmPlane->GetMinLimit();
1605 maxVoltage = m_hmPlane->GetMaxLimit();
1608 for (
auto& element : m_elementList) {
1609 if (
Bus* bus =
dynamic_cast<Bus*
>(element.get())) {
1610 if (m_hmAutomaticLabel) {
1611 const float voltage = std::abs(bus->GetElectricalData().voltage);
1612 if (minVoltage > voltage) minVoltage = voltage;
1613 if (maxVoltage < voltage) maxVoltage = voltage;
1615 busList.push_back(bus);
1618 if (m_hmAutomaticLabel) {
1619 m_hmPlane->SetLabelLimits(minVoltage, maxVoltage);
1622 for (
Bus* bus : busList) {
1623 const float voltage = std::abs(bus->GetElectricalData().voltage);
1624 float depth = 2.0f * (voltage - (maxVoltage + minVoltage) / 2.0f) / (maxVoltage - minVoltage);
1625 if (depth < -1.0) depth = -1.0;
1626 if (depth > 1.0) depth = 1.0;
1628 wxRect2DDouble rect = bus->GetRect();
1629 rect = wxRect2DDouble(
1630 (rect.m_x - 100.0f) * m_camera->GetScale() + m_camera->GetTranslation().m_x * m_camera->GetScale(),
1631 (rect.m_y - 50.0f) * m_camera->GetScale() + m_camera->GetTranslation().m_y * m_camera->GetScale(),
1632 (rect.m_width + 200.0f) * m_camera->GetScale(),
1633 (rect.m_height + 100.0f) * m_camera->GetScale());
1635 if (screenRect.Contains(rect))
1636 m_hmPlane->SetRectSlope(rect, M_PI *
static_cast<float>(bus->GetAngle()) / 180.0f, depth);
1642 int iterations = std::lround(10 * std::pow(m_camera->GetScale(), 2));
1645 if (iterations < 1) iterations = 1;
1646 m_hmPlane->SmoothPlane(iterations);
1650void Workspace::OnPopupClick(wxCommandEvent& event)
1652 wxWindow* dialogParent = wxGetTopLevelParent(
this);
1653 if (!dialogParent) dialogParent =
this;
1656 bool redrawHM =
false;
1658 wxMenu* menu =
static_cast<wxMenu*
>(
event.GetEventObject());
1660 int eventID =
event.GetId();
1663 if (element->
ShowForm(dialogParent, element,
this)) {
1664 CheckSlackBusDuplication(element);
1665 UpdateTextElements();
1669 if (m_workspacePanel) m_workspacePanel->SetFocus();
1672 Line* line =
static_cast<Line*
>(element);
1673 line->AddNode(m_camera->GetMousePosition());
1678 Line* line =
static_cast<Line*
>(element);
1679 line->RemoveNode(m_camera->GetMousePosition());
1685 for (
auto& iElement : m_elementList) {
1687 for (
int i = 0; i < (int)iElement->GetParentList().size(); i++) {
1689 if (parent == element) { iElement->RotateNode(parent); }
1698 for (
auto& iElement : m_elementList) {
1700 for (
int i = 0; i < (int)iElement->GetParentList().size(); i++) {
1702 if (parent == element) { iElement->RotateNode(parent,
false); }
1749 std::vector<Element*> childList = element->
GetChildList();
1750 for (
auto child : childList) {
1757 std::vector<Element*> parentList = element->
GetParentList();
1758 for (
auto parent : parentList) {
1763 std::erase_if(m_textList, [&](
const auto& text) {
1764 return text->GetElement() == element;
1767 std::erase_if(m_elementList, [&](
const auto& delElement) {
1768 return delElement.get() == element;
1771 menu->SetClientData(
nullptr);
1775 if (InsertTextElement(eventID, element)) {
1776 UpdateTextElements();
1781 if (redrawHM && m_hmPlane && m_showHM) {
1783 m_showHMTimer =
true;
1784 m_timerHeatMap->Start();
1788void Workspace::RotateSelectedElements(
bool clockwise)
1790 bool saveCurrrentState =
false;
1791 for (
auto& element : m_elementList) {
1793 for (
int i = 0; i < (int)element->
GetParentList().size(); i++) {
1800 element->
StartMove(m_camera->GetMousePosition());
1805 saveCurrrentState =
true;
1806 element->
Rotate(clockwise);
1807 element->
StartMove(m_camera->GetMousePosition());
1812 for (
auto& text : m_textList) {
1814 saveCurrrentState =
true;
1816 text->
StartMove(m_camera->GetMousePosition());
1819 if (saveCurrrentState) SaveCurrentState();
1821 if (m_hmPlane && m_showHM) {
1823 m_showHMTimer =
true;
1824 m_timerHeatMap->Start();
1829void Workspace::DeleteSelectedElements()
1832 for (
auto it = m_elementList.begin(); it != m_elementList.end();) {
1837 std::vector<Element*> childList = element->
GetChildList();
1838 for (
auto itc = childList.begin(), itEnd = childList.end(); itc != itEnd; ++itc) {
1845 std::vector<Element*> parentList = element->
GetParentList();
1846 for (
auto itp = parentList.begin(), itEnd = parentList.end(); itp != itEnd; ++itp) {
1861 std::erase_if(m_textList, [&](
const auto& text) {
1862 return text->GetElement() == element;
1865 it = m_elementList.erase(it);
1882 std::erase_if(m_textList, [](
const auto& text) {
1886 if (m_hmPlane && m_showHM) {
1888 m_showHMTimer =
true;
1889 m_timerHeatMap->Start();
1895bool Workspace::GetElementsCorners(wxPoint2DDouble& leftUpCorner,
1896 wxPoint2DDouble& rightDownCorner,
1897 std::vector<Element*> elementList)
1899 if (elementList.size() == 0)
return false;
1901 elementList[0]->CalculateBoundaries(leftUpCorner, rightDownCorner);
1903 for (
auto it = elementList.begin() + 1, itEnd = elementList.end(); it != itEnd; it++) {
1905 wxPoint2DDouble leftUp;
1906 wxPoint2DDouble rightDown;
1908 if (leftUp.m_x < leftUpCorner.m_x) leftUpCorner.m_x = leftUp.m_x;
1909 if (leftUp.m_y < leftUpCorner.m_y) leftUpCorner.m_y = leftUp.m_y;
1910 if (rightDown.m_x > rightDownCorner.m_x) rightDownCorner.m_x = rightDown.m_x;
1911 if (rightDown.m_y > rightDownCorner.m_y) rightDownCorner.m_y = rightDown.m_y;
1916void Workspace::Fit()
1918 wxPoint2DDouble leftUpCorner(0, 0);
1919 wxPoint2DDouble rightDownCorner(0, 0);
1920 std::vector<Element*> elementList = GetElementList();
1921 for (
const auto& text : m_textList) { elementList.push_back(text.get()); }
1923 if (!GetElementsCorners(leftUpCorner, rightDownCorner, elementList))
return;
1924 wxPoint2DDouble middleCoords = (leftUpCorner + rightDownCorner) / 2.0;
1928 GetSize(&width, &height);
1930 const double scaleX =
static_cast<double>(width) / (rightDownCorner.m_x - leftUpCorner.m_x);
1931 const double scaleY =
static_cast<double>(height) / (rightDownCorner.m_y - leftUpCorner.m_y);
1933 double scale = scaleX < scaleY ? scaleX : scaleY;
1934 scale = std::min(scale, m_camera->GetZoomMax());
1935 scale = std::max(scale, m_camera->GetZoomMin());
1937 m_camera->SetScale(scale);
1939 m_camera->StartTranslation(middleCoords);
1940 m_camera->SetTranslation(wxPoint2DDouble(width / 2.0, height / 2.0));
1942 if (m_hmPlane && m_showHM) {
1949bool Workspace::InsertTextElement(
int textID,
Element* parentElement, ElectricalUnit unit,
int precision)
1953 if (FindTextElement(parentElement, DATA_NAME))
return false;
1955 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(40, -30), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1956 newText->SetElement(parentElement);
1957 newText->SetDataType(DATA_NAME);
1958 newText->SetElementTypeText(parentElement->GetElementType());
1959 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1961 m_textList.push_back(newText);
1963 case ID_TXT_VOLTAGE: {
1964 if (FindTextElement(parentElement, DATA_VOLTAGE))
return false;
1966 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(40, 15), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1967 newText->SetElement(parentElement);
1968 newText->SetDataType(DATA_VOLTAGE);
1969 if (unit == ElectricalUnit::UNIT_NONE)
1970 newText->SetUnit(ElectricalUnit::UNIT_PU);
1972 newText->SetUnit(unit);
1973 newText->SetDecimalPlaces(precision);
1974 newText->SetElementTypeText(parentElement->GetElementType());
1975 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1977 m_textList.emplace_back(newText);
1979 case ID_TXT_ANGLE: {
1980 if (FindTextElement(parentElement, DATA_ANGLE))
return false;
1982 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(40, 30), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1983 newText->SetElement(parentElement);
1984 newText->SetDataType(DATA_ANGLE);
1985 if (unit == ElectricalUnit::UNIT_NONE)
1986 newText->SetUnit(ElectricalUnit::UNIT_DEGREE);
1988 newText->SetUnit(unit);
1989 newText->SetDecimalPlaces(precision);
1990 newText->SetElementTypeText(parentElement->GetElementType());
1991 newText->SetElementNumber(GetElementNumberFromList(parentElement));
1993 m_textList.emplace_back(newText);
1995 case ID_TXT_FAULTCURRENT: {
1996 if (FindTextElement(parentElement, DATA_SC_CURRENT))
return false;
1998 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(-70, 30), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
1999 newText->SetElement(parentElement);
2000 newText->SetDataType(DATA_SC_CURRENT);
2001 if (unit == ElectricalUnit::UNIT_NONE)
2002 newText->SetUnit(ElectricalUnit::UNIT_A);
2004 newText->SetUnit(unit);
2005 newText->SetDecimalPlaces(precision);
2006 newText->SetElementTypeText(parentElement->GetElementType());
2007 newText->SetElementNumber(GetElementNumberFromList(parentElement));
2009 m_textList.emplace_back(newText);
2011 case ID_TXT_FAULTVOLTAGE: {
2012 if (FindTextElement(parentElement, DATA_SC_VOLTAGE))
return false;
2014 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(-70, 75), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
2015 newText->SetElement(parentElement);
2016 newText->SetDataType(DATA_SC_VOLTAGE);
2017 if (unit == ElectricalUnit::UNIT_NONE)
2018 newText->SetUnit(ElectricalUnit::UNIT_PU);
2020 newText->SetUnit(unit);
2021 newText->SetDecimalPlaces(precision);
2022 newText->SetElementTypeText(parentElement->GetElementType());
2023 newText->SetElementNumber(GetElementNumberFromList(parentElement));
2025 m_textList.emplace_back(newText);
2028 if (FindTextElement(parentElement, DATA_SC_POWER))
return false;
2029 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(-50, -30), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
2030 newText->SetElement(parentElement);
2031 newText->SetDataType(DATA_SC_POWER);
2032 newText->SetUnit(ElectricalUnit::UNIT_MVA);
2033 if (unit == ElectricalUnit::UNIT_NONE)
2034 newText->SetUnit(ElectricalUnit::UNIT_MVA);
2036 newText->SetUnit(unit);
2037 newText->SetDecimalPlaces(precision);
2038 newText->SetElementTypeText(parentElement->GetElementType());
2039 newText->SetElementNumber(GetElementNumberFromList(parentElement));
2041 m_textList.emplace_back(newText);
2044 if (FindTextElement(parentElement, DATA_PQ_THD))
return false;
2045 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(-50, -15), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
2046 newText->SetElement(parentElement);
2047 newText->SetDataType(DATA_PQ_THD);
2048 newText->SetDecimalPlaces(precision);
2049 newText->SetElementTypeText(parentElement->GetElementType());
2050 newText->SetElementNumber(GetElementNumberFromList(parentElement));
2052 m_textList.emplace_back(newText);
2054 case ID_TXT_ACTIVE_POWER: {
2055 if (FindTextElement(parentElement, DATA_ACTIVE_POWER))
return false;
2056 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(0, 35), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
2057 newText->SetElement(parentElement);
2058 newText->SetDataType(DATA_ACTIVE_POWER);
2059 if (unit == ElectricalUnit::UNIT_NONE)
2060 newText->SetUnit(ElectricalUnit::UNIT_MW);
2062 newText->SetUnit(unit);
2063 newText->SetDecimalPlaces(precision);
2064 newText->SetElementTypeText(parentElement->GetElementType());
2065 newText->SetElementNumber(GetElementNumberFromList(parentElement));
2067 m_textList.emplace_back(newText);
2069 case ID_TXT_REACTIVE_POWER: {
2070 if (FindTextElement(parentElement, DATA_REACTIVE_POWER))
return false;
2071 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(0, 50), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
2072 newText->SetElement(parentElement);
2073 newText->SetDataType(DATA_REACTIVE_POWER);
2074 if (unit == ElectricalUnit::UNIT_NONE)
2075 newText->SetUnit(ElectricalUnit::UNIT_Mvar);
2077 newText->SetUnit(unit);
2078 newText->SetDecimalPlaces(precision);
2079 newText->SetElementTypeText(parentElement->GetElementType());
2080 newText->SetElementNumber(GetElementNumberFromList(parentElement));
2082 m_textList.emplace_back(newText);
2084 case ID_TXT_BRANCH_ACTIVE_POWER_1_2:
2085 case ID_TXT_BRANCH_ACTIVE_POWER_2_1: {
2086 if (FindTextElement(parentElement, DATA_PF_ACTIVE))
return false;
2087 wxPoint2DDouble position(0.0, -10.0);
2088 if (textID == ID_TXT_BRANCH_ACTIVE_POWER_1_2)
2093 auto newText = std::make_shared<Text>(position, m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
2094 newText->SetElement(parentElement);
2095 newText->SetDataType(DATA_PF_ACTIVE);
2096 if (unit == ElectricalUnit::UNIT_NONE)
2097 newText->SetUnit(ElectricalUnit::UNIT_MW);
2099 newText->SetUnit(unit);
2100 newText->SetDecimalPlaces(precision);
2101 newText->SetElementTypeText(parentElement->GetElementType());
2102 newText->SetElementNumber(GetElementNumberFromList(parentElement));
2103 if (textID == ID_TXT_BRANCH_ACTIVE_POWER_2_1)
2104 newText->SetDirection(1);
2106 m_textList.emplace_back(newText);
2108 case ID_TXT_BRANCH_REACTIVE_POWER_1_2:
2109 case ID_TXT_BRANCH_REACTIVE_POWER_2_1: {
2110 if (FindTextElement(parentElement, DATA_PF_REACTIVE))
return false;
2111 wxPoint2DDouble position(0.0, 10.0);
2112 if (textID == ID_TXT_BRANCH_REACTIVE_POWER_1_2)
2117 auto newText = std::make_shared<Text>(position, m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
2118 newText->SetElement(parentElement);
2119 newText->SetDataType(DATA_PF_REACTIVE);
2120 if (unit == ElectricalUnit::UNIT_NONE)
2121 newText->SetUnit(ElectricalUnit::UNIT_Mvar);
2123 newText->SetUnit(unit);
2124 newText->SetDecimalPlaces(precision);
2125 newText->SetElementTypeText(parentElement->GetElementType());
2126 newText->SetElementNumber(GetElementNumberFromList(parentElement));
2127 if (textID == ID_TXT_BRANCH_REACTIVE_POWER_2_1)
2128 newText->SetDirection(1);
2130 m_textList.emplace_back(newText);
2132 case ID_TXT_BRANCH_LOSSES: {
2133 if (FindTextElement(parentElement, DATA_PF_LOSSES))
return false;
2135 auto newText = std::make_shared<Text>(position, m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
2136 newText->SetElement(parentElement);
2137 newText->SetDataType(DATA_PF_LOSSES);
2138 if (unit == ElectricalUnit::UNIT_NONE)
2139 newText->SetUnit(ElectricalUnit::UNIT_MW);
2141 newText->SetUnit(unit);
2142 newText->SetDecimalPlaces(precision);
2143 newText->SetElementTypeText(parentElement->GetElementType());
2144 newText->SetElementNumber(GetElementNumberFromList(parentElement));
2146 m_textList.emplace_back(newText);
2148 case ID_TXT_BRANCH_CURRENT_1_2:
2149 case ID_TXT_BRANCH_CURRENT_2_1: {
2150 if (FindTextElement(parentElement, DATA_PF_CURRENT))
return false;
2151 wxPoint2DDouble position(0.0, 10.0);
2152 if (textID == ID_TXT_BRANCH_CURRENT_1_2)
2157 auto newText = std::make_shared<Text>(position, m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
2158 newText->SetElement(parentElement);
2159 newText->SetDataType(DATA_PF_CURRENT);
2160 if (unit == ElectricalUnit::UNIT_NONE)
2161 newText->SetUnit(ElectricalUnit::UNIT_A);
2163 newText->SetUnit(unit);
2164 newText->SetDecimalPlaces(precision);
2165 newText->SetElementTypeText(parentElement->GetElementType());
2166 newText->SetElementNumber(GetElementNumberFromList(parentElement));
2167 if (textID == ID_TXT_BRANCH_CURRENT_2_1)
2168 newText->SetDirection(1);
2170 m_textList.emplace_back(newText);
2172 case ID_TXT_BRANCH_FAULT_CURRENT_1_2:
2173 case ID_TXT_BRANCH_FAULT_CURRENT_2_1: {
2174 if (FindTextElement(parentElement, DATA_SC_CURRENT))
return false;
2175 wxPoint2DDouble position(0.0, 25.0);
2176 if (textID == ID_TXT_BRANCH_FAULT_CURRENT_1_2)
2181 auto newText = std::make_shared<Text>(position, m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
2182 newText->SetElement(parentElement);
2183 newText->SetDataType(DATA_SC_CURRENT);
2184 if (unit == ElectricalUnit::UNIT_NONE)
2185 newText->SetUnit(ElectricalUnit::UNIT_kA);
2187 newText->SetUnit(unit);
2188 newText->SetDecimalPlaces(precision);
2189 newText->SetElementTypeText(parentElement->GetElementType());
2190 newText->SetElementNumber(GetElementNumberFromList(parentElement));
2191 if (textID == ID_TXT_BRANCH_FAULT_CURRENT_2_1)
2192 newText->SetDirection(1);
2194 m_textList.emplace_back(newText);
2197 if (FindTextElement(parentElement, DATA_TRANSFORMER_TAP))
return false;
2198 auto newText = std::make_shared<Text>(parentElement->
GetPosition() + wxPoint2DDouble(25, -15), m_properties->GetGeneralPropertiesData().labelFont, m_properties->GetGeneralPropertiesData().labelFontSize);
2199 newText->SetElement(parentElement);
2200 newText->SetDataType(DATA_TRANSFORMER_TAP);
2201 newText->SetUnit(unit == ElectricalUnit::UNIT_NONE ? ElectricalUnit::UNIT_PU : unit);
2202 newText->SetDecimalPlaces(precision);
2203 newText->SetElementTypeText(parentElement->GetElementType());
2204 newText->SetElementNumber(GetElementNumberFromList(parentElement));
2206 m_textList.emplace_back(newText);
2215Element* Workspace::FindTextElement(
Element* parentElement,
int dataType)
2217 for (
auto& text : m_textList) {
2218 if (text->GetElement() == parentElement && text->GetDataType() == dataType)
2224void Workspace::RemoveAllTextElements()
2233void Workspace::CheckSlackBusDuplication(
Element* newSlackBus)
2235 Bus* newBus =
dynamic_cast<Bus*
>(newSlackBus);
2237 if (!newBus->GetElectricalData().slackBus)
return;
2239 for (
auto& element : m_elementList) {
2240 Bus* bus =
dynamic_cast<Bus*
>(element.get());
2242 if (bus->GetElectricalData().slackBus && bus != newSlackBus) {
2243 wxMessageDialog msgDialog(
this,
2244 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),
2245 _(
"Warning"), wxYES_NO | wxCENTRE | wxICON_WARNING);
2246 if (msgDialog.ShowModal() == wxID_YES) {
2247 auto data = bus->GetElectricalData();
2248 data.slackBus =
false;
2249 bus->SetElectricalData(data);
2253 auto data = newBus->GetElectricalData();
2254 data.slackBus =
false;
2255 newBus->SetElectricalData(data);
2263void Workspace::ValidateBusesVoltages(
Element* initialBus)
2265 double nominalVoltage =
static_cast<Bus*
>(initialBus)->GetElectricalData().nominalVoltage;
2266 ElectricalUnit nominalVoltageUnit =
static_cast<Bus*
>(initialBus)->GetElectricalData().nominalVoltageUnit;
2268 for (
auto it = m_elementList.begin(); it != m_elementList.end(); it++) {
2271 if (
auto line =
dynamic_cast<Line*
>(child)) {
2276 if (data1.nominalVoltage != data2.nominalVoltage ||
2277 data1.nominalVoltageUnit != data2.nominalVoltageUnit) {
2278 data1.nominalVoltage = nominalVoltage;
2279 data2.nominalVoltage = nominalVoltage;
2280 data1.nominalVoltageUnit = nominalVoltageUnit;
2281 data2.nominalVoltageUnit = nominalVoltageUnit;
2286 it = m_elementList.begin();
2295void Workspace::ValidateElementsVoltages()
2297 for (
auto& child : m_elementList) {
2298 std::vector<double> nominalVoltage;
2299 std::vector<ElectricalUnit> nominalVoltageUnit;
2300 for (
int i = 0; i < (int)child->
GetParentList().size(); i++) {
2303 nominalVoltage.push_back(parent->GetElectricalData().nominalVoltage);
2304 nominalVoltageUnit.push_back(parent->GetElectricalData().nominalVoltageUnit);
2307 child->SetNominalVoltage(nominalVoltage, nominalVoltageUnit);
2311void Workspace::ResetAllVoltages()
2315 UpdateTextElements();
2319bool Workspace::RunPowerFlow(
bool resetVoltages,
bool showBusyInfo)
2321 auto simProp = m_properties->GetSimulationPropertiesData();
2322 double basePower = simProp.basePower;
2323 if (simProp.basePowerUnit == ElectricalUnit::UNIT_MVA)
2325 else if (simProp.basePowerUnit == ElectricalUnit::UNIT_kVA)
2329 for (
auto& element : m_elementList) {
2330 if (
auto emtElement =
dynamic_cast<EMTElement*
>(element.get())) {
2331 if (emtElement->IsOnline()) {
2332 emtElement->UpdateData(m_properties,
true);
2336 bool result =
false;
2337 wxString errorMsg =
"";
2342 wxBusyInfo* info =
nullptr;
2344 info =
new wxBusyInfo(
2347 .Icon(wxIcon(Paths::GetDataPath() +
"/images/ribbon/powerFLow32.png", wxBITMAP_TYPE_PNG))
2348 .Title(_(
"<b>Calculating Power Flow</b>"))
2349 .
Text(_(
"Please wait..."))
2350 .Foreground(*wxWHITE)
2351 .Background(*wxBLACK)
2352 .Transparency(4 * wxALPHA_OPAQUE / 5)
2367 if (resetVoltages) pf.ResetVoltages();
2369 switch (simProp.powerFlowMethod) {
2370 case GAUSS_SEIDEL: {
2371 result = pf.RunGaussSeidel(basePower, simProp.powerFlowMaxIterations, simProp.powerFlowTolerance,
2372 simProp.initAngle, simProp.accFator);
2374 case NEWTON_RAPHSON: {
2375 result = pf.RunNewtonRaphson(basePower, simProp.powerFlowMaxIterations, simProp.powerFlowTolerance,
2376 simProp.initAngle, simProp.newtonInertia);
2378 case GAUSS_NEWTON: {
2380 pf.RunGaussNewton(basePower, simProp.powerFlowMaxIterations, simProp.powerFlowTolerance,
2381 simProp.initAngle, simProp.accFator, simProp.gaussTolerance, simProp.newtonInertia);
2385 errorMsg = pf.GetErrorMessage();
2386 numIt = pf.GetIterations();
2388 if (showBusyInfo)
delete info;
2393 wxMessageDialog msgDialog(
this, errorMsg, _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
2394 msgDialog.ShowModal();
2397 m_statusBar->SetStatusText(
2398 wxString::Format(_(
"Power flow converge with %d iterations (%ld ms)"), numIt, sw.Time()));
2401 UpdateTextElements();
2408bool Workspace::UpdateTextElements()
2410 bool isTexturesOK =
true;
2411 double basePower = m_properties->GetSimulationPropertiesData().basePower;
2412 if (m_properties->GetSimulationPropertiesData().basePowerUnit == ElectricalUnit::UNIT_kVA)
2414 else if (m_properties->GetSimulationPropertiesData().basePowerUnit == ElectricalUnit::UNIT_MVA)
2416 for (
auto& text : m_textList) {
2417 text->SetFontName(m_properties->GetGeneralPropertiesData().labelFont);
2418 text->SetFontSize(m_properties->GetGeneralPropertiesData().labelFontSize);
2419 text->UpdateText(basePower);
2422 return isTexturesOK;
2425void Workspace::CopySelection()
2428 std::vector<Element*> selectedElements;
2431 for (
auto& element : m_elementList) {
2432 if (
auto bus =
dynamic_cast<Bus*
>(element.get())) {
2433 auto data = bus->GetElectricalData();
2434 data.number = busNumber;
2435 bus->SetElectricalData(data);
2438 if (element->
IsSelected()) { selectedElements.push_back(element.get()); }
2440 for (
auto& text : m_textList) {
2441 if (text->
IsSelected()) { selectedElements.push_back(text.get()); }
2444 if (wxTheClipboard->Open()) {
2445 wxTheClipboard->SetData(dataObject);
2446 wxTheClipboard->Close();
2450bool Workspace::Paste()
2452 if (wxTheClipboard->Open()) {
2455 if (wxTheClipboard->IsSupported(dataObject.GetFormat())) {
2456 if (!wxTheClipboard->GetData(dataObject)) {
2457 wxMessageDialog dialog(
this, _(
"It was not possible to paste from clipboard."), _(
"Error"),
2458 wxOK | wxCENTER | wxICON_ERROR, wxDefaultPosition);
2460 wxTheClipboard->Close();
2465 wxTheClipboard->Close();
2468 wxTheClipboard->Close();
2472 std::vector<Element*> pastedElements;
2473 ElementsLists* elementsLists = dataObject.GetElementsLists();
2476 auto parentList = elementsLists->parentList;
2477 std::vector<Bus*> pastedBusList;
2478 for (
auto it = parentList.begin(), itEnd = parentList.end(); it != itEnd; ++it) {
2481 pastedElements.push_back(copy);
2482 pastedBusList.push_back(
static_cast<Bus*
>(copy));
2483 m_elementList.emplace_back(
static_cast<PowerElement*
>(copy));
2488 auto elementLists = elementsLists->elementList;
2489 for (
auto it = elementLists.begin(), itEnd = elementLists.end(); it != itEnd; ++it) {
2493 if (
Text* text =
dynamic_cast<Text*
>(copy)) {
2495 bool elementExist =
false;
2496 for (
auto& element : m_elementList) {
2497 if (text->GetElement() == element.get()) {
2498 elementExist =
true;
2503 pastedElements.push_back(copy);
2504 m_textList.emplace_back(text);
2511 if (currentParent) {
2512 int parentID = currentParent->
GetID();
2513 bool parentCopied =
false;
2514 for (
size_t k = 0; k < pastedBusList.size(); k++) {
2515 Bus* newParent = pastedBusList[k];
2516 if (parentID == newParent->
GetID()) {
2517 parentCopied =
true;
2526 pastedElements.push_back(copy);
2527 m_elementList.emplace_back(
static_cast<PowerElement*
>(copy));
2533 for (
auto it = pastedBusList.begin(), itEnd = pastedBusList.end(); it != itEnd; ++it) {
2536 for (
auto it = childList.begin(), itEnd = childList.end(); it != itEnd; ++it) {
2538 int childID = currentChild->
GetID();
2539 bool childCopied =
false;
2540 for (
int i = 0; i < (int)pastedElements.size(); i++) {
2541 Element* newChild = pastedElements[i];
2542 if (childID == newChild->
GetID()) {
2554 wxPoint2DDouble leftUpCorner, rightDownCorner;
2555 GetElementsCorners(leftUpCorner, rightDownCorner, pastedElements);
2556 wxPoint2DDouble startPosition = (leftUpCorner + rightDownCorner) / 2.0;
2557 for (
auto it = pastedElements.begin(), itEnd = pastedElements.end(); it != itEnd; ++it) {
2560 element->
Move(m_camera->GetMousePosition());
2561 for (
int i = 0; i < (int)element->
GetParentList().size(); i++) {
2563 element->
MoveNode(parent, m_camera->GetMousePosition());
2568 wxMessageDialog dialog(
this, _(
"It was not possible to paste from clipboard."), _(
"Error"),
2569 wxOK | wxCENTER | wxICON_ERROR, wxDefaultPosition);
2575 m_mode = WorkspaceMode::MODE_PASTE;
2576 m_statusBar->SetStatusText(_(
"Click to paste."));
2582void Workspace::SaveCurrentState()
2589 std::vector< std::shared_ptr<PowerElement> > currentStateElementList;
2590 std::vector< std::shared_ptr<Text> > currentStateTextList;
2592 GetStateListsCopy(m_elementList, m_textList, currentStateElementList, currentStateTextList);
2605 m_elementListState.resize(m_currenteState + 1);
2614 m_textListState.resize(m_currenteState + 1);
2617 if (m_currenteState >= m_maxStates) {
2618 m_currenteState = m_maxStates - 1;
2631 m_elementListState.erase(m_elementListState.begin());
2632 m_textListState.erase(m_textListState.begin());
2635 m_elementListState.emplace_back(currentStateElementList);
2636 m_textListState.emplace_back(currentStateTextList);
2647 wxString pointerStr;
2648 pointerStr.Printf(
"[%d S saved s%d] ", m_currenteState, m_elementListState.size());
2650 for (
auto& element : currentStateElementList) {
2651 pointerStr.Printf(
"%p ", element.get());
2655 pointerStr.Printf(
"[%d S curr s%d] ", m_currenteState, m_elementListState.size());
2657 for (
auto& element : m_elementList) {
2658 pointerStr.Printf(
"%p ", element.get());
2666void Workspace::SetNextState()
2669 if (m_currenteState >= 0 &&
2670 static_cast<size_t>(m_currenteState) < m_elementListState.size() &&
2671 static_cast<size_t>(m_currenteState) < m_textListState.size()) {
2674 GetStateListsCopy(m_elementListState[m_currenteState], m_textListState[m_currenteState], m_elementList, m_textList);
2677#ifdef SHOW_DEBUG_PANEL
2679 wxString pointerStr;
2680 pointerStr.Printf(
"[%d N curr s%d] ", m_currenteState, m_elementListState.size());
2682 for (
Element* element : m_elementList) {
2683 pointerStr.Printf(
"%p ", element);
2691 UpdateTextElements();
2699void Workspace::SetPreviousState()
2702 if (m_currenteState >= 0) {
2705 GetStateListsCopy(m_elementListState[m_currenteState], m_textListState[m_currenteState], m_elementList, m_textList);
2708#ifdef SHOW_DEBUG_PANEL
2710 wxString pointerStr;
2711 pointerStr.Printf(
"[%d P curr s%d] ", m_currenteState, m_elementListState.size());
2713 for (
Element* element : m_elementListState[m_currenteState]) {
2714 pointerStr.Printf(
"%p ", element);
2718 pointerStr.Printf(
"[%d P list s%d] ", m_currenteState, m_elementListState.size());
2720 for (
Element* element : m_elementList) {
2721 pointerStr.Printf(
"%p ", element);
2729 UpdateTextElements();
2737void Workspace::UnselectAll()
2739 for (
auto& element : m_elementList) {
2742 for (
auto& text : m_textList) {
2748void Workspace::EnableHeatMap(
const bool& enable)
2757void Workspace::UpdateElementsID()
2760 for (
auto& element : m_elementList) {
2764 for (
auto& text : m_textList) {
2769void Workspace::OnTimer(wxTimerEvent& event)
2772 m_tipWindow->Close();
2773 m_tipWindow =
nullptr;
2775 if (m_mode == WorkspaceMode::MODE_EDIT) {
2776 for (
auto& element : m_elementList) {
2777 if (element->
Contains(m_camera->GetMousePosition())) {
2779 if (!tipText.IsEmpty()) {
2781 m_tipWindow = wxTipWindow::New(
this, tipText, 10000);
2783 m_tipWindow->SetBoundingRect(wxRect(wxGetMousePosition(), wxSize(1, 1)));
2793void Workspace::SetTextList(
const std::vector< std::shared_ptr<Text> >& textList)
2797 m_textList = std::move(textList);
2799 UpdateTextElements();
2802void Workspace::SetName(wxString name)
2806#ifdef SHOW_DEBUG_PANEL
2807 m_debugFrame->SetTitle(_(
"Debug window: ") + m_name);
2812void Workspace::SetElementList(std::vector< std::shared_ptr<PowerElement> > elementList)
2814 m_elementList = std::move(elementList);
2817void Workspace::SetElementList(std::vector<Element*> elementList)
2819 m_elementList.clear();
2820 for (
auto it = elementList.begin(), itEnd = elementList.end(); it != itEnd; ++it)
2821 m_elementList.emplace_back(
static_cast<PowerElement*
>(*it));
2824void Workspace::OnIdle(wxIdleEvent& event)
2846 m_justOpened =
false;
2847 double limits[2] = { 1.05, 0.95 };
2848 m_hmPlane =
new HMPlane(m_width, m_height, limits);
2854std::vector<Element*> Workspace::GetAllElements()
const
2856 std::vector<Element*> allElements;
2858 for (
auto& element : m_elementList) allElements.push_back(element.get());
2859 for (
auto& text : m_textList) allElements.push_back(text.get());
2864bool Workspace::RunFault()
2866 auto simProp = m_properties->GetSimulationPropertiesData();
2867 double basePower = simProp.basePower;
2868 if (simProp.basePowerUnit == ElectricalUnit::UNIT_MVA)
2870 else if (simProp.basePowerUnit == ElectricalUnit::UNIT_kVA)
2873 Fault fault(GetElementList());
2874 bool result = fault.RunFaultCalculation(basePower);
2876 wxMessageDialog msgDialog(
this, fault.GetErrorMessage(), _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
2877 msgDialog.ShowModal();
2880 UpdateTextElements();
2886std::vector<Element*> Workspace::GetElementList()
const
2888 std::vector<Element*> elementList;
2889 for (
auto& element : m_elementList) elementList.push_back(element.get());
2893bool Workspace::RunSCPower()
2895 Fault fault(GetElementList());
2897 auto simProp = m_properties->GetSimulationPropertiesData();
2898 double basePower = simProp.basePower;
2899 if (simProp.basePowerUnit == ElectricalUnit::UNIT_MVA)
2901 else if (simProp.basePowerUnit == ElectricalUnit::UNIT_kVA)
2904 bool result = fault.RunSCPowerCalcutation(basePower);
2906 wxMessageDialog msgDialog(
this, fault.GetErrorMessage(), _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
2907 msgDialog.ShowModal();
2910 UpdateTextElements();
2916bool Workspace::RunStability()
2921 Electromechanical stability(
this, GetElementList(), m_properties->GetSimulationPropertiesData());
2923 bool result = stability.RunStabilityCalculation();
2925#ifdef SHOW_DEBUG_PANEL
2926 m_debugFrame->AppendDebugMessage(stability.GetDebugMessage());
2932 wxMessageDialog msgDialog(
this, stability.GetErrorMessage(), _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
2933 msgDialog.ShowModal();
2935 m_stabilityTimeVector.clear();
2936 m_stabilityTimeVector = stability.GetTimeVector();
2941 wxMessageDialog msgDialog(
2943 wxString::Format(_(
"The program took %ld ms to run this system.\nDo you wish to open the stability graphics?"),
2945 _(
"Question"), wxYES_NO | wxCENTRE | wxICON_QUESTION);
2946 if (msgDialog.ShowModal() == wxID_YES) {
2947 std::vector<ElementPlotData> plotDataList;
2948 for (
auto& element : m_elementList) {
2950 if (element->GetPlotData(plotData)) plotDataList.push_back(plotData);
2952#ifdef SHOW_SIMULATION_PARAMETERS
2954 plotData.SetName(_(
"Simulation parameters"));
2955 plotData.SetCurveType(ElementPlotData::CurveType::CT_TEST);
2956 plotData.AddData(stability.GetIterationVector(), _(
"Iterations number"));
2957 plotDataList.push_back(plotData);
2959 ChartView* cView =
new ChartView(
this, plotDataList, m_stabilityTimeVector, m_properties->GetGeneralPropertiesData().plotLib);
2965void Workspace::OnMiddleDoubleClick(wxMouseEvent& event)
2971bool Workspace::RunStaticStudies()
2973 bool pfStatus, faultStatus, scStatus, harmStatus;
2974 pfStatus = faultStatus = scStatus = harmStatus =
false;
2976 bool runHarmDistortion = m_properties->GetSimulationPropertiesData().harmDistortionAfterPowerFlow;
2978 pfStatus = RunPowerFlow(runHarmDistortion);
2980 if (m_properties->GetSimulationPropertiesData().faultAfterPowerFlow) {
2981 if (pfStatus) faultStatus = RunFault();
2987 if (m_properties->GetSimulationPropertiesData().scPowerAfterPowerFlow) {
2988 if (pfStatus) scStatus = RunSCPower();
2994 if (runHarmDistortion) {
2995 if (pfStatus) harmStatus = RunHarmonicDistortion(
false);
3001 if (pfStatus && faultStatus && scStatus && harmStatus)
return true;
3006bool Workspace::RunHarmonicDistortion(
bool runPowerFlowBefore)
3008 auto simProp = m_properties->GetSimulationPropertiesData();
3009 double basePower = simProp.basePower;
3010 if (simProp.basePowerUnit == ElectricalUnit::UNIT_MVA)
3012 else if (simProp.basePowerUnit == ElectricalUnit::UNIT_kVA)
3014 if (runPowerFlowBefore) {
3015 if (!RunPowerFlow(
true))
return false;
3018 bool hasEMTElement =
false;
3019 for (
auto& element : m_elementList) {
3020 if (
auto emtElement =
dynamic_cast<EMTElement*
>(element.get())) {
3021 if (emtElement->IsOnline()) hasEMTElement =
true;
3025 HarmLoadConnection loadConnection = simProp.harmLoadConnection;
3028 bool result = pq.CalculateDistortions(basePower, loadConnection);
3031 if (hasEMTElement && result) {
3035 .Icon(wxIcon(Paths::GetDataPath() +
"/images/ribbon/harmDist32.png", wxBITMAP_TYPE_PNG))
3036 .Title(_(
"<b>Calculating Harmonic Flow</b>"))
3037 .
Text(_(
"Please wait..."))
3038 .Foreground(*wxWHITE)
3039 .Background(*wxBLACK)
3040 .Transparency(4 * wxALPHA_OPAQUE / 5)
3042 std::vector<double> thdList;
3043 for (
auto const& bus : pq.GetBusList())
3044 thdList.emplace_back(bus->GetElectricalData().thd);
3046 while (error > 1e-3) {
3048 if (!RunPowerFlow(
false,
false))
return false;
3051 bool result = pq.CalculateDistortions(basePower, loadConnection);
3056 for (
auto const& bus : pq.GetBusList()) {
3057 double errorBus = std::abs(bus->GetElectricalData().thd - thdList[i]);
3060 else if (errorBus > error)
3062 thdList[i] = bus->GetElectricalData().thd;
3069 wxMessageDialog msgDialog(
this, pq.GetErrorMessage(), _(
"Error"), wxOK | wxCENTRE | wxICON_ERROR);
3070 msgDialog.ShowModal();
3073 UpdateTextElements();
3080bool Workspace::RunFrequencyResponse()
3083 std::vector<Bus*> busList;
3084 for (
auto& element : m_elementList) {
3085 if (
Bus* bus =
dynamic_cast<Bus*
>(element.get())) { busList.push_back(bus); }
3088 auto data = m_properties->GetFreqRespData();
3090 FrequencyResponseForm frForm(
this, busList, data.injBusNumber, data.initFreq, data.finalFreq, data.stepFreq);
3092 if (frForm.ShowModal() == wxID_OK) {
3093 data.initFreq = frForm.GetInitFreq();
3094 data.finalFreq = frForm.GetEndFreq();
3095 data.stepFreq = frForm.GetStepFreq();
3096 data.injBusNumber = frForm.GetInjBusNumber();
3097 m_properties->SetFreqRespData(data);
3102 auto simProp = m_properties->GetSimulationPropertiesData();
3103 double basePower = simProp.basePower;
3104 if (simProp.basePowerUnit == ElectricalUnit::UNIT_MVA)
3106 else if (simProp.basePowerUnit == ElectricalUnit::UNIT_kVA)
3109 bool result = pq.CalculateFrequencyResponse(simProp.stabilityFrequency, data.initFreq, data.finalFreq,
3110 data.stepFreq, data.injBusNumber, basePower, simProp.harmLoadConnection);
3112 wxMessageDialog msgDialog(
3113 this, wxString::Format(_(
"Calculations done.\nDo you wish to open the frequency response graphics?")),
3114 _(
"Question"), wxYES_NO | wxCENTRE | wxICON_QUESTION);
3115 if (msgDialog.ShowModal() == wxID_YES) {
3116 std::vector<ElementPlotData> plotDataList;
3117 for (
auto& element : m_elementList) {
3122 ChartView* cView =
new ChartView(
this, plotDataList, pq.GetFrequencies(), m_properties->GetGeneralPropertiesData().plotLib);
3126 UpdateTextElements();
3131void Workspace::OnResize(wxSizeEvent& event)
3133 m_width =
static_cast<float>(GetSize().x) - 1.0f;
3134 m_height =
static_cast<float>(GetSize().y) - 1.0f;
3136 if (m_hmPlane && m_showHM) {
3137 m_hmPlane->ResizeDC(m_width, m_height);
3138 m_showHMTimer =
true;
3139 m_timerHeatMap->Start();
3144void Workspace::OnHeatMapTime(wxTimerEvent& event)
3146 if (m_showHMTimer) {
3149 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 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 bool AddParent(Element *parent, wxPoint2DDouble position, bool isOpening=false)
Add a parent to the element. This method must be used on power elements that connect to a bus,...
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 (including OLTC tap adjustment).
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.