From 290c7b98de23be739020d783d235d94f4b714041 Mon Sep 17 00:00:00 2001 From: robbr48 Date: Tue, 22 Apr 2025 15:36:21 +0200 Subject: [PATCH 01/20] SSP support (wip) --- HopsanCore/include/HopsanCoreGitVersion.h | 4 +-- HopsanGUI/CMakeLists.txt | 4 +-- HopsanGUI/HopsanGUI.pro | 5 +++ HopsanGUI/ModelHandler.cpp | 31 ++++++++++++++-- HopsanGUI/ModelHandler.h | 2 ++ HopsanGUI/PlotArea.cpp | 7 ++++ HopsanGUI/Widgets/ModelWidget.cpp | 4 +-- dependencies/setupSsp4c.sh | 33 +++++++++++++++++ dependencies/ssp4c.cmake | 44 +++++++++++++++++++++++ dependencies/ssp4c.pri | 21 +++++++++++ 10 files changed, 147 insertions(+), 8 deletions(-) create mode 100755 dependencies/setupSsp4c.sh create mode 100644 dependencies/ssp4c.cmake create mode 100644 dependencies/ssp4c.pri diff --git a/HopsanCore/include/HopsanCoreGitVersion.h b/HopsanCore/include/HopsanCoreGitVersion.h index 9bfa58b4c7..71ba092f53 100644 --- a/HopsanCore/include/HopsanCoreGitVersion.h +++ b/HopsanCore/include/HopsanCoreGitVersion.h @@ -2,6 +2,6 @@ // $Id$ #ifndef HOPSANCOREGITVERSION_H_INCLUDED #define HOPSANCOREGITVERSION_H_INCLUDED -#define HOPSANCORE_COMMIT_HASH 250596a1b -#define HOPSANCORE_COMMIT_TIMESTAMP 20200515.1608 +#define HOPSANCORE_COMMIT_HASH 58f0c8472 +#define HOPSANCORE_COMMIT_TIMESTAMP 20250331.1258 #endif diff --git a/HopsanGUI/CMakeLists.txt b/HopsanGUI/CMakeLists.txt index 4b59e0e047..1c7030d5f0 100644 --- a/HopsanGUI/CMakeLists.txt +++ b/HopsanGUI/CMakeLists.txt @@ -20,6 +20,7 @@ include(${CMAKE_CURRENT_LIST_DIR}/../dependencies/discount.cmake) include(${CMAKE_CURRENT_LIST_DIR}/../dependencies/katex.cmake) include(${CMAKE_CURRENT_LIST_DIR}/../dependencies/libzip.cmake) include(${CMAKE_CURRENT_LIST_DIR}/../dependencies/xerces.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/../dependencies/ssp4c.cmake) set(target_name hopsangui) @@ -39,11 +40,10 @@ if(NOT CMAKE_BUILD_TYPE MATCHES Debug) endif() target_link_libraries(${target_name} Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Xml Qt5::Svg Qt5::Network Qt5::Test Qt5::PrintSupport - qwt hopsancore hopsangeneratorgui symhop ops hopsandcp xercesc ) + qwt hopsancore hopsangeneratorgui symhop ops hopsandcp xercesc ssp4c ) if(UNIX) target_link_libraries(${target_name} Qt5::WebEngineWidgets) endif() - target_link_optional_libraries(${target_name} libhopsanremoteclient hopsanhdf5exporter discount katex) set_target_properties(${target_name} PROPERTIES INSTALL_RPATH "\$ORIGIN/../lib") diff --git a/HopsanGUI/HopsanGUI.pro b/HopsanGUI/HopsanGUI.pro index b524f95d69..8589beb7fd 100644 --- a/HopsanGUI/HopsanGUI.pro +++ b/HopsanGUI/HopsanGUI.pro @@ -38,6 +38,11 @@ isEqual(QT_MAJOR_VERSION, 5){ TARGET = $${TARGET}$${DEBUG_EXT} +#-------------------------------------------------------- +# Set the ssp4c paths +include($${PWD}/../dependencies/ssp4c.pri) +#-------------------------------------------------------- + #-------------------------------------------------------- # Set the QWT paths include($${PWD}/../dependencies/qwt.pri) diff --git a/HopsanGUI/ModelHandler.cpp b/HopsanGUI/ModelHandler.cpp index f8919b34ed..9a6367f460 100644 --- a/HopsanGUI/ModelHandler.cpp +++ b/HopsanGUI/ModelHandler.cpp @@ -58,6 +58,8 @@ #include "Widgets/PlotWidget2.h" #include "Widgets/TextEditorWidget.h" #include "Utilities/GUIUtilities.h" +#include "ssp4c.h" +#include "ssp4c_ssd.h" #ifdef USEZMQ #include "RemoteSimulationUtils.h" @@ -251,9 +253,14 @@ void ModelHandler::loadModel() QDir fileDialogOpenDir; QStringList modelFileNames = QFileDialog::getOpenFileNames(gpMainWindowWidget, tr("Choose Model File"), gpConfig->getStringSetting(cfg::dir::loadmodel), - tr("Hopsan Model Files (*.hmf *.xml)")); + tr("Hopsan Model Files (*.hmf *.xml *.ssp)")); for(const QString &modelFileName : modelFileNames) { - loadModel(modelFileName); + if(QFileInfo(modelFileName).suffix() == "ssp") { + loadSsp(modelFileName); + } + else { + loadModel(modelFileName); + } QFileInfo fileInfo = QFileInfo(modelFileName); gpConfig->setStringSetting(cfg::dir::loadmodel, fileInfo.absolutePath()); } @@ -412,6 +419,26 @@ TextEditorWidget *ModelHandler::loadTextFile(QString fileName) return pNewEditor; } +void ModelHandler::loadSsp(QString fileName) +{ + sspHandle *ssp = ssp4c_loadSsp(fileName.toStdString().c_str()); + int ssdCount = ssp4c_getNumberOfSsds(ssp); + for(int i=0; igetPlotTabType() == XYPlotType ) { + qDebug() << "Correct plot type"; QWidget::dropEvent(event); mpPainterWidget->clearRect(); if (event->mimeData()->hasText()) { + qDebug() << "Has text"; QString mimeText = event->mimeData()->text(); if(mimeText.startsWith("HOPSANPLOTDATA:")) { + qDebug() << "Correct text"; qDebug() << mimeText; QStringList fields = mimeText.split(":"); if (fields.size() > 3) { + qDebug() << "Correct size"; QString &name = fields[1]; QString &model = fields[3]; bool parseOk = false; @@ -1048,10 +1053,12 @@ void PlotArea::dropEvent(QDropEvent *event) } if (pContainer) { + qDebug() << "Found container"; SharedVectorVariableT data = pContainer->getLogDataHandler()->getVectorVariable(name, gen); // If we have found data then add it to the plot if (data) { + qDebug() << "Found data"; QCursor cursor; if(this->mapFromGlobal(cursor.pos()).y() > getQwtPlot()->canvas()->height()*2.0/3.0+getQwtPlot()->canvas()->y()+10 && getNumberOfCurves() >= 1) { diff --git a/HopsanGUI/Widgets/ModelWidget.cpp b/HopsanGUI/Widgets/ModelWidget.cpp index 11cb768ea8..7e27175c38 100644 --- a/HopsanGUI/Widgets/ModelWidget.cpp +++ b/HopsanGUI/Widgets/ModelWidget.cpp @@ -981,7 +981,7 @@ void ModelWidget::importModelParametersFromSsv() if(componentName.isEmpty()) { //Set system parameter CoreParameterData parameter; pSystem->getParameter(parameterName, parameter); - if(ssv::dataTypeTranslator[parameter.mType] != ssvParameter.dataType) { + if(!parameter.mName.isEmpty() && ssv::dataTypeTranslator[parameter.mType] != ssvParameter.dataType) { gpMessageHandler->addWarningMessage("Wrong data type for unit: \""+ssvParameter.name+"\", ignoring value. ("+ssv::dataTypeTranslator[parameter.mType]+" != "+ssvParameter.dataType+")"); } else if(parameter.mType == ssv::datatype::real) { //Handle units for "Real" type parameters only @@ -1012,7 +1012,7 @@ void ModelWidget::importModelParametersFromSsv() } } else { - pSystem->setParameterValue(parameterName, ssvParameter.value); //Ignore units for non-Real parameters + pSystem->setOrAddParameter(parameter, true); //Ignore units for non-Real parameters } } else { //Set component parameter diff --git a/dependencies/setupSsp4c.sh b/dependencies/setupSsp4c.sh new file mode 100755 index 0000000000..7ca294c520 --- /dev/null +++ b/dependencies/setupSsp4c.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Shell script building Hopsan dependency ssp4c + +basedir=$(pwd) + +name=ssp4c +codedir=${basedir}/${name}-code +builddir=${basedir}/${name}-build +installdir=${basedir}/${name} + + +# Download and verify +#./download-dependencies.py ${name} + +# Include general settings +source setHopsanBuildPaths.sh + +# Create build dir and enter it +mkdir -p $builddir +cd $builddir + +# Generate makefiles +cmake -Wno-dev -DCMAKE_INSTALL_PREFIX=${installdir} -DCMAKE_BUILD_TYPE=Release -DSSP4C_BUILD_SHARED=OFF ${codedir} + +# Build and install +cmake --build . --parallel 8 +cmake --build . --target install + + +# Return to basedir +cd $basedir +echo "setupSsp4c.sh done!" diff --git a/dependencies/ssp4c.cmake b/dependencies/ssp4c.cmake new file mode 100644 index 0000000000..80f3b77180 --- /dev/null +++ b/dependencies/ssp4c.cmake @@ -0,0 +1,44 @@ +set(local_ssp4c_dir ${CMAKE_CURRENT_LIST_DIR}/ssp4c) + +if (EXISTS ${local_ssp4c_dir}) + + set(zlibstatic_name zlibstatic) + if (NOT WIN32) + set(zlibstatic_name z) + endif() + + set(ssp4c_dbg_ext "") + if (WIN32) + set(ssp4c_dbg_ext d) + endif() + + set(static_ssp4c ${local_ssp4c_dir}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}ssp4c${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(static_ssp4c_d ${local_ssp4c_dir}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}ssp4c${ssp4c_dbg_ext}${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(static_zlib ${local_ssp4c_dir}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}${zlibstatic_name}${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(static_zlib_d ${local_ssp4c_dir}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}${zlibstatic_name}${ssp4c_dbg_ext}${CMAKE_STATIC_LIBRARY_SUFFIX}) + + add_library(ssp4c STATIC IMPORTED) + + set_target_properties(ssp4c PROPERTIES + IMPORTED_LOCATION ${static_ssp4c} + IMPORTED_LOCATION_DEBUG ${static_ssp4c_d} + INTERFACE_INCLUDE_DIRECTORIES ${local_ssp4c_dir}/include + INTERFACE_COMPILE_DEFINITIONS "USESSP4C;SSP4C_STATIC") + + if (EXISTS ${static_zlib}) + message(STATUS "Found static zlib in ${local_ssp4c_dir}") + add_library(ssp4c_zlib STATIC IMPORTED) + set_target_properties(ssp4c_zlib PROPERTIES + IMPORTED_LOCATION ${static_zlib} + IMPORTED_LOCATION_DEBUG ${static_zlib_d}) + target_link_libraries(ssp4c INTERFACE ssp4c_zlib) + else() + find_package(ZLIB MODULE REQUIRED) + target_link_libraries(ssp4c INTERFACE ZLIB::ZLIB) + endif() + + install(DIRECTORY ${local_ssp4c_dir} DESTINATION dependencies) + message(STATUS "Building with ssp4c support") +else() + message(WARNING "Building without ssp4c support") +endif() diff --git a/dependencies/ssp4c.pri b/dependencies/ssp4c.pri new file mode 100644 index 0000000000..5930bd3359 --- /dev/null +++ b/dependencies/ssp4c.pri @@ -0,0 +1,21 @@ +ssp4c_dir = $${PWD}/ssp4c + +exists($${ssp4c_dir}) { + DEFINES *= USESSP4C + DEFINES *= SSP4C_STATIC + INCLUDEPATH *= $${ssp4c_dir}/include + + macx { + # Not supported + } win32 { + CONFIG(debug, debug|release) { + ssp4c_dbg_ext = d + } + LIBS *= -L$${ssp4c_dir}/lib -lssp4c$${ssp4c_dbg_ext} + } else { + LIBS *= -L$${ssp4c_dir}/lib -lssp4c -lz + } + message(Found local ssp4c) +} else { + message(Compiling without ssp4c) +} From 6d0b93ed60b6fa9ce504f2aea764f1be3878ad04 Mon Sep 17 00:00:00 2001 From: robbr48 Date: Tue, 22 Apr 2025 15:36:21 +0200 Subject: [PATCH 02/20] SSP support (wip) --- HopsanGUI/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/HopsanGUI/CMakeLists.txt b/HopsanGUI/CMakeLists.txt index 1c7030d5f0..7bc5b10cf2 100644 --- a/HopsanGUI/CMakeLists.txt +++ b/HopsanGUI/CMakeLists.txt @@ -44,6 +44,7 @@ target_link_libraries(${target_name} Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Xml Qt if(UNIX) target_link_libraries(${target_name} Qt5::WebEngineWidgets) endif() + target_link_optional_libraries(${target_name} libhopsanremoteclient hopsanhdf5exporter discount katex) set_target_properties(${target_name} PROPERTIES INSTALL_RPATH "\$ORIGIN/../lib") From b34850ba65c0770d37a94d4e753d89746c2321cb Mon Sep 17 00:00:00 2001 From: Robert Braun Date: Mon, 2 Mar 2026 14:22:53 +0100 Subject: [PATCH 03/20] Load FMUs and connetors from SSD --- HopsanGUI/ModelHandler.cpp | 72 +++++++++++++++++++++++++++++++++++++ dependencies/setupSsp4c.bat | 33 +++++++++++++++++ dependencies/ssp4c.pri | 3 +- 3 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 dependencies/setupSsp4c.bat diff --git a/HopsanGUI/ModelHandler.cpp b/HopsanGUI/ModelHandler.cpp index 9a6367f460..67c859a84f 100644 --- a/HopsanGUI/ModelHandler.cpp +++ b/HopsanGUI/ModelHandler.cpp @@ -46,6 +46,7 @@ #include "MessageHandler.h" #include "MainWindow.h" #include "ModelHandler.h" +#include "GUIPort.h" #include "SimulationThreadHandler.h" #include "version_gui.h" #include "Widgets/DebuggerWidget.h" @@ -60,6 +61,9 @@ #include "Utilities/GUIUtilities.h" #include "ssp4c.h" #include "ssp4c_ssd.h" +#include "ssp4c_ssd_component.h" +#include "ssp4c_ssd_connector.h" +#include "ssp4c_ssd_connection.h" #ifdef USEZMQ #include "RemoteSimulationUtils.h" @@ -422,6 +426,8 @@ TextEditorWidget *ModelHandler::loadTextFile(QString fileName) void ModelHandler::loadSsp(QString fileName) { sspHandle *ssp = ssp4c_loadSsp(fileName.toStdString().c_str()); + + qDebug() << "SSP unzipped path: " << ssp4c_getUnzippedLocation(ssp); int ssdCount = ssp4c_getNumberOfSsds(ssp); for(int i=0; igetCurrentTopLevelSystem(); + if(pSystem) { + QPointF pos = pSystem->getGraphicsViewport().mCenter; + ModelObject *pFmuComponent = pSystem->addModelObject("FMIWrapper", pos); + if(pFmuComponent) { + QString unzippedLocation = ssp4c_getUnzippedLocation(ssp); + QString source = ssp4c_ssd_component_getSource(comp); + pFmuComponent->setParameterValue("path", unzippedLocation+"/"+source); + pSystem->renameModelObject(pFmuComponent->getName(), ssp4c_ssd_component_getName(comp)); + } + } + } + + int connetionCount = ssp4c_ssd_getNumberOfConnections(ssd); + for(int i=0; igetCurrentTopLevelSystem(); + if(pSystem) { + ModelObject *pStartComponent = pSystem->getModelObject(ssp4c_ssd_connection_getStartElement(con)); + ModelObject *pEndComponent = pSystem->getModelObject(ssp4c_ssd_connection_getEndElement(con)); + if(pStartComponent != nullptr && pEndComponent != nullptr) { + qDebug() << "Found components!"; + QString startPortName = ssp4c_ssd_connection_getStartConnector(con); + startPortName.replace(".", "_"); + QString endPortName = ssp4c_ssd_connection_getEndConnector(con); + endPortName.replace(".", "_"); + Port *pStartPort = pStartComponent->getPort(startPortName); + Port *pEndPort = pEndComponent->getPort(endPortName); + if(pStartPort != nullptr && pEndComponent != nullptr) { + qDebug() << "Found ports!"; + SystemObject *sysObj = this->getCurrentViewContainerObject(); + Connector *pCon = sysObj->createConnector(pStartPort, pEndPort, NoUndo); + if(pCon != nullptr) { + qDebug() << "Creatd connetor!"; + QVector pointVector; + QStringList geometryList; + pointVector.push_back(pCon->getStartPort()->boundingRect().center()); + pointVector.push_back(pCon->getEndPort()->boundingRect().center()); + geometryList.clear(); + geometryList.append(hmf::connector::diagonal); + pCon->setPointsAndGeometries(pointVector, geometryList); + } + } + } + } + } } } diff --git a/dependencies/setupSsp4c.bat b/dependencies/setupSsp4c.bat new file mode 100644 index 0000000000..b287d1fcbc --- /dev/null +++ b/dependencies/setupSsp4c.bat @@ -0,0 +1,33 @@ +@ECHO OFF + +REM Bat script for building ssp4c dependency automatically + +setlocal +set basedir=%~dp0 +set name=ssp4c +set codedir=%basedir%\%name%-code +set builddir=%basedir%\%name%-build +set installdir=%basedir%\%name% + +REM Setup PATH +call setHopsanBuildPaths.bat + +REM build and install +if exist %builddir% ( + echo Removing existing build directory %builddir% + rmdir /S /Q %builddir% +) +mkdir %builddir% +pushd %builddir% + +cmake -Wno-dev -G %HOPSAN_BUILD_CMAKE_GENERATOR% -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=%installdir% -DSSP4C_BUILD_SHARED=OFF %codedir% +cmake --build . --config Debug --parallel 8 +cmake --build . --config Debug --target install +popd + +echo. +echo setupSsp4c.bat done!" +if "%HOPSAN_BUILD_SCRIPT_NOPAUSE%" == "" ( + pause +) +endlocal diff --git a/dependencies/ssp4c.pri b/dependencies/ssp4c.pri index 5930bd3359..815387e84f 100644 --- a/dependencies/ssp4c.pri +++ b/dependencies/ssp4c.pri @@ -4,12 +4,13 @@ exists($${ssp4c_dir}) { DEFINES *= USESSP4C DEFINES *= SSP4C_STATIC INCLUDEPATH *= $${ssp4c_dir}/include + message(SSP includes: $${ssp4c_dir}/include) macx { # Not supported } win32 { CONFIG(debug, debug|release) { - ssp4c_dbg_ext = d + ssp4c_dbg_ext = } LIBS *= -L$${ssp4c_dir}/lib -lssp4c$${ssp4c_dbg_ext} } else { From 1baf3c74b01f368fd8fd4790ad1e8d0f7e243d07 Mon Sep 17 00:00:00 2001 From: robbr48 Date: Tue, 22 Apr 2025 15:36:21 +0200 Subject: [PATCH 04/20] SSP support (wip) --- HopsanGUI/CMakeLists.txt | 3 +++ HopsanGUI/ModelHandler.cpp | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/HopsanGUI/CMakeLists.txt b/HopsanGUI/CMakeLists.txt index 7bc5b10cf2..21ec025080 100644 --- a/HopsanGUI/CMakeLists.txt +++ b/HopsanGUI/CMakeLists.txt @@ -41,9 +41,12 @@ endif() target_link_libraries(${target_name} Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Xml Qt5::Svg Qt5::Network Qt5::Test Qt5::PrintSupport qwt hopsancore hopsangeneratorgui symhop ops hopsandcp xercesc ssp4c ) +<<<<<<< HEAD if(UNIX) target_link_libraries(${target_name} Qt5::WebEngineWidgets) endif() +======= +>>>>>>> 226b534b5 (SSP support (wip)) target_link_optional_libraries(${target_name} libhopsanremoteclient hopsanhdf5exporter discount katex) diff --git a/HopsanGUI/ModelHandler.cpp b/HopsanGUI/ModelHandler.cpp index 67c859a84f..17759a2b0e 100644 --- a/HopsanGUI/ModelHandler.cpp +++ b/HopsanGUI/ModelHandler.cpp @@ -61,9 +61,12 @@ #include "Utilities/GUIUtilities.h" #include "ssp4c.h" #include "ssp4c_ssd.h" +<<<<<<< HEAD #include "ssp4c_ssd_component.h" #include "ssp4c_ssd_connector.h" #include "ssp4c_ssd_connection.h" +======= +>>>>>>> 226b534b5 (SSP support (wip)) #ifdef USEZMQ #include "RemoteSimulationUtils.h" @@ -426,8 +429,11 @@ TextEditorWidget *ModelHandler::loadTextFile(QString fileName) void ModelHandler::loadSsp(QString fileName) { sspHandle *ssp = ssp4c_loadSsp(fileName.toStdString().c_str()); +<<<<<<< HEAD qDebug() << "SSP unzipped path: " << ssp4c_getUnzippedLocation(ssp); +======= +>>>>>>> 226b534b5 (SSP support (wip)) int ssdCount = ssp4c_getNumberOfSsds(ssp); for(int i=0; i>>>>>> 226b534b5 (SSP support (wip)) } } From 1954215dcbd7ca7c8e6b163e31808eb48d4af306 Mon Sep 17 00:00:00 2001 From: robbr48 Date: Tue, 22 Apr 2025 15:36:21 +0200 Subject: [PATCH 05/20] SSP support (wip) --- HopsanGUI/CMakeLists.txt | 3 +++ HopsanGUI/ModelHandler.cpp | 13 ------------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/HopsanGUI/CMakeLists.txt b/HopsanGUI/CMakeLists.txt index 21ec025080..6e7b6b6fd3 100644 --- a/HopsanGUI/CMakeLists.txt +++ b/HopsanGUI/CMakeLists.txt @@ -42,11 +42,14 @@ endif() target_link_libraries(${target_name} Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Xml Qt5::Svg Qt5::Network Qt5::Test Qt5::PrintSupport qwt hopsancore hopsangeneratorgui symhop ops hopsandcp xercesc ssp4c ) <<<<<<< HEAD +<<<<<<< HEAD if(UNIX) target_link_libraries(${target_name} Qt5::WebEngineWidgets) endif() ======= >>>>>>> 226b534b5 (SSP support (wip)) +======= +>>>>>>> 8bad348f9 (SSP support (wip)) target_link_optional_libraries(${target_name} libhopsanremoteclient hopsanhdf5exporter discount katex) diff --git a/HopsanGUI/ModelHandler.cpp b/HopsanGUI/ModelHandler.cpp index 17759a2b0e..e2558ff786 100644 --- a/HopsanGUI/ModelHandler.cpp +++ b/HopsanGUI/ModelHandler.cpp @@ -61,12 +61,6 @@ #include "Utilities/GUIUtilities.h" #include "ssp4c.h" #include "ssp4c_ssd.h" -<<<<<<< HEAD -#include "ssp4c_ssd_component.h" -#include "ssp4c_ssd_connector.h" -#include "ssp4c_ssd_connection.h" -======= ->>>>>>> 226b534b5 (SSP support (wip)) #ifdef USEZMQ #include "RemoteSimulationUtils.h" @@ -429,11 +423,7 @@ TextEditorWidget *ModelHandler::loadTextFile(QString fileName) void ModelHandler::loadSsp(QString fileName) { sspHandle *ssp = ssp4c_loadSsp(fileName.toStdString().c_str()); -<<<<<<< HEAD - qDebug() << "SSP unzipped path: " << ssp4c_getUnzippedLocation(ssp); -======= ->>>>>>> 226b534b5 (SSP support (wip)) int ssdCount = ssp4c_getNumberOfSsds(ssp); for(int i=0; i>>>>>> 226b534b5 (SSP support (wip)) } } From a085c9f4a496b74ae2012d409e7854833aa967fe Mon Sep 17 00:00:00 2001 From: Robert Braun Date: Fri, 6 Mar 2026 14:57:35 +0100 Subject: [PATCH 06/20] SSP Explorer Widget --- HopsanGUI/HopsanGUI.pro | 2 + HopsanGUI/MainWindow.cpp | 14 ++ HopsanGUI/MainWindow.h | 3 + HopsanGUI/ModelHandler.cpp | 86 +-------- HopsanGUI/Resources.qrc | 2 + HopsanGUI/Widgets/SSPWidget.cpp | 178 ++++++++++++++++++ HopsanGUI/Widgets/SSPWidget.h | 33 ++++ HopsanGUI/global.h | 2 + HopsanGUI/graphics/uiicons/svg/Hopsan-SSD.svg | 71 +++++++ HopsanGUI/graphics/uiicons/svg/Hopsan-SSP.svg | 71 +++++++ 10 files changed, 379 insertions(+), 83 deletions(-) create mode 100644 HopsanGUI/Widgets/SSPWidget.cpp create mode 100644 HopsanGUI/Widgets/SSPWidget.h create mode 100644 HopsanGUI/graphics/uiicons/svg/Hopsan-SSD.svg create mode 100644 HopsanGUI/graphics/uiicons/svg/Hopsan-SSP.svg diff --git a/HopsanGUI/HopsanGUI.pro b/HopsanGUI/HopsanGUI.pro index 8589beb7fd..3fa9241781 100644 --- a/HopsanGUI/HopsanGUI.pro +++ b/HopsanGUI/HopsanGUI.pro @@ -224,6 +224,7 @@ SOURCES += main.cpp \ ProgressBarThread.cpp \ GUIPortAppearance.cpp \ GUIConnectorAppearance.cpp \ + Widgets/SSPWidget.cpp \ Widgets/SystemParametersWidget.cpp \ PlotWindow.cpp \ GUIObjects/GUIWidgets.cpp \ @@ -302,6 +303,7 @@ HEADERS += MainWindow.h \ GUIPort.h \ Widgets/MessageWidget.h \ InitializationThread.h \ + Widgets/SSPWidget.h \ version_gui.h \ Dialogs/OptionsDialog.h \ UndoStack.h \ diff --git a/HopsanGUI/MainWindow.cpp b/HopsanGUI/MainWindow.cpp index 3ec746b136..9ca326be86 100644 --- a/HopsanGUI/MainWindow.cpp +++ b/HopsanGUI/MainWindow.cpp @@ -80,6 +80,7 @@ #include "Widgets/DataExplorer.h" #include "Widgets/FindWidget.h" #include "Widgets/TextEditorWidget.h" +#include "Widgets/SSPWidget.h" #include "Dialogs/OptionsDialog.h" #include "Dialogs/AboutDialog.h" @@ -104,6 +105,7 @@ ModelHandler *gpModelHandler = 0; PlotWidget2 *gpPlotWidget = 0; CentralTabWidget *gpCentralTabWidget = 0; SystemParametersWidget *gpSystemParametersWidget = 0; +SSPWidget *gpSSPWidget = 0; UndoWidget *gpUndoWidget = 0; LibraryHandler *gpLibraryHandler = 0; HelpPopUpWidget *gpHelpPopupWidget = 0; @@ -275,6 +277,11 @@ void MainWindow::createContents() gpSystemParametersWidget = mpSystemParametersWidget; mpSystemParametersWidget->setVisible(false); + //Create the SSP widget and hide it + mpSSPWidget = new SSPWidget(this); + gpSSPWidget = mpSSPWidget; + //mpSSPWidget->setVisible(false); + // Create the HVC Widget mpHVCWidget = new HVCWidget(this); mpHVCWidget->setVisible(false); @@ -296,6 +303,13 @@ void MainWindow::createContents() addDockWidget(Qt::RightDockWidgetArea, mpSystemParametersDock); mpSystemParametersDock->hide(); + //Create the SSP dock widget and hide it + mpSSPWidgetDock = new QDockWidget(tr("SSP Explorer"), this); + mpSSPWidgetDock->setAllowedAreas((Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea)); + mpSSPWidgetDock->setWidget(mpSSPWidget); + addDockWidget(Qt::RightDockWidgetArea, mpSSPWidgetDock); + //mpSSPWidgetDock->hide(); + //Create the undo dock widget and hide it mpUndoWidgetDock = new QDockWidget(tr("Undo History"), this); mpUndoWidgetDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); diff --git a/HopsanGUI/MainWindow.h b/HopsanGUI/MainWindow.h index a8438969eb..309323dd7c 100644 --- a/HopsanGUI/MainWindow.h +++ b/HopsanGUI/MainWindow.h @@ -66,6 +66,7 @@ class MainWindowLineEdit; class DataExplorer; class FindWidget; class HelpPopUpWidget; +class SSPWidget; class SimulationTimeEdit : public QWidget @@ -132,6 +133,7 @@ class MainWindow : public QMainWindow WelcomeWidget *mpWelcomeWidget; LibraryWidget *mpLibraryWidget; SystemParametersWidget *mpSystemParametersWidget; + SSPWidget *mpSSPWidget; QStatusBar *mpStatusBar; //Not used, but gives some nice extra space at bottom :) QGridLayout *mpCentralGridLayout; @@ -291,6 +293,7 @@ private slots: QDockWidget *mpPlotWidgetDock; QDockWidget *mpUndoWidgetDock; QDockWidget *mpSystemParametersDock; + QDockWidget *mpSSPWidgetDock; QWidget *mpCentralWidget; QGridLayout *mpTabgrid; diff --git a/HopsanGUI/ModelHandler.cpp b/HopsanGUI/ModelHandler.cpp index e2558ff786..9b48559ada 100644 --- a/HopsanGUI/ModelHandler.cpp +++ b/HopsanGUI/ModelHandler.cpp @@ -57,8 +57,10 @@ #include "Widgets/ProjectTabWidget.h" #include "Widgets/DataExplorer.h" #include "Widgets/PlotWidget2.h" +#include "Widgets/SSPWidget.h" #include "Widgets/TextEditorWidget.h" #include "Utilities/GUIUtilities.h" + #include "ssp4c.h" #include "ssp4c_ssd.h" @@ -422,89 +424,7 @@ TextEditorWidget *ModelHandler::loadTextFile(QString fileName) void ModelHandler::loadSsp(QString fileName) { - sspHandle *ssp = ssp4c_loadSsp(fileName.toStdString().c_str()); - - int ssdCount = ssp4c_getNumberOfSsds(ssp); - for(int i=0; igetCurrentTopLevelSystem(); - if(pSystem) { - QPointF pos = pSystem->getGraphicsViewport().mCenter; - ModelObject *pFmuComponent = pSystem->addModelObject("FMIWrapper", pos); - if(pFmuComponent) { - QString unzippedLocation = ssp4c_getUnzippedLocation(ssp); - QString source = ssp4c_ssd_component_getSource(comp); - pFmuComponent->setParameterValue("path", unzippedLocation+"/"+source); - pSystem->renameModelObject(pFmuComponent->getName(), ssp4c_ssd_component_getName(comp)); - } - } - } - - int connetionCount = ssp4c_ssd_getNumberOfConnections(ssd); - for(int i=0; igetCurrentTopLevelSystem(); - if(pSystem) { - ModelObject *pStartComponent = pSystem->getModelObject(ssp4c_ssd_connection_getStartElement(con)); - ModelObject *pEndComponent = pSystem->getModelObject(ssp4c_ssd_connection_getEndElement(con)); - if(pStartComponent != nullptr && pEndComponent != nullptr) { - qDebug() << "Found components!"; - QString startPortName = ssp4c_ssd_connection_getStartConnector(con); - startPortName.replace(".", "_"); - QString endPortName = ssp4c_ssd_connection_getEndConnector(con); - endPortName.replace(".", "_"); - Port *pStartPort = pStartComponent->getPort(startPortName); - Port *pEndPort = pEndComponent->getPort(endPortName); - if(pStartPort != nullptr && pEndComponent != nullptr) { - qDebug() << "Found ports!"; - SystemObject *sysObj = this->getCurrentViewContainerObject(); - Connector *pCon = sysObj->createConnector(pStartPort, pEndPort, NoUndo); - if(pCon != nullptr) { - qDebug() << "Creatd connetor!"; - QVector pointVector; - QStringList geometryList; - pointVector.push_back(pCon->getStartPort()->boundingRect().center()); - pointVector.push_back(pCon->getEndPort()->boundingRect().center()); - geometryList.clear(); - geometryList.append(hmf::connector::diagonal); - pCon->setPointsAndGeometries(pointVector, geometryList); - } - } - } - } - } - } + gpSSPWidget->addSSP(QFileInfo(fileName)); } diff --git a/HopsanGUI/Resources.qrc b/HopsanGUI/Resources.qrc index c103487db5..da998f07ba 100644 --- a/HopsanGUI/Resources.qrc +++ b/HopsanGUI/Resources.qrc @@ -156,6 +156,8 @@ graphics/uiicons/svg/Hopsan-NewDcpModel.svg graphics/builtinCAF/hidden/dcpcomponent.xml graphics/objecticons/dcpcomponent.svg + graphics/uiicons/svg/Hopsan-SSP.svg + graphics/uiicons/svg/Hopsan-SSD.svg ../licenseNoticeGPLv3 diff --git a/HopsanGUI/Widgets/SSPWidget.cpp b/HopsanGUI/Widgets/SSPWidget.cpp new file mode 100644 index 0000000000..ff6c8fd2dc --- /dev/null +++ b/HopsanGUI/Widgets/SSPWidget.cpp @@ -0,0 +1,178 @@ +/*----------------------------------------------------------------------------- + + Copyright 2017 Hopsan Group + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + The full license is available in the file GPLv3. + For details about the 'Hopsan Group' or information about Authors and + Contributors see the HOPSANGROUP and AUTHORS files that are located in + the Hopsan source code root directory. + +-----------------------------------------------------------------------------*/ + +//! +//! @file SSPWidget.cpp +//! @brief A widget for exploring SSPs +//! @author Robert Braun +//! +//$Id$ + +#include "SSPWidget.h" +#include "GUIObjects/GUIContainerObject.h" +#include "GUIObjects/GUIModelObject.h" +#include "GUIPort.h" +#include "ModelHandler.h" +#include "common.h" +#include +#include +#include + +#include "global.h" +#include "ssp4c.h" +#include "ssp4c_ssd.h" +#include "ssp4c_ssd_component.h" +#include "ssp4c_ssd_connection.h" +#include "ssp4c_ssd_connector.h" + +SSPWidget::SSPWidget(QWidget *pParent) +{ + this->setObjectName("SSPWidget"); + this->setWindowTitle("SSP Explorer"); + + mpTree = new QTreeWidget(this); + mpTree->setMouseTracking(true); + mpTree->setHeaderHidden(true); + mpTree->setColumnCount(1); + + + QVBoxLayout *pLayout = new QVBoxLayout(this);; + pLayout->addWidget(mpTree); +} + +void SSPWidget::addSSP(QFileInfo path) +{ + sspHandle *ssp = ssp4c_loadSsp(path.absoluteFilePath().toStdString().c_str()); + + QFont boldFont = qApp->font(); + boldFont.setBold(true); + + QTreeWidgetItem *pSspItem = new QTreeWidgetItem(); + pSspItem->setFont(0,boldFont); + pSspItem->setIcon(0, QIcon(QString(ICONPATH) + "svg/Hopsan-SSP.svg")); + pSspItem->setText(0,path.fileName()); + mpTree->addTopLevelItem(pSspItem); + + connect(mpTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(openSSDModel(QTreeWidgetItem*,int))); + + qDebug() << "SSP unzipped path: " << ssp4c_getUnzippedLocation(ssp); + int ssdCount = ssp4c_getNumberOfSsds(ssp); + for(int i=0; isetIcon(0, QIcon(QString(ICONPATH) + "svg/Hopsan-SSD.svg")); + pSsdItem->setText(0,ssp4c_ssd_getName(ssd)); + pSspItem->addChild(pSsdItem); + + itemToSspMap.insert(pSsdItem, ssp); + itemToSsdMap.insert(pSsdItem, ssd); + } +} + +void SSPWidget::openSSDModel(QTreeWidgetItem *item, int) +{ + sspHandle *ssp = itemToSspMap[item]; + ssdHandle* ssd = itemToSsdMap[item]; + + //! @todo Check so that SSD is not already open somehow + + gpModelHandler->addNewModel(ssp4c_ssd_getName(ssd)); + + int componentCount = ssp4c_ssd_getNumberOfComponents(ssd); + for(int i=0; igetCurrentTopLevelSystem(); + if(pSystem) { + QPointF pos = pSystem->getGraphicsViewport().mCenter; + ModelObject *pFmuComponent = pSystem->addModelObject("FMIWrapper", pos); + if(pFmuComponent) { + QString unzippedLocation = ssp4c_getUnzippedLocation(ssp); + QString source = ssp4c_ssd_component_getSource(comp); + pFmuComponent->setParameterValue("path", unzippedLocation+"/"+source); + pSystem->renameModelObject(pFmuComponent->getName(), ssp4c_ssd_component_getName(comp)); + } + } + } + + int connetionCount = ssp4c_ssd_getNumberOfConnections(ssd); + for(int i=0; igetCurrentTopLevelSystem(); + if(pSystem) { + ModelObject *pStartComponent = pSystem->getModelObject(ssp4c_ssd_connection_getStartElement(con)); + ModelObject *pEndComponent = pSystem->getModelObject(ssp4c_ssd_connection_getEndElement(con)); + if(pStartComponent != nullptr && pEndComponent != nullptr) { + qDebug() << "Found components!"; + QString startPortName = ssp4c_ssd_connection_getStartConnector(con); + startPortName.replace(".", "_"); + QString endPortName = ssp4c_ssd_connection_getEndConnector(con); + endPortName.replace(".", "_"); + Port *pStartPort = pStartComponent->getPort(startPortName); + Port *pEndPort = pEndComponent->getPort(endPortName); + if(pStartPort != nullptr && pEndComponent != nullptr) { + qDebug() << "Found ports!"; + SystemObject *sysObj = gpModelHandler->getCurrentViewContainerObject(); + Connector *pCon = sysObj->createConnector(pStartPort, pEndPort, NoUndo); + if(pCon != nullptr) { + qDebug() << "Creatd connetor!"; + QVector pointVector; + QStringList geometryList; + pointVector.push_back(pCon->getStartPort()->boundingRect().center()); + pointVector.push_back(pCon->getEndPort()->boundingRect().center()); + geometryList.clear(); + geometryList.append(hmf::connector::diagonal); + pCon->setPointsAndGeometries(pointVector, geometryList); + } + } + } + } + } + +} + diff --git a/HopsanGUI/Widgets/SSPWidget.h b/HopsanGUI/Widgets/SSPWidget.h new file mode 100644 index 0000000000..bf9ad32880 --- /dev/null +++ b/HopsanGUI/Widgets/SSPWidget.h @@ -0,0 +1,33 @@ +#include +#include +#include + +#ifndef SSPWIDGET_H +#define SSPWIDGET_H + +#include + +//Forward declarations +class sspHandle; +class ssdHandle; + +class SSPWidget : public QWidget +{ + Q_OBJECT +public: + SSPWidget(QWidget *pParent=0); + + void addSSP(QFileInfo path); +public slots: + +protected slots: + void openSSDModel(QTreeWidgetItem*item, int); + +private: + QTreeWidget *mpTree; + + QMap itemToSspMap; + QMap itemToSsdMap; +}; + +#endif // SSPWIDGET_H diff --git a/HopsanGUI/global.h b/HopsanGUI/global.h index ed3be14611..bf21da51eb 100644 --- a/HopsanGUI/global.h +++ b/HopsanGUI/global.h @@ -61,6 +61,7 @@ class OptionsDialog; class QGridLayout; class FindWidget; class PlotWidget2; +class SSPWidget; // Global pointer to the main window and QWidget cast version extern MainWindow* gpMainWindow; @@ -80,6 +81,7 @@ extern ModelHandler *gpModelHandler; extern PlotWidget2 *gpPlotWidget; extern CentralTabWidget *gpCentralTabWidget; extern SystemParametersWidget *gpSystemParametersWidget; +extern SSPWidget *gpSSPWidget; extern UndoWidget *gpUndoWidget; extern LibraryHandler *gpLibraryHandler; extern HelpPopUpWidget *gpHelpPopupWidget; diff --git a/HopsanGUI/graphics/uiicons/svg/Hopsan-SSD.svg b/HopsanGUI/graphics/uiicons/svg/Hopsan-SSD.svg new file mode 100644 index 0000000000..b8cad9fb53 --- /dev/null +++ b/HopsanGUI/graphics/uiicons/svg/Hopsan-SSD.svg @@ -0,0 +1,71 @@ + + + +image/svg+xml diff --git a/HopsanGUI/graphics/uiicons/svg/Hopsan-SSP.svg b/HopsanGUI/graphics/uiicons/svg/Hopsan-SSP.svg new file mode 100644 index 0000000000..ea17b1c8f0 --- /dev/null +++ b/HopsanGUI/graphics/uiicons/svg/Hopsan-SSP.svg @@ -0,0 +1,71 @@ + + + +image/svg+xml From 337e29546e70374aef222b0698f3364a5a8071b2 Mon Sep 17 00:00:00 2001 From: Robert Braun Date: Tue, 17 Mar 2026 08:39:58 +0100 Subject: [PATCH 07/20] SSP widget --- HopsanGUI/ModelHandler.cpp | 3 - HopsanGUI/Resources.qrc | 1 + HopsanGUI/Widgets/ModelWidget.cpp | 51 +++++ HopsanGUI/Widgets/ModelWidget.h | 6 + HopsanGUI/Widgets/SSPWidget.cpp | 204 ++++++++++++++++-- HopsanGUI/Widgets/SSPWidget.h | 34 ++- HopsanGUI/graphics/uiicons/svg/Hopsan-FMI.svg | 124 +++++++++++ 7 files changed, 403 insertions(+), 20 deletions(-) create mode 100644 HopsanGUI/graphics/uiicons/svg/Hopsan-FMI.svg diff --git a/HopsanGUI/ModelHandler.cpp b/HopsanGUI/ModelHandler.cpp index 9b48559ada..fa3adcaa70 100644 --- a/HopsanGUI/ModelHandler.cpp +++ b/HopsanGUI/ModelHandler.cpp @@ -61,9 +61,6 @@ #include "Widgets/TextEditorWidget.h" #include "Utilities/GUIUtilities.h" -#include "ssp4c.h" -#include "ssp4c_ssd.h" - #ifdef USEZMQ #include "RemoteSimulationUtils.h" #endif diff --git a/HopsanGUI/Resources.qrc b/HopsanGUI/Resources.qrc index da998f07ba..45c05e6679 100644 --- a/HopsanGUI/Resources.qrc +++ b/HopsanGUI/Resources.qrc @@ -158,6 +158,7 @@ graphics/objecticons/dcpcomponent.svg graphics/uiicons/svg/Hopsan-SSP.svg graphics/uiicons/svg/Hopsan-SSD.svg + graphics/uiicons/svg/Hopsan-FMI.svg ../licenseNoticeGPLv3 diff --git a/HopsanGUI/Widgets/ModelWidget.cpp b/HopsanGUI/Widgets/ModelWidget.cpp index 7e27175c38..e1dde952bb 100644 --- a/HopsanGUI/Widgets/ModelWidget.cpp +++ b/HopsanGUI/Widgets/ModelWidget.cpp @@ -58,6 +58,7 @@ #include "ModelHandler.h" #include "ProjectTabWidget.h" #include "SimulationThreadHandler.h" +#include "ssp4c_ssd_component.h" #include "version_gui.h" #include "Widgets/AnimationWidget.h" #include "MessageHandler.h" @@ -71,6 +72,9 @@ #include "LibraryHandler.h" #include "UndoStack.h" +#include "ssp4c_ssd.h" +#include "ssp4c_ssd_system.h" + //! @class ModelWidget //! @brief The ModelWidget class is a Widget to contain a simulation model //! @@ -162,6 +166,8 @@ ModelWidget::ModelWidget(ModelHandler *pModelHandler, CentralTabWidget *pParentT mpGraphicsView->centerView(); mLastSimulationTime = 0; + + mSsd = nullptr; } @@ -196,6 +202,21 @@ void ModelWidget::setMessageHandler(GUIMessageHandler *pMessageHandler) void ModelWidget::setModelType(ModelWidget::ModelType type) { mModelType = type; + + QString modelTypeString; + if(type == ModelWidget::DcpModel) { + modelTypeString = " DCP"; + } + else if(type == ModelWidget::SsdModel) { + modelTypeString = " SSD"; + } + QLabel *pLabel = new QLabel(modelTypeString, this); + pLabel->setStyleSheet("color: Red;"); + QGridLayout *pLayout = qobject_cast(this->layout()); + pLayout->addWidget(pLabel, 1,0,1,1); + pLayout->setRowStretch(2,1); + pLayout->setColumnStretch(1,1); + pLabel->setVisible(type != ModelWidget::HopsanModel); } ModelWidget::ModelType ModelWidget::getModelType() const @@ -318,6 +339,10 @@ void ModelWidget::setLogDataHandler(QSharedPointer pHandler) pHandler->setParentModel(this); } +void ModelWidget::setSsdHandle(ssdHandle *ssd) +{ + mSsd = ssd; +} //! @brief Sets last simulation time (only use this from project tab widget!) void ModelWidget::setLastSimulationTime(int time) @@ -1329,6 +1354,32 @@ void ModelWidget::openCurrentContainerInNewTab() //! @see loadModel() void ModelWidget::saveModel(SaveTargetEnumT saveAsFlag, SaveContentsEnumT contents) { + if(mModelType == ModelWidget::SsdModel) { + auto rootSystem = ssp4c_ssd_getRootSystem(mSsd); + QMap ssdMap; + for(int c=0; cgetModelObjects()) { + if(ssdMap.contains(c->getName())) { + //Update! + qDebug() << "Updating: " << c->getName(); + } + else { + //Add! + qDebug() << "Adding: " << c->getName(); + } + } + for(const auto &name : ssdMap.keys()) { + //Remove! + if(!mpToplevelSystem->hasModelObject(name)) { + qDebug() << "Removing: " << name; + } + } + return; + } + // Backup old save file before saving (if old file exists) if(saveAsFlag == ExistingFile && gpConfig->getBoolSetting(cfg::autobackup)) { diff --git a/HopsanGUI/Widgets/ModelWidget.h b/HopsanGUI/Widgets/ModelWidget.h index 887983a6dc..c56adeb64d 100644 --- a/HopsanGUI/Widgets/ModelWidget.h +++ b/HopsanGUI/Widgets/ModelWidget.h @@ -56,6 +56,7 @@ class AnimationWidget; class SimulationThreadHandler; class GUIMessageHandler; class LogDataHandler2; +class ssdHandle; #include "CoreAccess.h" #include "RemoteCoreAccess.h" @@ -70,6 +71,7 @@ class ModelWidget : public QWidget enum ModelType { HopsanModel = 0x0, DcpModel = 0x1, + SsdModel = 0x2, }; ModelWidget(ModelHandler *pModelHandler, CentralTabWidget *pParentTabWidget = nullptr); @@ -123,6 +125,8 @@ class ModelWidget : public QWidget GraphicsView *mpGraphicsView; AnimationWidget *mpAnimationWidget; + void setSsdHandle(ssdHandle* ssd); + public slots: void setTopLevelSimulationTime(const QString startTime, const QString timeStep, const QString stopTime, UndoStatusEnumT undoSettings=Undo); bool simulate_nonblocking(); @@ -195,6 +199,8 @@ private slots: double mSimulationProgress; QVector mRemoteResultVariables; QMutex mSimulateMutex; + + ssdHandle *mSsd; }; diff --git a/HopsanGUI/Widgets/SSPWidget.cpp b/HopsanGUI/Widgets/SSPWidget.cpp index ff6c8fd2dc..3438784fbb 100644 --- a/HopsanGUI/Widgets/SSPWidget.cpp +++ b/HopsanGUI/Widgets/SSPWidget.cpp @@ -42,20 +42,21 @@ #include "global.h" #include "ssp4c.h" #include "ssp4c_ssd.h" +#include "ssp4c_ssd_system.h" #include "ssp4c_ssd_component.h" #include "ssp4c_ssd_connection.h" #include "ssp4c_ssd_connector.h" +#include "ssp4c_ssv_parameter_set.h" +#include "ssp4c_ssv_parameter.h" +#include "ssp4c_ssm_parameter_mapping.h" +#include "ssp4c_ssm_mapping_entry.h" SSPWidget::SSPWidget(QWidget *pParent) { this->setObjectName("SSPWidget"); this->setWindowTitle("SSP Explorer"); - mpTree = new QTreeWidget(this); - mpTree->setMouseTracking(true); - mpTree->setHeaderHidden(true); - mpTree->setColumnCount(1); - + mpTree = new SSPTreeWidget(this); QVBoxLayout *pLayout = new QVBoxLayout(this);; pLayout->addWidget(mpTree); @@ -92,28 +93,201 @@ void SSPWidget::addSSP(QFileInfo path) qDebug() << "SSD generationTool :" << ssp4c_ssd_getGenerationTool(ssd); qDebug() << "SSD generationDateAndTime: " << ssp4c_ssd_getGenerationDateAndTime(ssd); - QTreeWidgetItem *pSsdItem = new QTreeWidgetItem(); + QTreeWidgetItem *pSsdItem = new QTreeWidgetItem(SSPTreeWidget::SSDItem); //pSsdItem->setIcon(0, QIcon(QString(ICONPATH) + "svg/Hopsan-SSD.svg")); - pSsdItem->setText(0,ssp4c_ssd_getName(ssd)); + QString itemName = ssp4c_ssd_getFileName(ssd); + QString name = ssp4c_ssd_getName(ssd); + if(!name.isEmpty()) { + itemName.append(" (\""); + itemName.append(name); + itemName.append("\")"); + } + pSsdItem->setText(0,itemName); pSspItem->addChild(pSsdItem); itemToSspMap.insert(pSsdItem, ssp); itemToSsdMap.insert(pSsdItem, ssd); + + auto rootSystem = ssp4c_ssd_getRootSystem(ssd); + if(rootSystem != nullptr) { + QTreeWidgetItem *pSystemItem = new QTreeWidgetItem(); + pSystemItem->setText(0, QString(ssp4c_ssd_system_getName(rootSystem))); + pSsdItem->addChild(pSystemItem); + itemToSspMap.insert(pSystemItem, ssp); + itemToSsdMap.insert(pSystemItem, ssd); + itemToSystemMap.insert(pSystemItem, rootSystem); + int componentCount = ssp4c_ssd_system_getNumberOfComponents(rootSystem); + for(int i=0; isetText(0, QString(ssp4c_ssd_component_getName(comp))); + pCompItem->setIcon(0, QIcon(QString(ICONPATH) + "svg/Hopsan-FMI.svg")); + pSystemItem->addChild(pCompItem); + } + } + + + } + + QTreeWidgetItem *pResourcesItem = new QTreeWidgetItem(); + pResourcesItem->setText(0, "Resources"); + pSspItem->addChild(pResourcesItem); + + int ssvCount = ssp4c_getNumberOfSsvs(ssp); + qDebug() << "Found" << ssvCount << "SSVs"; + for(int i=0; isetIcon(0, QIcon(QString(ICONPATH) + "svg/Hopsan-SSD.svg")); + QString itemName = ssp4c_ssv_parameterSet_getFileName(ssv); + QString name = ssp4c_ssv_parameterSet_getName(ssv); + if(!name.isEmpty()) { + itemName.append(" (\""); + itemName.append(name); + itemName.append("\")"); + } + pSsvItem->setText(0,itemName); + pSsvItem->setTextColor(0,QColor("darkred")); + pResourcesItem->addChild(pSsvItem); + + itemToSspMap.insert(pSsvItem, ssp); + itemToSsvMap.insert(pSsvItem, ssv); + } + + int ssmCount = ssp4c_getNumberOfSsms(ssp); + qDebug() << "Found" << ssmCount << "SSMs"; + for(int i=0; isetIcon(0, QIcon(QString(ICONPATH) + "svg/Hopsan-SSD.svg")); + pSsmItem->setText(0,QString(ssp4c_ssm_parameterMapping_getFilename(ssm))); + pSsmItem->setTextColor(0,QColor("darkgreen")); + pResourcesItem->addChild(pSsmItem); + + itemToSspMap.insert(pSsmItem, ssp); + itemToSsmMap.insert(pSsmItem, ssm); } } +SSPTreeWidget::SSPTreeWidget(QWidget *parent) : QTreeWidget(parent) +{ + setMouseTracking(true); + setHeaderHidden(true); + setColumnCount(1); + + setDragEnabled(true); + setAcceptDrops(true); + setDropIndicatorShown(true); + + setDragDropMode(QAbstractItemView::DragDrop); + setDefaultDropAction(Qt::CopyAction); +} + +void SSPTreeWidget::startDrag(Qt::DropActions supportedActions) +{ + QTreeWidgetItem *item = currentItem(); + if (!item) + return; + + // Tillåt bara drag av SourceItem + if (item->type() != SSVItem) + return; + + QTreeWidget::startDrag(Qt::CopyAction); +} + +void SSPTreeWidget::dragMoveEvent(QDragMoveEvent *event) +{ + QTreeWidgetItem *target = itemAt(event->pos()); + + if (!target || target->type() != FMUItem) + { + event->ignore(); + return; + } + + event->setDropAction(Qt::CopyAction); + event->accept(); +} + +void SSPTreeWidget::dropEvent(QDropEvent *event) +{ + QTreeWidgetItem *target = itemAt(event->pos()); + + if (!target || target->type() != FMUItem) + { + event->ignore(); + return; + } + + QTreeWidgetItem *source = currentItem(); + + if (!source || source->type() != SSVItem) + { + event->ignore(); + return; + } + + // Create a copy of item and add it to the target + QTreeWidgetItem *copy = new QTreeWidgetItem(SSVItem); + copy->setText(0, source->text(0)); + //! @todo We must also copy the mapping betweein SSV item and SSV file + + target->addChild(copy); + target->setExpanded(true); + + event->acceptProposedAction(); +} + void SSPWidget::openSSDModel(QTreeWidgetItem *item, int) { - sspHandle *ssp = itemToSspMap[item]; - ssdHandle* ssd = itemToSsdMap[item]; + sspHandle *ssp; + ssdHandle *ssd; + ssdSystemHandle *system; + if(itemToSsdMap.contains(item)) { + ssp = itemToSspMap[item]; + ssd = itemToSsdMap[item]; + } + else { + return; + } + + system = ssp4c_ssd_getRootSystem(ssd); //! @todo Check so that SSD is not already open somehow - gpModelHandler->addNewModel(ssp4c_ssd_getName(ssd)); + auto pModel = gpModelHandler->addNewModel(ssp4c_ssd_getName(ssd)); + pModel->setModelType(ModelWidget::SsdModel); + pModel->setSsdHandle(ssd); - int componentCount = ssp4c_ssd_getNumberOfComponents(ssd); + int componentCount = ssp4c_ssd_system_getNumberOfComponents(system); for(int i=0; igetPort(startPortName); Port *pEndPort = pEndComponent->getPort(endPortName); - if(pStartPort != nullptr && pEndComponent != nullptr) { + if(pStartPort != nullptr && pEndPort != nullptr) { qDebug() << "Found ports!"; SystemObject *sysObj = gpModelHandler->getCurrentViewContainerObject(); Connector *pCon = sysObj->createConnector(pStartPort, pEndPort, NoUndo); diff --git a/HopsanGUI/Widgets/SSPWidget.h b/HopsanGUI/Widgets/SSPWidget.h index bf9ad32880..3e7d57733e 100644 --- a/HopsanGUI/Widgets/SSPWidget.h +++ b/HopsanGUI/Widgets/SSPWidget.h @@ -10,6 +10,10 @@ //Forward declarations class sspHandle; class ssdHandle; +class ssvParameterSetHandle; +class ssmParameterMappingHandle; +class ssdSystemHandle; +class SSPTreeWidget; class SSPWidget : public QWidget { @@ -18,16 +22,42 @@ class SSPWidget : public QWidget SSPWidget(QWidget *pParent=0); void addSSP(QFileInfo path); -public slots: + + public slots: protected slots: void openSSDModel(QTreeWidgetItem*item, int); private: - QTreeWidget *mpTree; + SSPTreeWidget *mpTree; QMap itemToSspMap; QMap itemToSsdMap; + QMap itemToSystemMap; + QMap itemToSsvMap; + QMap itemToSsmMap; }; +class SSPTreeWidget : public QTreeWidget +{ + Q_OBJECT + +public: + explicit SSPTreeWidget(QWidget *parent = nullptr); + + enum ItemType { + SSDItem = QTreeWidgetItem::UserType + 1, + FMUItem, + SSVItem, + SSMItem + }; + +protected: + void startDrag(Qt::DropActions supportedActions) override; + void dragMoveEvent(QDragMoveEvent *event) override; + void dropEvent(QDropEvent *event) override; +}; + + + #endif // SSPWIDGET_H diff --git a/HopsanGUI/graphics/uiicons/svg/Hopsan-FMI.svg b/HopsanGUI/graphics/uiicons/svg/Hopsan-FMI.svg new file mode 100644 index 0000000000..6688a5eae8 --- /dev/null +++ b/HopsanGUI/graphics/uiicons/svg/Hopsan-FMI.svg @@ -0,0 +1,124 @@ + +image/svg+xml \ No newline at end of file From be049522931fe0cbc3f2561637a6e47dfcf3babe Mon Sep 17 00:00:00 2001 From: Robert Braun Date: Wed, 18 Mar 2026 12:51:51 +0100 Subject: [PATCH 08/20] Right-click and drag SSVs and SSMs --- HopsanCore/include/HopsanCoreGitVersion.h | 4 +- HopsanGUI/Resources.qrc | 2 + HopsanGUI/Widgets/SSPWidget.cpp | 72 +++++++++++++----- HopsanGUI/Widgets/SSPWidget.h | 3 + HopsanGUI/graphics/uiicons/svg/Hopsan-SSD.svg | 43 +++++------ HopsanGUI/graphics/uiicons/svg/Hopsan-SSM.svg | 73 +++++++++++++++++++ HopsanGUI/graphics/uiicons/svg/Hopsan-SSV.svg | 69 ++++++++++++++++++ 7 files changed, 221 insertions(+), 45 deletions(-) create mode 100644 HopsanGUI/graphics/uiicons/svg/Hopsan-SSM.svg create mode 100644 HopsanGUI/graphics/uiicons/svg/Hopsan-SSV.svg diff --git a/HopsanCore/include/HopsanCoreGitVersion.h b/HopsanCore/include/HopsanCoreGitVersion.h index 71ba092f53..62e4b43236 100644 --- a/HopsanCore/include/HopsanCoreGitVersion.h +++ b/HopsanCore/include/HopsanCoreGitVersion.h @@ -2,6 +2,6 @@ // $Id$ #ifndef HOPSANCOREGITVERSION_H_INCLUDED #define HOPSANCOREGITVERSION_H_INCLUDED -#define HOPSANCORE_COMMIT_HASH 58f0c8472 -#define HOPSANCORE_COMMIT_TIMESTAMP 20250331.1258 +#define HOPSANCORE_COMMIT_HASH f6800a955 +#define HOPSANCORE_COMMIT_TIMESTAMP 20260217.1325 #endif diff --git a/HopsanGUI/Resources.qrc b/HopsanGUI/Resources.qrc index 45c05e6679..855f04ddfb 100644 --- a/HopsanGUI/Resources.qrc +++ b/HopsanGUI/Resources.qrc @@ -158,6 +158,8 @@ graphics/objecticons/dcpcomponent.svg graphics/uiicons/svg/Hopsan-SSP.svg graphics/uiicons/svg/Hopsan-SSD.svg + graphics/uiicons/svg/Hopsan-SSV.svg + graphics/uiicons/svg/Hopsan-SSM.svg graphics/uiicons/svg/Hopsan-FMI.svg diff --git a/HopsanGUI/Widgets/SSPWidget.cpp b/HopsanGUI/Widgets/SSPWidget.cpp index 3438784fbb..95f9b6df55 100644 --- a/HopsanGUI/Widgets/SSPWidget.cpp +++ b/HopsanGUI/Widgets/SSPWidget.cpp @@ -38,6 +38,9 @@ #include #include #include +#include +#include +#include #include "global.h" #include "ssp4c.h" @@ -69,7 +72,7 @@ void SSPWidget::addSSP(QFileInfo path) QFont boldFont = qApp->font(); boldFont.setBold(true); - QTreeWidgetItem *pSspItem = new QTreeWidgetItem(); + QTreeWidgetItem *pSspItem = new QTreeWidgetItem(SSPTreeWidget::SSPItem); pSspItem->setFont(0,boldFont); pSspItem->setIcon(0, QIcon(QString(ICONPATH) + "svg/Hopsan-SSP.svg")); pSspItem->setText(0,path.fileName()); @@ -94,7 +97,7 @@ void SSPWidget::addSSP(QFileInfo path) qDebug() << "SSD generationDateAndTime: " << ssp4c_ssd_getGenerationDateAndTime(ssd); QTreeWidgetItem *pSsdItem = new QTreeWidgetItem(SSPTreeWidget::SSDItem); - //pSsdItem->setIcon(0, QIcon(QString(ICONPATH) + "svg/Hopsan-SSD.svg")); + pSsdItem->setIcon(0, QIcon(QString(ICONPATH) + "svg/Hopsan-SSD.svg")); QString itemName = ssp4c_ssd_getFileName(ssd); QString name = ssp4c_ssd_getName(ssd); if(!name.isEmpty()) { @@ -110,7 +113,7 @@ void SSPWidget::addSSP(QFileInfo path) auto rootSystem = ssp4c_ssd_getRootSystem(ssd); if(rootSystem != nullptr) { - QTreeWidgetItem *pSystemItem = new QTreeWidgetItem(); + QTreeWidgetItem *pSystemItem = new QTreeWidgetItem(SSPTreeWidget::SystemItem); pSystemItem->setText(0, QString(ssp4c_ssd_system_getName(rootSystem))); pSsdItem->addChild(pSystemItem); itemToSspMap.insert(pSystemItem, ssp); @@ -142,19 +145,19 @@ void SSPWidget::addSSP(QFileInfo path) qDebug() << "Found" << parCount << "SSV parameters"; for(int p=0; psetIcon(0, QIcon(QString(ICONPATH) + "svg/Hopsan-SSD.svg")); + pSsvItem->setIcon(0, QIcon(QString(ICONPATH) + "svg/Hopsan-SSV.svg")); QString itemName = ssp4c_ssv_parameterSet_getFileName(ssv); QString name = ssp4c_ssv_parameterSet_getName(ssv); if(!name.isEmpty()) { @@ -163,7 +166,6 @@ void SSPWidget::addSSP(QFileInfo path) itemName.append("\")"); } pSsvItem->setText(0,itemName); - pSsvItem->setTextColor(0,QColor("darkred")); pResourcesItem->addChild(pSsvItem); itemToSspMap.insert(pSsvItem, ssp); @@ -179,14 +181,13 @@ void SSPWidget::addSSP(QFileInfo path) for(int e=0; esetIcon(0, QIcon(QString(ICONPATH) + "svg/Hopsan-SSD.svg")); + pSsmItem->setIcon(0, QIcon(QString(ICONPATH) + "svg/Hopsan-SSM.svg")); pSsmItem->setText(0,QString(ssp4c_ssm_parameterMapping_getFilename(ssm))); - pSsmItem->setTextColor(0,QColor("darkgreen")); pResourcesItem->addChild(pSsmItem); itemToSspMap.insert(pSsmItem, ssp); @@ -214,8 +215,8 @@ void SSPTreeWidget::startDrag(Qt::DropActions supportedActions) if (!item) return; - // Tillåt bara drag av SourceItem - if (item->type() != SSVItem) + // Allow drag of SSV and SSM items + if (item->type() != SSVItem && item->type() != SSMItem) return; QTreeWidget::startDrag(Qt::CopyAction); @@ -225,12 +226,20 @@ void SSPTreeWidget::dragMoveEvent(QDragMoveEvent *event) { QTreeWidgetItem *target = itemAt(event->pos()); - if (!target || target->type() != FMUItem) + static QTreeWidgetItem *lastItem = nullptr; + if (lastItem) { + lastItem->setBackground(0, Qt::NoBrush); + } + + if (!target || (target->type() != FMUItem && target->type() != SystemItem)) { + lastItem = nullptr; event->ignore(); return; } + target->setBackground(0, QBrush(Qt::lightGray)); + lastItem = target; event->setDropAction(Qt::CopyAction); event->accept(); } @@ -239,7 +248,7 @@ void SSPTreeWidget::dropEvent(QDropEvent *event) { QTreeWidgetItem *target = itemAt(event->pos()); - if (!target || target->type() != FMUItem) + if (!target || (target->type() != FMUItem && target->type() != SystemItem)) { event->ignore(); return; @@ -247,16 +256,17 @@ void SSPTreeWidget::dropEvent(QDropEvent *event) QTreeWidgetItem *source = currentItem(); - if (!source || source->type() != SSVItem) + if (!source || (source->type() != SSVItem && source->type() != SSMItem)) { event->ignore(); return; } // Create a copy of item and add it to the target - QTreeWidgetItem *copy = new QTreeWidgetItem(SSVItem); + QTreeWidgetItem *copy = new QTreeWidgetItem(source->type()); copy->setText(0, source->text(0)); - //! @todo We must also copy the mapping betweein SSV item and SSV file + copy->setTextColor(0, source->textColor(0)); // Preserve text color + //! @todo We must also copy the mapping between SSV/SSM item and SSV/SSM file target->addChild(copy); target->setExpanded(true); @@ -264,6 +274,28 @@ void SSPTreeWidget::dropEvent(QDropEvent *event) event->acceptProposedAction(); } +void SSPTreeWidget::contextMenuEvent(QContextMenuEvent *event) +{ + QTreeWidgetItem *item = itemAt(event->pos()); + if (!item) { + return; + } + + QMenu menu(this); + + if((item->type() == SSVItem || item->type() == SSMItem) && item->parent()->type() != SSPItem) { + menu.addAction("Remove from SSD", [this, item]() { + QTreeWidgetItem *parent = item->parent(); + if(parent) { + parent->removeChild(item); + // @todo Also remove the mapping between SSV/SSM item and SSV/SSM file + } + }); + } + + menu.exec(event->globalPos()); +} + void SSPWidget::openSSDModel(QTreeWidgetItem *item, int) { sspHandle *ssp; diff --git a/HopsanGUI/Widgets/SSPWidget.h b/HopsanGUI/Widgets/SSPWidget.h index 3e7d57733e..bb072a9508 100644 --- a/HopsanGUI/Widgets/SSPWidget.h +++ b/HopsanGUI/Widgets/SSPWidget.h @@ -46,7 +46,9 @@ class SSPTreeWidget : public QTreeWidget explicit SSPTreeWidget(QWidget *parent = nullptr); enum ItemType { + SSPItem, SSDItem = QTreeWidgetItem::UserType + 1, + SystemItem, FMUItem, SSVItem, SSMItem @@ -56,6 +58,7 @@ class SSPTreeWidget : public QTreeWidget void startDrag(Qt::DropActions supportedActions) override; void dragMoveEvent(QDragMoveEvent *event) override; void dropEvent(QDropEvent *event) override; + void contextMenuEvent(QContextMenuEvent *event) override; }; diff --git a/HopsanGUI/graphics/uiicons/svg/Hopsan-SSD.svg b/HopsanGUI/graphics/uiicons/svg/Hopsan-SSD.svg index b8cad9fb53..9edc1c5d3c 100644 --- a/HopsanGUI/graphics/uiicons/svg/Hopsan-SSD.svg +++ b/HopsanGUI/graphics/uiicons/svg/Hopsan-SSD.svg @@ -29,7 +29,7 @@ guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" - inkscape:window-width="2560" + inkscape:window-width="2200" inkscape:window-height="1369" id="namedview184" showgrid="false" @@ -37,9 +37,9 @@ fit-margin-right="2" fit-margin-left="2" fit-margin-bottom="2" - inkscape:zoom="0.86069101" - inkscape:cx="772.635" - inkscape:cy="-358.43293" + inkscape:zoom="1.721382" + inkscape:cx="105.729" + inkscape:cy="-76.973036" inkscape:window-x="-8" inkscape:window-y="-8" inkscape:window-maximized="1" @@ -50,22 +50,19 @@ id="g190" inkscape:groupmode="layer" inkscape:label="ink_ext_XXXXXX" - transform="matrix(1.3333333,0,0,-1.3333333,0,135.184)"> + transform="matrix(1.3333333,0,0,-1.3333333,0,135.184)"> diff --git a/HopsanGUI/graphics/uiicons/svg/Hopsan-SSM.svg b/HopsanGUI/graphics/uiicons/svg/Hopsan-SSM.svg new file mode 100644 index 0000000000..c49482b0fb --- /dev/null +++ b/HopsanGUI/graphics/uiicons/svg/Hopsan-SSM.svg @@ -0,0 +1,73 @@ + + + +image/svg+xml diff --git a/HopsanGUI/graphics/uiicons/svg/Hopsan-SSV.svg b/HopsanGUI/graphics/uiicons/svg/Hopsan-SSV.svg new file mode 100644 index 0000000000..c93c9e4cfa --- /dev/null +++ b/HopsanGUI/graphics/uiicons/svg/Hopsan-SSV.svg @@ -0,0 +1,69 @@ + + + +image/svg+xml From 5d82c555612f3e09f662f5a636f2018cee9226f0 Mon Sep 17 00:00:00 2001 From: Robert Braun Date: Wed, 18 Mar 2026 13:09:26 +0100 Subject: [PATCH 09/20] Editor widget for SSVs --- HopsanGUI/HopsanGUI.pro | 2 + HopsanGUI/Widgets/SSPWidget.cpp | 162 ++++++++++++++++++-------------- HopsanGUI/Widgets/SSPWidget.h | 1 + 3 files changed, 97 insertions(+), 68 deletions(-) diff --git a/HopsanGUI/HopsanGUI.pro b/HopsanGUI/HopsanGUI.pro index 3fa9241781..5cf555e229 100644 --- a/HopsanGUI/HopsanGUI.pro +++ b/HopsanGUI/HopsanGUI.pro @@ -225,6 +225,7 @@ SOURCES += main.cpp \ GUIPortAppearance.cpp \ GUIConnectorAppearance.cpp \ Widgets/SSPWidget.cpp \ + Widgets/SSVEditorWidget.cpp \ Widgets/SystemParametersWidget.cpp \ PlotWindow.cpp \ GUIObjects/GUIWidgets.cpp \ @@ -304,6 +305,7 @@ HEADERS += MainWindow.h \ Widgets/MessageWidget.h \ InitializationThread.h \ Widgets/SSPWidget.h \ + Widgets/SSVEditorWidget.h \ version_gui.h \ Dialogs/OptionsDialog.h \ UndoStack.h \ diff --git a/HopsanGUI/Widgets/SSPWidget.cpp b/HopsanGUI/Widgets/SSPWidget.cpp index 95f9b6df55..690bd8758e 100644 --- a/HopsanGUI/Widgets/SSPWidget.cpp +++ b/HopsanGUI/Widgets/SSPWidget.cpp @@ -30,6 +30,8 @@ //$Id$ #include "SSPWidget.h" +#include "SSVEditorWidget.h" +#include "Widgets/ProjectTabWidget.h" #include "GUIObjects/GUIContainerObject.h" #include "GUIObjects/GUIModelObject.h" #include "GUIPort.h" @@ -298,87 +300,111 @@ void SSPTreeWidget::contextMenuEvent(QContextMenuEvent *event) void SSPWidget::openSSDModel(QTreeWidgetItem *item, int) { - sspHandle *ssp; - ssdHandle *ssd; - ssdSystemHandle *system; - if(itemToSsdMap.contains(item)) { + if (!item) return; + + // Check if this is an SSD item + if (item->type() == SSPTreeWidget::SSDItem) { + sspHandle *ssp; + ssdHandle *ssd; + ssdSystemHandle *system; + if(!itemToSsdMap.contains(item)) { + return; + } + ssp = itemToSspMap[item]; ssd = itemToSsdMap[item]; - } - else { - return; - } - - system = ssp4c_ssd_getRootSystem(ssd); + system = ssp4c_ssd_getRootSystem(ssd); - //! @todo Check so that SSD is not already open somehow + //! @todo Check so that SSD is not already open somehow - auto pModel = gpModelHandler->addNewModel(ssp4c_ssd_getName(ssd)); - pModel->setModelType(ModelWidget::SsdModel); - pModel->setSsdHandle(ssd); + auto pModel = gpModelHandler->addNewModel(ssp4c_ssd_getName(ssd)); + pModel->setModelType(ModelWidget::SsdModel); + pModel->setSsdHandle(ssd); - int componentCount = ssp4c_ssd_system_getNumberOfComponents(system); - for(int i=0; igetCurrentTopLevelSystem(); - if(pSystem) { - QPointF pos = pSystem->getGraphicsViewport().mCenter; - ModelObject *pFmuComponent = pSystem->addModelObject("FMIWrapper", pos); - if(pFmuComponent) { - QString unzippedLocation = ssp4c_getUnzippedLocation(ssp); - QString source = ssp4c_ssd_component_getSource(comp); - pFmuComponent->setParameterValue("path", unzippedLocation+"/"+source); - pSystem->renameModelObject(pFmuComponent->getName(), ssp4c_ssd_component_getName(comp)); + int connectorsCount = ssp4c_ssd_component_getNumberOfConnectors(comp); + for(int i=0; igetCurrentTopLevelSystem(); + if(pSystem) { + QPointF pos = pSystem->getGraphicsViewport().mCenter; + ModelObject *pFmuComponent = pSystem->addModelObject("FMIWrapper", pos); + if(pFmuComponent) { + QString unzippedLocation = ssp4c_getUnzippedLocation(ssp); + QString source = ssp4c_ssd_component_getSource(comp); + pFmuComponent->setParameterValue("path", unzippedLocation+"/"+source); + pSystem->renameModelObject(pFmuComponent->getName(), ssp4c_ssd_component_getName(comp)); + } } } - } - int connetionCount = ssp4c_ssd_system_getNumberOfConnections(system); - for(int i=0; igetCurrentTopLevelSystem(); - if(pSystem) { - ModelObject *pStartComponent = pSystem->getModelObject(ssp4c_ssd_connection_getStartElement(con)); - ModelObject *pEndComponent = pSystem->getModelObject(ssp4c_ssd_connection_getEndElement(con)); - if(pStartComponent != nullptr && pEndComponent != nullptr) { - qDebug() << "Found components!"; - QString startPortName = ssp4c_ssd_connection_getStartConnector(con); - startPortName.replace(".", "_"); - QString endPortName = ssp4c_ssd_connection_getEndConnector(con); - endPortName.replace(".", "_"); - Port *pStartPort = pStartComponent->getPort(startPortName); - Port *pEndPort = pEndComponent->getPort(endPortName); - if(pStartPort != nullptr && pEndPort != nullptr) { - qDebug() << "Found ports!"; - SystemObject *sysObj = gpModelHandler->getCurrentViewContainerObject(); - Connector *pCon = sysObj->createConnector(pStartPort, pEndPort, NoUndo); - if(pCon != nullptr) { - qDebug() << "Creatd connetor!"; - QVector pointVector; - QStringList geometryList; - pointVector.push_back(pCon->getStartPort()->boundingRect().center()); - pointVector.push_back(pCon->getEndPort()->boundingRect().center()); - geometryList.clear(); - geometryList.append(hmf::connector::diagonal); - pCon->setPointsAndGeometries(pointVector, geometryList); + int connetionCount = ssp4c_ssd_system_getNumberOfConnections(system); + for(int i=0; igetCurrentTopLevelSystem(); + if(pSystem) { + ModelObject *pStartComponent = pSystem->getModelObject(ssp4c_ssd_connection_getStartElement(con)); + ModelObject *pEndComponent = pSystem->getModelObject(ssp4c_ssd_connection_getEndElement(con)); + if(pStartComponent != nullptr && pEndComponent != nullptr) { + qDebug() << "Found components!"; + QString startPortName = ssp4c_ssd_connection_getStartConnector(con); + startPortName.replace(".", "_"); + QString endPortName = ssp4c_ssd_connection_getEndConnector(con); + endPortName.replace(".", "_"); + Port *pStartPort = pStartComponent->getPort(startPortName); + Port *pEndPort = pEndComponent->getPort(endPortName); + if(pStartPort != nullptr && pEndPort != nullptr) { + qDebug() << "Found ports!"; + SystemObject *sysObj = gpModelHandler->getCurrentViewContainerObject(); + Connector *pCon = sysObj->createConnector(pStartPort, pEndPort, NoUndo); + if(pCon != nullptr) { + qDebug() << "Creatd connetor!"; + QVector pointVector; + QStringList geometryList; + pointVector.push_back(pCon->getStartPort()->boundingRect().center()); + pointVector.push_back(pCon->getEndPort()->boundingRect().center()); + geometryList.clear(); + geometryList.append(hmf::connector::diagonal); + pCon->setPointsAndGeometries(pointVector, geometryList); + } } } } } } - + // Check if this is an SSV item + else if (item->type() == SSPTreeWidget::SSVItem) { + openSSVEditor(item, 0); + } } +void SSPWidget::openSSVEditor(QTreeWidgetItem *item, int) +{ + if (!item || !itemToSsvMap.contains(item)) { + return; + } + + ssvParameterSetHandle *ssv = itemToSsvMap[item]; + QString fileName = QString::fromUtf8(ssp4c_ssv_parameterSet_getFileName(ssv)); + + // Create the SSV editor widget + SSVEditorWidget *pEditor = new SSVEditorWidget(ssv, fileName, gpCentralTabWidget); + + // Add to the central tab widget + if (gpCentralTabWidget) { + gpCentralTabWidget->addTab(pEditor, fileName); + gpCentralTabWidget->setCurrentWidget(pEditor); + } +} diff --git a/HopsanGUI/Widgets/SSPWidget.h b/HopsanGUI/Widgets/SSPWidget.h index bb072a9508..f3ded91039 100644 --- a/HopsanGUI/Widgets/SSPWidget.h +++ b/HopsanGUI/Widgets/SSPWidget.h @@ -27,6 +27,7 @@ class SSPWidget : public QWidget protected slots: void openSSDModel(QTreeWidgetItem*item, int); + void openSSVEditor(QTreeWidgetItem*item, int); private: SSPTreeWidget *mpTree; From d1ba21898df480f44657fbeccd5092b77c1d3c26 Mon Sep 17 00:00:00 2001 From: Robert Braun Date: Wed, 18 Mar 2026 13:52:53 +0100 Subject: [PATCH 10/20] SSM Editor --- HopsanGUI/HopsanGUI.pro | 2 ++ HopsanGUI/Widgets/SSPWidget.cpp | 26 +++++++++++++++++++++++++- HopsanGUI/Widgets/SSPWidget.h | 1 + 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/HopsanGUI/HopsanGUI.pro b/HopsanGUI/HopsanGUI.pro index 5cf555e229..76b20dda01 100644 --- a/HopsanGUI/HopsanGUI.pro +++ b/HopsanGUI/HopsanGUI.pro @@ -226,6 +226,7 @@ SOURCES += main.cpp \ GUIConnectorAppearance.cpp \ Widgets/SSPWidget.cpp \ Widgets/SSVEditorWidget.cpp \ + Widgets/SSMEditorWidget.cpp \ Widgets/SystemParametersWidget.cpp \ PlotWindow.cpp \ GUIObjects/GUIWidgets.cpp \ @@ -306,6 +307,7 @@ HEADERS += MainWindow.h \ InitializationThread.h \ Widgets/SSPWidget.h \ Widgets/SSVEditorWidget.h \ + Widgets/SSMEditorWidget.h \ version_gui.h \ Dialogs/OptionsDialog.h \ UndoStack.h \ diff --git a/HopsanGUI/Widgets/SSPWidget.cpp b/HopsanGUI/Widgets/SSPWidget.cpp index 690bd8758e..f43169d06b 100644 --- a/HopsanGUI/Widgets/SSPWidget.cpp +++ b/HopsanGUI/Widgets/SSPWidget.cpp @@ -31,6 +31,7 @@ #include "SSPWidget.h" #include "SSVEditorWidget.h" +#include "SSMEditorWidget.h" #include "Widgets/ProjectTabWidget.h" #include "GUIObjects/GUIContainerObject.h" #include "GUIObjects/GUIModelObject.h" @@ -303,7 +304,7 @@ void SSPWidget::openSSDModel(QTreeWidgetItem *item, int) if (!item) return; // Check if this is an SSD item - if (item->type() == SSPTreeWidget::SSDItem) { + if (item->type() == SSPTreeWidget::SSDItem || item->type() == SSPTreeWidget::SystemItem) { sspHandle *ssp; ssdHandle *ssd; ssdSystemHandle *system; @@ -388,6 +389,10 @@ void SSPWidget::openSSDModel(QTreeWidgetItem *item, int) else if (item->type() == SSPTreeWidget::SSVItem) { openSSVEditor(item, 0); } + // Check if this is an SSM item + else if (item->type() == SSPTreeWidget::SSMItem) { + openSSMEditor(item, 0); + } } void SSPWidget::openSSVEditor(QTreeWidgetItem *item, int) @@ -408,3 +413,22 @@ void SSPWidget::openSSVEditor(QTreeWidgetItem *item, int) gpCentralTabWidget->setCurrentWidget(pEditor); } } + +void SSPWidget::openSSMEditor(QTreeWidgetItem *item, int) +{ + if (!item || !itemToSsmMap.contains(item)) { + return; + } + + ssmParameterMappingHandle *ssm = itemToSsmMap[item]; + QString fileName = QString::fromUtf8(ssp4c_ssm_parameterMapping_getFilename(ssm)); + + // Create the SSM editor widget + SSMEditorWidget *pEditor = new SSMEditorWidget(ssm, fileName, gpCentralTabWidget); + + // Add to the central tab widget + if (gpCentralTabWidget) { + gpCentralTabWidget->addTab(pEditor, fileName); + gpCentralTabWidget->setCurrentWidget(pEditor); + } +} diff --git a/HopsanGUI/Widgets/SSPWidget.h b/HopsanGUI/Widgets/SSPWidget.h index f3ded91039..a1dd71c435 100644 --- a/HopsanGUI/Widgets/SSPWidget.h +++ b/HopsanGUI/Widgets/SSPWidget.h @@ -28,6 +28,7 @@ class SSPWidget : public QWidget protected slots: void openSSDModel(QTreeWidgetItem*item, int); void openSSVEditor(QTreeWidgetItem*item, int); + void openSSMEditor(QTreeWidgetItem*item, int); private: SSPTreeWidget *mpTree; From 18add18812a5cf411e88fa70f70b03ebb7a7a000 Mon Sep 17 00:00:00 2001 From: Robert Braun Date: Thu, 2 Apr 2026 13:29:21 +0200 Subject: [PATCH 11/20] More SSP work --- HopsanGUI/GUIConnector.cpp | 5 +- HopsanGUI/GUIObjects/GUIComponent.cpp | 267 ++++++++++++++++++++ HopsanGUI/GUIObjects/GUIComponent.h | 36 +++ HopsanGUI/GUIObjects/GUIContainerObject.cpp | 15 +- HopsanGUI/GUIObjects/GUIContainerObject.h | 4 +- HopsanGUI/MainWindow.cpp | 7 +- HopsanGUI/Widgets/SSPWidget.cpp | 103 +++++++- HopsanGUI/Widgets/WelcomeWidget.cpp | 8 +- HopsanGUI/common.h | 1 + 9 files changed, 427 insertions(+), 19 deletions(-) diff --git a/HopsanGUI/GUIConnector.cpp b/HopsanGUI/GUIConnector.cpp index 7d1530b600..433acc7a40 100644 --- a/HopsanGUI/GUIConnector.cpp +++ b/HopsanGUI/GUIConnector.cpp @@ -1258,8 +1258,11 @@ void Connector::setPointsAndGeometries(const QVector &rPoints, const QS mGeometries.push_back(Horizontal); else if(mPoints[i].y() == mPoints[i+1].y()) mGeometries.push_back(Vertical); - else + else { + qDebug() << mPoints[i].x() << " != " << mPoints[i+1].x(); + qDebug() << mPoints[i].y() << " != " << mPoints[i+1].y(); mGeometries.push_back(Diagonal); + } } else { diff --git a/HopsanGUI/GUIObjects/GUIComponent.cpp b/HopsanGUI/GUIObjects/GUIComponent.cpp index c8b695c0da..f345cab0e4 100644 --- a/HopsanGUI/GUIObjects/GUIComponent.cpp +++ b/HopsanGUI/GUIObjects/GUIComponent.cpp @@ -50,6 +50,7 @@ #include "PlotWindow.h" #include "Widgets/ModelWidget.h" #include "LibraryHandler.h" +#include "UndoStack.h" Component::Component(QPointF position, double rotation, ModelObjectAppearance* pAppearanceData, SystemObject *pParentSystem, SelectionStatusEnumT startSelected, GraphicsTypeEnumT gfxType) @@ -461,6 +462,272 @@ QDomElement Component::saveGuiDataToDomElement(QDomElement &rDomElement) } +//========== ResizableComponent ========== + +ResizableComponent::ResizableComponent(QPointF position, double rotation, ModelObjectAppearance* pAppearanceData, SystemObject *pParentSystem, SelectionStatusEnumT startSelected, GraphicsTypeEnumT gfxType) + : Component(position, rotation, pAppearanceData, pParentSystem, startSelected, gfxType) +{ + // Initialize resizing members + mResizeTop = false; + mResizeBottom = false; + mResizeLeft = false; + mResizeRight = false; + mIsResizing = false; + mWidthBeforeResize = 0; + mHeightBeforeResize = 0; + + // Hide the icon and create a rectangle instead + mpIcon->hide(); + + // Create rectangle with default size + mpRect = new QGraphicsRectItem(0, 0, 100, 80, this); + QPen borderPen; + borderPen.setWidth(2); + borderPen.setColor(Qt::black); + mpRect->setPen(borderPen); + mpRect->setBrush(QBrush(Qt::lightGray)); + mpRect->show(); + + // Create text item in the center + mpText = new QGraphicsTextItem(this); + mpText->setPlainText("Resizable"); + mpText->setAcceptHoverEvents(false); + QFont textFont = mpText->font(); + textFont.setPointSize(10); + mpText->setFont(textFont); + mpText->show(); + + // Center text in rectangle + QRectF textRect = mpText->boundingRect(); + mpText->setPos((100 - textRect.width()) / 2, (80 - textRect.height()) / 2); + + // Resize the widget to match rectangle size + this->resize(mpRect->boundingRect().width(), mpRect->boundingRect().height()); + this->refreshSelectionBoxSize(); +} + + +//! @brief Refreshes the appearance of the object +void ResizableComponent::refreshAppearance() +{ + //! @todo should make sure we only do this if we really need to resize (after icon change) + QPointF centerPos = this->getCenterPos(); //Remember center pos for resize + this->setCenterPos(centerPos); //Re-set center pos after resize + + this->refreshDisplayName(); + this->refreshExternalPortsAppearanceAndPosition(); +} + +//! @brief Set the outline color of the rectangle +void ResizableComponent::setRectColor(const QColor &color) +{ + if(mpRect) + { + QPen pen = mpRect->pen(); + pen.setColor(color); + mpRect->setPen(pen); + } +} + + +//! @brief Set the fill color of the rectangle +void ResizableComponent::setRectFillColor(const QColor &color) +{ + if(mpRect) + { + mpRect->setBrush(QBrush(color)); + } +} + + +//! @brief Set the text in the center of the rectangle +void ResizableComponent::setText(const QString &text) +{ + if(mpText) + { + mpText->setPlainText(text); + // Re-center the text + QRectF rectSize = mpRect->rect(); + QRectF textRect = mpText->boundingRect(); + mpText->setPos((rectSize.width() - textRect.width()) / 2, (rectSize.height() - textRect.height()) / 2); + } +} + + +//! @brief Set the component size (width and height) +void ResizableComponent::setComponentSize(double width, double height) +{ + if(width > 0 && height > 0 && mpRect) + { + this->prepareGeometryChange(); + mpRect->setRect(0, 0, width, height); + this->resize(width, height); + refreshSelectionBoxSize(); + + // Refresh port positions for the new component size + for(int i = 0; i < mPortListPtrs.size(); ++i) + { + createRefreshExternalPort(mPortListPtrs[i]->getName()); + } + } +} + + +//! @brief Refresh the selection box to match rectangle size +void ResizableComponent::refreshSelectionBoxSize() +{ + mpSelectionBox->setSize(0.0, 0.0, mpRect->rect().width(), mpRect->rect().height()); +} + + +//! @brief Handle hover movement for edge-based resizing +void ResizableComponent::hoverMoveEvent(QGraphicsSceneHoverEvent *event) +{ + Component::hoverMoveEvent(event); + + this->setCursor(Qt::ArrowCursor); + mResizeLeft = false; + mResizeRight = false; + mResizeTop = false; + mResizeBottom = false; + + int resLim = 5; + + if(event->pos().x() > boundingRect().left() && event->pos().x() < boundingRect().left()+resLim) + { + mResizeLeft = true; + } + if(event->pos().x() > boundingRect().right()-resLim && event->pos().x() < boundingRect().right()) + { + mResizeRight = true; + } + if(event->pos().y() > boundingRect().top() && event->pos().y() < boundingRect().top()+resLim) + { + mResizeTop = true; + } + if(event->pos().y() > boundingRect().bottom()-resLim && event->pos().y() < boundingRect().bottom()) + { + mResizeBottom = true; + } + + if( (mResizeLeft && mResizeTop) || (mResizeRight && mResizeBottom) ) + this->setCursor(Qt::SizeFDiagCursor); + else if( (mResizeTop && mResizeRight) || (mResizeBottom && mResizeLeft) ) + this->setCursor(Qt::SizeBDiagCursor); + else if(mResizeLeft || mResizeRight) + this->setCursor(Qt::SizeHorCursor); + else if(mResizeTop || mResizeBottom) + this->setCursor(Qt::SizeVerCursor); +} + + +//! @brief Handle mouse press for starting resize operation +void ResizableComponent::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + if(mResizeLeft || mResizeRight || mResizeTop || mResizeBottom) + { + mPosBeforeResize = this->pos(); + mWidthBeforeResize = boundingRect().width(); + mHeightBeforeResize = boundingRect().height(); + mIsResizing = true; + } + else + { + mIsResizing = false; + } + Component::mousePressEvent(event); +} + + +//! @brief Handle mouse move for resizing the component +void ResizableComponent::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Component::mouseMoveEvent(event); + + if (getModelLockLevel()==NotLocked && mIsResizing) + { + // Calculate the desired new width and height + double desiredWidth = mWidthBeforeResize; + double desiredHeight = mHeightBeforeResize; + QPointF newPos = mPosBeforeResize; + + // Handle left resize + if(mResizeLeft) + { + double deltaX = this->pos().x() - mPosBeforeResize.x(); + desiredWidth = qMax(5.0, mWidthBeforeResize - deltaX); + newPos.setX(mPosBeforeResize.x() + (mWidthBeforeResize - desiredWidth)); + } + // Handle right resize + else if(mResizeRight) + { + double deltaX = this->pos().x() - mPosBeforeResize.x(); + desiredWidth = qMax(5.0, mWidthBeforeResize + deltaX); + newPos.setX(mPosBeforeResize.x()); + } + + // Handle top resize + if(mResizeTop) + { + double deltaY = this->pos().y() - mPosBeforeResize.y(); + desiredHeight = qMax(5.0, mHeightBeforeResize - deltaY); + newPos.setY(mPosBeforeResize.y() + (mHeightBeforeResize - desiredHeight)); + } + // Handle bottom resize + else if(mResizeBottom) + { + double deltaY = this->pos().y() - mPosBeforeResize.y(); + desiredHeight = qMax(5.0, mHeightBeforeResize + deltaY); + newPos.setY(mPosBeforeResize.y()); + } + + // Update rectangle size based on the desired new width and height + this->prepareGeometryChange(); + mpRect->setRect(0, 0, desiredWidth, desiredHeight); + this->setPos(newPos); + + // Resize the widget to match the rectangle size + this->resize(desiredWidth, desiredHeight); + + // Re-center the text + if(mpText) + { + QRectF textRect = mpText->boundingRect(); + mpText->setPos((desiredWidth - textRect.width()) / 2, (desiredHeight - textRect.height()) / 2); + } + + refreshSelectionBoxSize(); + mpSelectionBox->setActive(); + } +} + + +//! @brief Handle mouse release to register resize in undo stack +void ResizableComponent::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Component::mouseReleaseEvent(event); + + if(mResizeLeft || mResizeRight || mResizeTop || mResizeBottom) + { + // Refresh port positions for the new component size + for(int i = 0; i < mPortListPtrs.size(); ++i) + { + createRefreshExternalPort(mPortListPtrs[i]->getName()); + } + + if(mWidthBeforeResize != boundingRect().width() || mHeightBeforeResize != boundingRect().height()) + { + mpParentSystemObject->getUndoStackPtr()->newPost(); + } + mResizeLeft = false; + mResizeRight = false; + mResizeTop = false; + mResizeBottom = false; + mIsResizing = false; + } +} + + void ScopeComponent::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) { QGraphicsWidget::mouseDoubleClickEvent(event); diff --git a/HopsanGUI/GUIObjects/GUIComponent.h b/HopsanGUI/GUIObjects/GUIComponent.h index 0210ffa3d7..7d4e6b3e22 100644 --- a/HopsanGUI/GUIObjects/GUIComponent.h +++ b/HopsanGUI/GUIObjects/GUIComponent.h @@ -80,6 +80,42 @@ private slots: void createPorts(); }; + +class ResizableComponent : public Component +{ + Q_OBJECT + +public: + ResizableComponent(QPointF position, double rotation, ModelObjectAppearance* pAppearanceData, SystemObject *pParentSystem, SelectionStatusEnumT startSelected = Deselected, GraphicsTypeEnumT gfxType = UserGraphics); + + void setRectColor(const QColor &color); + void setRectFillColor(const QColor &color); + void setText(const QString &text); + void setComponentSize(double width, double height); + void refreshAppearance() override; + +protected: + virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event) override; + virtual void mousePressEvent(QGraphicsSceneMouseEvent *event) override; + virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event) override; + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; + virtual void refreshSelectionBoxSize() override; + +private: + // Resizing members + bool mResizeTop; + bool mResizeBottom; + bool mResizeLeft; + bool mResizeRight; + bool mIsResizing; + QPointF mPosBeforeResize; + double mWidthBeforeResize; + double mHeightBeforeResize; + + QGraphicsRectItem *mpRect; + QGraphicsTextItem *mpText; +}; + class ScopeComponent final : public Component { Q_OBJECT diff --git a/HopsanGUI/GUIObjects/GUIContainerObject.cpp b/HopsanGUI/GUIObjects/GUIContainerObject.cpp index 05a3fe783d..67e3747ea5 100644 --- a/HopsanGUI/GUIObjects/GUIContainerObject.cpp +++ b/HopsanGUI/GUIObjects/GUIContainerObject.cpp @@ -597,7 +597,7 @@ void SystemObject::renameExternalPort(const QString oldName, const QString newNa //! @brief Helper function that allows calling addGUIModelObject with typeName instead of appearance data -ModelObject* SystemObject::addModelObject(QString fullTypeName, QPointF position, double rotation, SelectionStatusEnumT startSelected, NameVisibilityEnumT nameStatus, UndoStatusEnumT undoSettings) +ModelObject* SystemObject::addModelObject(QString fullTypeName, QPointF position, double rotation, SelectionStatusEnumT startSelected, NameVisibilityEnumT nameStatus, UndoStatusEnumT undoSettings, ResizbleEnumT resizable) { ModelObjectAppearance *pAppearanceData = gpLibraryHandler->getModelObjectAppearancePtr(fullTypeName).data(); @@ -632,7 +632,7 @@ ModelObject* SystemObject::addModelObject(QString fullTypeName, QPointF position return pObj; } } - return addModelObject(pAppearanceData, position, rotation, startSelected, nameStatus, undoSettings); + return addModelObject(pAppearanceData, position, rotation, startSelected, nameStatus, undoSettings, resizable); } @@ -642,7 +642,7 @@ ModelObject* SystemObject::addModelObject(QString fullTypeName, QPointF position //! @param name will be the name of the component. //! @returns a pointer to the created and added object //! @todo only modelobjects for now -ModelObject* SystemObject::addModelObject(ModelObjectAppearance *pAppearanceData, QPointF position, double rotation, SelectionStatusEnumT startSelected, NameVisibilityEnumT nameStatus, UndoStatusEnumT undoSettings) +ModelObject* SystemObject::addModelObject(ModelObjectAppearance *pAppearanceData, QPointF position, double rotation, SelectionStatusEnumT startSelected, NameVisibilityEnumT nameStatus, UndoStatusEnumT undoSettings, ResizbleEnumT resizability) { // Deselect all other components and connectors emit deselectAllGUIObjects(); @@ -665,6 +665,15 @@ ModelObject* SystemObject::addModelObject(ModelObjectAppearance *pAppearanceData { pNewModelObject = new ScopeComponent(position, rotation, pAppearanceData, this, startSelected, mGfxType); } + else if (componentTypeName == "FMIWrapper") + { + if(resizability == Resizable) { + pNewModelObject = new ResizableComponent(position, rotation, pAppearanceData, this, startSelected, mGfxType); + } + else { + pNewModelObject = new Component(position, rotation, pAppearanceData, this, startSelected, mGfxType); + } + } else //Assume some standard component type { pNewModelObject = new Component(position, rotation, pAppearanceData, this, startSelected, mGfxType); diff --git a/HopsanGUI/GUIObjects/GUIContainerObject.h b/HopsanGUI/GUIObjects/GUIContainerObject.h index a2a4f53186..cf724985dc 100644 --- a/HopsanGUI/GUIObjects/GUIContainerObject.h +++ b/HopsanGUI/GUIObjects/GUIContainerObject.h @@ -170,8 +170,8 @@ class SystemObject : public ModelObject void takeOwnershipOf(QList &rModeObjectlist, QList &rWidgetList); //GUIModelObject methods - ModelObject *addModelObject(QString fullTypeName, QPointF position, double rotation=0, SelectionStatusEnumT startSelected = Deselected, NameVisibilityEnumT nameStatus = UseDefault, UndoStatusEnumT undoSettings = Undo); - ModelObject *addModelObject(ModelObjectAppearance* pAppearanceData, QPointF position, double rotation=0, SelectionStatusEnumT startSelected = Deselected, NameVisibilityEnumT nameStatus = UseDefault, UndoStatusEnumT undoSettings = Undo); + ModelObject *addModelObject(QString fullTypeName, QPointF position, double rotation=0, SelectionStatusEnumT startSelected = Deselected, NameVisibilityEnumT nameStatus = UseDefault, UndoStatusEnumT undoSettings = Undo, ResizbleEnumT resizable = NotResizable); + ModelObject *addModelObject(ModelObjectAppearance* pAppearanceData, QPointF position, double rotation=0, SelectionStatusEnumT startSelected = Deselected, NameVisibilityEnumT nameStatus = UseDefault, UndoStatusEnumT undoSettings = Undo, ResizbleEnumT resizable = NotResizable); void deleteModelObject(const QString &rObjectName, UndoStatusEnumT undoSettings=Undo); void renameModelObject(QString oldName, QString newName, UndoStatusEnumT undoSettings=Undo); diff --git a/HopsanGUI/MainWindow.cpp b/HopsanGUI/MainWindow.cpp index 9ca326be86..ede7397eeb 100644 --- a/HopsanGUI/MainWindow.cpp +++ b/HopsanGUI/MainWindow.cpp @@ -1341,7 +1341,12 @@ void MainWindow::openRecentModel() qDebug() << "Trying to open " << action->text(); if (action) { - mpModelHandler->loadModel(action->text()); + if(QFileInfo(action->text()).suffix() == "ssp") { + mpModelHandler->loadSsp(action->text()); + } + else { + mpModelHandler->loadModel(action->text()); + } } } diff --git a/HopsanGUI/Widgets/SSPWidget.cpp b/HopsanGUI/Widgets/SSPWidget.cpp index f43169d06b..49b3219d4b 100644 --- a/HopsanGUI/Widgets/SSPWidget.cpp +++ b/HopsanGUI/Widgets/SSPWidget.cpp @@ -35,6 +35,7 @@ #include "Widgets/ProjectTabWidget.h" #include "GUIObjects/GUIContainerObject.h" #include "GUIObjects/GUIModelObject.h" +#include "GUIObjects/GUIComponent.h" #include "GUIPort.h" #include "ModelHandler.h" #include "common.h" @@ -51,7 +52,10 @@ #include "ssp4c_ssd_system.h" #include "ssp4c_ssd_component.h" #include "ssp4c_ssd_connection.h" +#include "ssp4c_ssd_connection_geometry.h" #include "ssp4c_ssd_connector.h" +#include "ssp4c_ssd_connector_geometry.h" +#include "ssp4c_ssd_element_geometry.h" #include "ssp4c_ssv_parameter_set.h" #include "ssp4c_ssv_parameter.h" #include "ssp4c_ssm_parameter_mapping.h" @@ -303,6 +307,8 @@ void SSPWidget::openSSDModel(QTreeWidgetItem *item, int) { if (!item) return; + QMap portToConnectorMap; + // Check if this is an SSD item if (item->type() == SSPTreeWidget::SSDItem || item->type() == SSPTreeWidget::SystemItem) { sspHandle *ssp; @@ -328,20 +334,76 @@ void SSPWidget::openSSDModel(QTreeWidgetItem *item, int) qDebug() << " component name: " << ssp4c_ssd_component_getName(comp); qDebug() << " component source: " << ssp4c_ssd_component_getSource(comp); - int connectorsCount = ssp4c_ssd_component_getNumberOfConnectors(comp); - for(int i=0; igetCurrentTopLevelSystem(); if(pSystem) { QPointF pos = pSystem->getGraphicsViewport().mCenter; - ModelObject *pFmuComponent = pSystem->addModelObject("FMIWrapper", pos); + ModelObject *pFmuComponent = pSystem->addModelObject("FMIWrapper", pos, 0, Deselected, UseDefault, Undo, Resizable); + if(pFmuComponent) { QString unzippedLocation = ssp4c_getUnzippedLocation(ssp); QString source = ssp4c_ssd_component_getSource(comp); pFmuComponent->setParameterValue("path", unzippedLocation+"/"+source); pSystem->renameModelObject(pFmuComponent->getName(), ssp4c_ssd_component_getName(comp)); + ResizableComponent *pRC = qobject_cast(pFmuComponent); + pRC->setText(ssp4c_ssd_component_getName(comp)); + + // Set component size and position from ElementGeometry in SSD + ssdElementGeometryHandle *geom = nullptr; + geom = ssp4c_ssd_component_getElementGeometry(comp); + if(geom) { + double x1 = ssp4c_ssd_elementGeometry_getX1(geom) + 2500; //2500,2500 is center of view + double x2 = ssp4c_ssd_elementGeometry_getX2(geom) + 2500; + double y1 = ssp4c_ssd_elementGeometry_getY1(geom) + 2500; + double y2 = ssp4c_ssd_elementGeometry_getY2(geom) + 2500; + double width = x2 - x1; + double height = y2 - y1; + if(width < 1) { + width = 100; + } + if(height < 1) { + height = 100; + } + double rotation = ssp4c_ssd_elementGeometry_getRotation(geom); + + // Set position and size + pFmuComponent->setPos(x1, y1); + if(width > 0 && height > 0) { + pRC->setComponentSize(width, height); + } + if(rotation != 0) { + pFmuComponent->setRotation(rotation); + } + } + } + + int connectorsCount = ssp4c_ssd_component_getNumberOfConnectors(comp); + QStringList visiblePorts; + for(int i=0; igetPort(ssp4c_ssd_connector_getName(con)); + if(port != nullptr) { + qDebug() << "x = " << x; + qDebug() << "y = " << y; + port->getPortAppearance()->x = x; + port->getPortAppearance()->y = y; + pFmuComponent->createRefreshExternalPort(ssp4c_ssd_connector_getName(con)); + portToConnectorMap.insert(port, con); + } + } + + + qDebug() << " connector: " << ssp4c_ssd_connector_getName(con); + } + for(const auto &port : pFmuComponent->getPortListPtrs()) { + port->setVisible(visiblePorts.contains(port->getName())); } } } @@ -354,6 +416,8 @@ void SSPWidget::openSSDModel(QTreeWidgetItem *item, int) qDebug() << " end element: " << ssp4c_ssd_connection_getEndElement(con); qDebug() << " end connector: " << ssp4c_ssd_connection_getEndConnector(con); + auto conGeom = ssp4c_ssd_connection_getConnectionGeometry(con); + SystemObject *pSystem = gpModelHandler->getCurrentTopLevelSystem(); if(pSystem) { ModelObject *pStartComponent = pSystem->getModelObject(ssp4c_ssd_connection_getStartElement(con)); @@ -371,14 +435,31 @@ void SSPWidget::openSSDModel(QTreeWidgetItem *item, int) SystemObject *sysObj = gpModelHandler->getCurrentViewContainerObject(); Connector *pCon = sysObj->createConnector(pStartPort, pEndPort, NoUndo); if(pCon != nullptr) { - qDebug() << "Creatd connetor!"; + qDebug() << "Created connector!"; QVector pointVector; QStringList geometryList; - pointVector.push_back(pCon->getStartPort()->boundingRect().center()); - pointVector.push_back(pCon->getEndPort()->boundingRect().center()); - geometryList.clear(); - geometryList.append(hmf::connector::diagonal); + + //Start port + auto startPort = pCon->getStartPort(); + pointVector.push_back(startPort->mapToScene(startPort->boundingRect().center())); + + //Middle points + if(conGeom != nullptr) { + int count = 0; + double *pointsX = ssp4c_ssd_connectionGeometry_getPointsX(conGeom, &count); + double *pointsY = ssp4c_ssd_connectionGeometry_getPointsY(conGeom, &count); + for(int i=0; igetEndPort(); + pointVector.push_back(endPort->mapToScene(endPort->boundingRect().center())); + pCon->setPointsAndGeometries(pointVector, geometryList); + pCon->refreshConnectorAppearance(); + } } } diff --git a/HopsanGUI/Widgets/WelcomeWidget.cpp b/HopsanGUI/Widgets/WelcomeWidget.cpp index 1fe1649295..1429e7c6ab 100644 --- a/HopsanGUI/Widgets/WelcomeWidget.cpp +++ b/HopsanGUI/Widgets/WelcomeWidget.cpp @@ -580,7 +580,13 @@ void WelcomeWidget::mousePressEvent(QMouseEvent *event) //! @brief Opens selected recent model from the list and closes the welcome dialog. void WelcomeWidget::openRecentModel() { - gpModelHandler->loadModel(mRecentModelList.at(mpRecentList->currentIndex().row())); + QString path = mRecentModelList.at(mpRecentList->currentIndex().row()); + if(QFileInfo(path).suffix() == "ssp") { + gpModelHandler->loadSsp(path); + } + else { + gpModelHandler->loadModel(path); + } } diff --git a/HopsanGUI/common.h b/HopsanGUI/common.h index 52c2081747..0bcedb7637 100644 --- a/HopsanGUI/common.h +++ b/HopsanGUI/common.h @@ -83,6 +83,7 @@ enum GraphicsTypeEnumT {UserGraphics, ISOGraphics, NoGraphics}; enum ConnectorGeometryEnumT {Vertical, Horizontal, Diagonal}; enum RenameRestrictionEnumT {Unrestricted, CoreRenameOnly}; enum UndoStatusEnumT {NoUndo, Undo}; +enum ResizbleEnumT {NotResizable, Resizable}; enum SaveTargetEnumT {ExistingFile, NewFile}; enum SaveContentsEnumT {FullModel, ParametersOnly}; enum ConnectorStyleEnumT {PowerConnectorStyle, SignalConnectorStyle, BrokenConnectorStyle, UndefinedConnectorStyle}; From 35d5c5c3aad95fdb4fabcc755bf87ac03f87f311 Mon Sep 17 00:00:00 2001 From: Robert Braun Date: Thu, 2 Apr 2026 13:29:43 +0200 Subject: [PATCH 12/20] Fix instantiation problems in FMIWrapper --- .../Connectivity/FMIWrapper.hpp | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/componentLibraries/defaultLibrary/Connectivity/FMIWrapper.hpp b/componentLibraries/defaultLibrary/Connectivity/FMIWrapper.hpp index c6542a3b2c..bed25404d7 100644 --- a/componentLibraries/defaultLibrary/Connectivity/FMIWrapper.hpp +++ b/componentLibraries/defaultLibrary/Connectivity/FMIWrapper.hpp @@ -229,7 +229,7 @@ class FMIWrapper : public ComponentSignal fmi3InstanceHandle *fmi3_instance; HString mVisibleOutputs; double mTolerance = 1e-4; - bool mLoggingOn = false; + bool mLoggingOn = true; bool mReinstantiate = false; bool mIsInstantiated = false; @@ -446,6 +446,18 @@ class FMIWrapper : public ComponentSignal } } else if(mFmiVersion == fmiVersion2) { + //Instantiate FMU + if(!mReinstantiate) { + addDebugMessage("Calling: fmi2Instantiate"); + fmi2_instance = fmi2_instantiate(fmu, fmi2CoSimulation, FMIWrapper_fmi2Logger, calloc, free, NULL, (fmi2ComponentEnvironment*)this, fmi2False, mLoggingOn); + if(!fmi2_instance) { + stopSimulation("Failed to instantiate FMU"); + fmi2_instance = NULL; + return; + } + mIsInstantiated = true; + } + if(fmi2_defaultToleranceDefined(fmu)) { mTolerance = fmi2_getDefaultTolerance(fmu); } @@ -547,19 +559,6 @@ class FMIWrapper : public ComponentSignal mVisibleOutputs.erase(mVisibleOutputs.size()-1,1); //Remove trailing comma } addConstant("visibleOutputs", "Visible output variables (hidden)", "", mVisibleOutputs, mVisibleOutputs); - - - //Instantiate FMU - if(!mReinstantiate) { - addDebugMessage("Calling: fmi2Instantiate"); - fmi2_instance = fmi2_instantiate(fmu, fmi2CoSimulation, FMIWrapper_fmi2Logger, calloc, free, NULL, (fmi2ComponentEnvironment*)this, fmi2False, mLoggingOn); - if(!fmi2_instance) { - stopSimulation("Failed to instantiate FMU"); - fmi2_instance = NULL; - return; - } - mIsInstantiated = true; - } } else {//FMI 3 if(fmi3_defaultToleranceDefined(fmu)) { From 6a776f960d4b259136e738dc534d1bc2cbbceeed Mon Sep 17 00:00:00 2001 From: Robert Braun Date: Thu, 2 Apr 2026 13:30:53 +0200 Subject: [PATCH 13/20] Add missing files --- HopsanGUI/Widgets/SSMEditorWidget.cpp | 147 +++++++++++++++++++++ HopsanGUI/Widgets/SSMEditorWidget.h | 65 +++++++++ HopsanGUI/Widgets/SSVEditorWidget.cpp | 183 ++++++++++++++++++++++++++ HopsanGUI/Widgets/SSVEditorWidget.h | 65 +++++++++ 4 files changed, 460 insertions(+) create mode 100644 HopsanGUI/Widgets/SSMEditorWidget.cpp create mode 100644 HopsanGUI/Widgets/SSMEditorWidget.h create mode 100644 HopsanGUI/Widgets/SSVEditorWidget.cpp create mode 100644 HopsanGUI/Widgets/SSVEditorWidget.h diff --git a/HopsanGUI/Widgets/SSMEditorWidget.cpp b/HopsanGUI/Widgets/SSMEditorWidget.cpp new file mode 100644 index 0000000000..acb305f1a7 --- /dev/null +++ b/HopsanGUI/Widgets/SSMEditorWidget.cpp @@ -0,0 +1,147 @@ +/*----------------------------------------------------------------------------- + + Copyright 2017 Hopsan Group + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + The full license is available in the file GPLv3. + For details about the 'Hopsan Group' or information about Authors and + Contributors see the HOPSANGROUP and AUTHORS files that are located in + the Hopsan source code root directory. + +-----------------------------------------------------------------------------*/ + +//! +//! @file SSMEditorWidget.cpp +//! @brief A widget for editing SSM parameter mappings +//! @author Robert Braun +//! +//$Id$ + +#include "SSMEditorWidget.h" +#include +#include +#include +#include +#include + +#include "ssp4c_ssm_parameter_mapping.h" +#include "ssp4c_ssm_mapping_entry.h" + +SSMEditorWidget::SSMEditorWidget(ssmParameterMappingHandle *pSsm, QString fileName, QWidget *parent) + : QWidget(parent), mpSsm(pSsm), mFileName(fileName) +{ + // Create table widget with columns for Source and Target + mpTable = new QTableWidget(this); + mpTable->setColumnCount(2); + mpTable->setHorizontalHeaderLabels(QStringList() << "Source" << "Target"); + mpTable->horizontalHeader()->setStretchLastSection(true); + mpTable->setSelectionBehavior(QAbstractItemView::SelectRows); + mpTable->setSelectionMode(QAbstractItemView::SingleSelection); + + // Create buttons + mpAddButton = new QPushButton("Add Mapping", this); + mpRemoveButton = new QPushButton("Remove Mapping", this); + + // Connect button signals + connect(mpAddButton, SIGNAL(clicked()), this, SLOT(addMapping())); + connect(mpRemoveButton, SIGNAL(clicked()), this, SLOT(removeMapping())); + + // Create layout + QVBoxLayout *pMainLayout = new QVBoxLayout(this); + pMainLayout->addWidget(mpTable); + + QHBoxLayout *pButtonLayout = new QHBoxLayout(); + pButtonLayout->addWidget(mpAddButton); + pButtonLayout->addWidget(mpRemoveButton); + pButtonLayout->addStretch(); + + pMainLayout->addLayout(pButtonLayout); + + // Load the SSM data + loadSSMData(); +} + +SSMEditorWidget::~SSMEditorWidget() +{ +} + +void SSMEditorWidget::loadSSMData() +{ + if (!mpSsm) + return; + + // Clear existing rows + mpTable->setRowCount(0); + + // Get number of mapping entries + int entryCount = ssp4c_ssm_parameterMapping_getNumberOfMappingEntries(mpSsm); + + // Add each mapping entry as a table row + for (int i = 0; i < entryCount; ++i) { + ssmParameterMappingEntryHandle *pEntry = ssp4c_ssm_parameterMapping_getMappingEntryByIndex(mpSsm, i); + if (!pEntry) + continue; + + // Insert a new row + int row = mpTable->rowCount(); + mpTable->insertRow(row); + + // Get mapping data + QString source = QString::fromUtf8(ssp4c_ssm_mappingEntry_getSource(pEntry)); + QString target = QString::fromUtf8(ssp4c_ssm_mappingEntry_getTarget(pEntry)); + + // Add items to table + QTableWidgetItem *pSourceItem = new QTableWidgetItem(source); + QTableWidgetItem *pTargetItem = new QTableWidgetItem(target); + + // Add items to row + mpTable->setItem(row, 0, pSourceItem); + mpTable->setItem(row, 1, pTargetItem); + } + + // Resize columns to content + mpTable->resizeColumnsToContents(); +} + +void SSMEditorWidget::addMapping() +{ + // Insert a new row at the end + int row = mpTable->rowCount(); + mpTable->insertRow(row); + + // Create editable items + QTableWidgetItem *pSourceItem = new QTableWidgetItem(""); + QTableWidgetItem *pTargetItem = new QTableWidgetItem(""); + + // Add items to row + mpTable->setItem(row, 0, pSourceItem); + mpTable->setItem(row, 1, pTargetItem); + + // Resize columns + mpTable->resizeColumnsToContents(); + + // Note: Actual SSM data modification would happen on save +} + +void SSMEditorWidget::removeMapping() +{ + // Remove currently selected row + int currentRow = mpTable->currentRow(); + if (currentRow >= 0) { + mpTable->removeRow(currentRow); + } + + // Note: Actual SSM data modification would happen on save +} diff --git a/HopsanGUI/Widgets/SSMEditorWidget.h b/HopsanGUI/Widgets/SSMEditorWidget.h new file mode 100644 index 0000000000..b6bae490e5 --- /dev/null +++ b/HopsanGUI/Widgets/SSMEditorWidget.h @@ -0,0 +1,65 @@ +/*----------------------------------------------------------------------------- + + Copyright 2017 Hopsan Group + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + The full license is available in the file GPLv3. + For details about the 'Hopsan Group' or information about Authors and + Contributors see the HOPSANGROUP and AUTHORS files that are located in + the Hopsan source code root directory. + +-----------------------------------------------------------------------------*/ + +//! +//! @file SSMEditorWidget.h +//! @brief A widget for editing SSM parameter mappings +//! @author Robert Braun +//! +//$Id$ + +#ifndef SSMEDITORGWIDGET_H +#define SSMEDITORGWIDGET_H + +#include +#include +#include + +// Forward declarations +class ssmParameterMappingHandle; +class QPushButton; + +class SSMEditorWidget : public QWidget +{ + Q_OBJECT + +public: + SSMEditorWidget(ssmParameterMappingHandle *pSsm, QString fileName, QWidget *parent = nullptr); + ~SSMEditorWidget(); + + void loadSSMData(); + +protected slots: + void addMapping(); + void removeMapping(); + +private: + ssmParameterMappingHandle *mpSsm; + QString mFileName; + QTableWidget *mpTable; + QPushButton *mpAddButton; + QPushButton *mpRemoveButton; +}; + +#endif // SSMEDITORGWIDGET_H diff --git a/HopsanGUI/Widgets/SSVEditorWidget.cpp b/HopsanGUI/Widgets/SSVEditorWidget.cpp new file mode 100644 index 0000000000..84feedbf22 --- /dev/null +++ b/HopsanGUI/Widgets/SSVEditorWidget.cpp @@ -0,0 +1,183 @@ +/*----------------------------------------------------------------------------- + + Copyright 2017 Hopsan Group + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + The full license is available in the file GPLv3. + For details about the 'Hopsan Group' or information about Authors and + Contributors see the HOPSANGROUP and AUTHORS files that are located in + the Hopsan source code root directory. + +-----------------------------------------------------------------------------*/ + +//! +//! @file SSVEditorWidget.cpp +//! @brief A widget for editing SSV parameter sets +//! @author Robert Braun +//! +//$Id$ + +#include "SSVEditorWidget.h" +#include +#include +#include +#include +#include + +#include "ssp4c.h" +#include "ssp4c_ssv_parameter_set.h" +#include "ssp4c_ssv_parameter.h" + +SSVEditorWidget::SSVEditorWidget(ssvParameterSetHandle *pSsv, QString fileName, QWidget *parent) + : QWidget(parent), mpSsv(pSsv), mFileName(fileName) +{ + // Create table widget with columns for Name, Unit, and Value + mpTable = new QTableWidget(this); + mpTable->setColumnCount(3); + mpTable->setHorizontalHeaderLabels(QStringList() << "Name" << "Unit" << "Value"); + mpTable->horizontalHeader()->setStretchLastSection(true); + mpTable->setSelectionBehavior(QAbstractItemView::SelectRows); + mpTable->setSelectionMode(QAbstractItemView::SingleSelection); + + // Create buttons + mpAddButton = new QPushButton("Add Parameter", this); + mpRemoveButton = new QPushButton("Remove Parameter", this); + + // Connect button signals + connect(mpAddButton, SIGNAL(clicked()), this, SLOT(addParameter())); + connect(mpRemoveButton, SIGNAL(clicked()), this, SLOT(removeParameter())); + + // Create layout + QVBoxLayout *pMainLayout = new QVBoxLayout(this); + pMainLayout->addWidget(mpTable); + + QHBoxLayout *pButtonLayout = new QHBoxLayout(); + pButtonLayout->addWidget(mpAddButton); + pButtonLayout->addWidget(mpRemoveButton); + pButtonLayout->addStretch(); + + pMainLayout->addLayout(pButtonLayout); + + // Load the SSV data + loadSSVData(); +} + +SSVEditorWidget::~SSVEditorWidget() +{ +} + +void SSVEditorWidget::loadSSVData() +{ + if (!mpSsv) + return; + + // Clear existing rows + mpTable->setRowCount(0); + + // Get number of parameters + int paramCount = ssp4c_ssv_parameterSet_getNumberOfParameters(mpSsv); + + // Add each parameter as a table row + for (int i = 0; i < paramCount; ++i) { + ssvParameterHandle *pParam = ssp4c_ssv_parameterSet_getParameterByIndex(mpSsv, i); + if (!pParam) + continue; + + // Insert a new row + int row = mpTable->rowCount(); + mpTable->insertRow(row); + + // Get parameter data + QString name = QString::fromUtf8(ssp4c_ssv_parameter_getName(pParam)); + QString unit = QString::fromUtf8(ssp4c_ssv_parameter_getUnit(pParam)); + QString value; + + sspDataType type = ssp4c_ssv_parameter_getDatatype(pParam); + if (type == sspDataTypeString) { + value = QString::fromUtf8(ssp4c_ssv_parameter_getStringValue(pParam)); + } else if (type == sspDataTypeReal || type == sspDataTypeFloat64 || type == sspDataTypeFloat32) { + value = QString::number(ssp4c_ssv_parameter_getRealValue(pParam)); + } else if (type == sspDataTypeInteger || + type == sspDataTypeInt8 || type == sspDataTypeUInt8 || + type == sspDataTypeInt16 || type == sspDataTypeUInt16 || + type == sspDataTypeInt32 || type == sspDataTypeUInt32 || + type == sspDataTypeInt64 || type == sspDataTypeUInt64) { + // For integer types, try to get as double and convert + value = QString::number((int)ssp4c_ssv_parameter_getRealValue(pParam)); + } else if (type == sspDataTypeBoolean) { + // For boolean, show as "true" or "false" based on the value + double boolValue = ssp4c_ssv_parameter_getRealValue(pParam); + value = (boolValue != 0.0) ? "true" : "false"; + } else { + // For other types (Enumeration, Binary, etc.), try to get string representation + value = QString::fromUtf8(ssp4c_ssv_parameter_getStringValue(pParam)); + } + + // Add items to table + QTableWidgetItem *pNameItem = new QTableWidgetItem(name); + QTableWidgetItem *pUnitItem = new QTableWidgetItem(unit); + QTableWidgetItem *pValueItem = new QTableWidgetItem(value); + + // Add items to row + mpTable->setItem(row, 0, pNameItem); + mpTable->setItem(row, 1, pUnitItem); + mpTable->setItem(row, 2, pValueItem); + + // Make name and unit read-only, but allow value editing + pNameItem->setFlags(pNameItem->flags() & ~Qt::ItemIsEditable); + pUnitItem->setFlags(pUnitItem->flags() & ~Qt::ItemIsEditable); + } + + // Resize columns to content + mpTable->resizeColumnsToContents(); +} + + +void SSVEditorWidget::addParameter() +{ + // Insert a new row at the end + int row = mpTable->rowCount(); + mpTable->insertRow(row); + + // Create editable items + QTableWidgetItem *pNameItem = new QTableWidgetItem("New_Parameter"); + QTableWidgetItem *pUnitItem = new QTableWidgetItem(""); + QTableWidgetItem *pValueItem = new QTableWidgetItem("0"); + + // Make name and unit read-only + pNameItem->setFlags(pNameItem->flags() & ~Qt::ItemIsEditable); + pUnitItem->setFlags(pUnitItem->flags() & ~Qt::ItemIsEditable); + + // Add items to row + mpTable->setItem(row, 0, pNameItem); + mpTable->setItem(row, 1, pUnitItem); + mpTable->setItem(row, 2, pValueItem); + + // Resize columns + mpTable->resizeColumnsToContents(); + + // Note: Actual SSV data modification would happen on save +} + +void SSVEditorWidget::removeParameter() +{ + // Remove currently selected row + int currentRow = mpTable->currentRow(); + if (currentRow >= 0) { + mpTable->removeRow(currentRow); + } + + // Note: Actual SSV data modification would happen on save +} diff --git a/HopsanGUI/Widgets/SSVEditorWidget.h b/HopsanGUI/Widgets/SSVEditorWidget.h new file mode 100644 index 0000000000..6b3b77c8d9 --- /dev/null +++ b/HopsanGUI/Widgets/SSVEditorWidget.h @@ -0,0 +1,65 @@ +/*----------------------------------------------------------------------------- + + Copyright 2017 Hopsan Group + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + The full license is available in the file GPLv3. + For details about the 'Hopsan Group' or information about Authors and + Contributors see the HOPSANGROUP and AUTHORS files that are located in + the Hopsan source code root directory. + +-----------------------------------------------------------------------------*/ + +//! +//! @file SSVEditorWidget.h +//! @brief A widget for editing SSV parameter sets +//! @author Robert Braun +//! +//$Id$ + +#ifndef SSVEDITORWIDGET_H +#define SSVEDITORWIDGET_H + +#include +#include +#include + +// Forward declarations +class ssvParameterSetHandle; +class QPushButton; + +class SSVEditorWidget : public QWidget +{ + Q_OBJECT + +public: + SSVEditorWidget(ssvParameterSetHandle *pSsv, QString fileName, QWidget *parent = nullptr); + ~SSVEditorWidget(); + + void loadSSVData(); + +protected slots: + void addParameter(); + void removeParameter(); + +private: + ssvParameterSetHandle *mpSsv; + QString mFileName; + QTableWidget *mpTable; + QPushButton *mpAddButton; + QPushButton *mpRemoveButton; +}; + +#endif // SSVEDITORWIDGET_H From ab32fc2154b4a103dc1f54da90f6cb8f4b319f9b Mon Sep 17 00:00:00 2001 From: Robert Braun Date: Thu, 2 Apr 2026 14:38:08 +0200 Subject: [PATCH 14/20] Save SSP geometries --- HopsanGUI/GUIConnector.cpp | 7 +++ HopsanGUI/GUIConnector.h | 1 + HopsanGUI/Widgets/ModelWidget.cpp | 74 ++++++++++++++++++++++++++----- HopsanGUI/Widgets/ModelWidget.h | 5 ++- HopsanGUI/Widgets/SSPWidget.cpp | 46 ++++++++++++++++++- HopsanGUI/Widgets/SSPWidget.h | 4 +- 6 files changed, 123 insertions(+), 14 deletions(-) diff --git a/HopsanGUI/GUIConnector.cpp b/HopsanGUI/GUIConnector.cpp index 433acc7a40..946eccc7e6 100644 --- a/HopsanGUI/GUIConnector.cpp +++ b/HopsanGUI/GUIConnector.cpp @@ -472,6 +472,13 @@ QPointF Connector::getEndPoint() return mPoints.last(); } + +//! @brief Returns all points of the connector +QVector Connector::getPoints() const +{ + return mPoints; +} + //! @brief Returns the name of the start port of a connector //! @see getEndPortName() QString Connector::getStartPortName() const diff --git a/HopsanGUI/GUIConnector.h b/HopsanGUI/GUIConnector.h index 918179d14a..c319aad2d3 100644 --- a/HopsanGUI/GUIConnector.h +++ b/HopsanGUI/GUIConnector.h @@ -81,6 +81,7 @@ class Connector : public QGraphicsWidget void setPointsAndGeometries(const QVector &rPoints, const QStringList &rGeometries); QPointF getStartPoint(); QPointF getEndPoint(); + QVector getPoints() const; ConnectorLine *getLine(int line); ConnectorLine *getLastLine(); int getNumberOfLines(); diff --git a/HopsanGUI/Widgets/ModelWidget.cpp b/HopsanGUI/Widgets/ModelWidget.cpp index e1dde952bb..2e1e0efcce 100644 --- a/HopsanGUI/Widgets/ModelWidget.cpp +++ b/HopsanGUI/Widgets/ModelWidget.cpp @@ -74,6 +74,9 @@ #include "ssp4c_ssd.h" #include "ssp4c_ssd_system.h" +#include "ssp4c_ssd_element_geometry.h" +#include "ssp4c_ssd_connection.h" +#include "ssp4c_ssd_connection_geometry.h" //! @class ModelWidget //! @brief The ModelWidget class is a Widget to contain a simulation model @@ -168,6 +171,7 @@ ModelWidget::ModelWidget(ModelHandler *pModelHandler, CentralTabWidget *pParentT mLastSimulationTime = 0; mSsd = nullptr; + mSsp = nullptr; } @@ -339,9 +343,10 @@ void ModelWidget::setLogDataHandler(QSharedPointer pHandler) pHandler->setParentModel(this); } -void ModelWidget::setSsdHandle(ssdHandle *ssd) +void ModelWidget::setSsdHandle(ssdHandle *ssd, sspHandle *ssp) { mSsd = ssd; + mSsp = ssp; } //! @brief Sets last simulation time (only use this from project tab widget!) @@ -1361,22 +1366,69 @@ void ModelWidget::saveModel(SaveTargetEnumT saveAsFlag, SaveContentsEnumT conten auto comp = ssp4c_ssd_system_getComponentByIndex(rootSystem, c); ssdMap.insert(ssp4c_ssd_component_getName(comp), comp); } + QMap ssdConnectionMap; + int numConnections = ssp4c_ssd_system_getNumberOfConnections(rootSystem); + for(int c=0; c" + endComp + ":" + endPort; + ssdConnectionMap.insert(key, con); + } for(const auto &c : mpToplevelSystem->getModelObjects()) { if(ssdMap.contains(c->getName())) { - //Update! - qDebug() << "Updating: " << c->getName(); - } - else { - //Add! - qDebug() << "Adding: " << c->getName(); + ssdComponentHandle* ssdComp = ssdMap[c->getName()]; + ssdElementGeometryHandle* geom = ssp4c_ssd_component_getElementGeometry(ssdComp); + if(geom) { + QPointF center = c->getCenterPos(); + QRectF rect = c->boundingRect(); + double width = rect.width(); + double height = rect.height(); + double x1 = center.x() - width/2 - 2500; + double x2 = center.x() + width/2 - 2500; + double y1 = center.y() - height/2 - 2500; + double y2 = center.y() + height/2 - 2500; + double rotation = c->rotation(); + ssp4c_ssd_elementGeometry_setX1(geom, x1); + ssp4c_ssd_elementGeometry_setX2(geom, x2); + ssp4c_ssd_elementGeometry_setY1(geom, y1); + ssp4c_ssd_elementGeometry_setY2(geom, y2); + ssp4c_ssd_elementGeometry_setRotation(geom, rotation); + } } } - for(const auto &name : ssdMap.keys()) { - //Remove! - if(!mpToplevelSystem->hasModelObject(name)) { - qDebug() << "Removing: " << name; + for(auto connector : mpToplevelSystem->getSubConnectorPtrs()) { + QString startComp = connector->getStartComponentName(); + QString startPort = connector->getStartPortName(); + QString endComp = connector->getEndComponentName(); + QString endPort = connector->getEndPortName(); + QString key = startComp + ":" + startPort + "->" + endComp + ":" + endPort; + if(ssdConnectionMap.contains(key)) { + ssdConnectionHandle* ssdCon = ssdConnectionMap[key]; + ssdConnectionGeometryHandle* conGeom = ssp4c_ssd_connection_getConnectionGeometry(ssdCon); + if(conGeom) { + QVector points = connector->getPoints(); + int count = points.size(); + double* xArray = new double[count]; + double* yArray = new double[count]; + for(int i=0; igetModelFileInfo().filePath().toStdString().c_str()); + } + this->setSaved(true); return; } diff --git a/HopsanGUI/Widgets/ModelWidget.h b/HopsanGUI/Widgets/ModelWidget.h index c56adeb64d..4dc8d2d687 100644 --- a/HopsanGUI/Widgets/ModelWidget.h +++ b/HopsanGUI/Widgets/ModelWidget.h @@ -42,6 +42,8 @@ #include #include +#include + //Hopsan includes #include "common.h" @@ -125,7 +127,7 @@ class ModelWidget : public QWidget GraphicsView *mpGraphicsView; AnimationWidget *mpAnimationWidget; - void setSsdHandle(ssdHandle* ssd); + void setSsdHandle(ssdHandle* ssd, sspHandle* ssp = nullptr); public slots: void setTopLevelSimulationTime(const QString startTime, const QString timeStep, const QString stopTime, UndoStatusEnumT undoSettings=Undo); @@ -201,6 +203,7 @@ private slots: QMutex mSimulateMutex; ssdHandle *mSsd; + sspHandle *mSsp; }; diff --git a/HopsanGUI/Widgets/SSPWidget.cpp b/HopsanGUI/Widgets/SSPWidget.cpp index 49b3219d4b..52f0eae363 100644 --- a/HopsanGUI/Widgets/SSPWidget.cpp +++ b/HopsanGUI/Widgets/SSPWidget.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include "global.h" #include "ssp4c.h" @@ -85,6 +86,9 @@ void SSPWidget::addSSP(QFileInfo path) pSspItem->setText(0,path.fileName()); mpTree->addTopLevelItem(pSspItem); + itemToSspMap.insert(pSspItem, ssp); + mSspFileMap.insert(ssp, path); + connect(mpTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(openSSDModel(QTreeWidgetItem*,int))); qDebug() << "SSP unzipped path: " << ssp4c_getUnzippedLocation(ssp); @@ -290,6 +294,15 @@ void SSPTreeWidget::contextMenuEvent(QContextMenuEvent *event) QMenu menu(this); + if(item->type() == SSPItem) { + menu.addAction("Save SSP", [this, item]() { + auto p = qobject_cast(parentWidget()); + if(p) { + p->saveSspModel(item); + } + }); + } + if((item->type() == SSVItem || item->type() == SSMItem) && item->parent()->type() != SSPItem) { menu.addAction("Remove from SSD", [this, item]() { QTreeWidgetItem *parent = item->parent(); @@ -326,7 +339,7 @@ void SSPWidget::openSSDModel(QTreeWidgetItem *item, int) auto pModel = gpModelHandler->addNewModel(ssp4c_ssd_getName(ssd)); pModel->setModelType(ModelWidget::SsdModel); - pModel->setSsdHandle(ssd); + pModel->setSsdHandle(ssd, ssp); int componentCount = ssp4c_ssd_system_getNumberOfComponents(system); for(int i=0; itype() != SSPTreeWidget::SSPItem) { + return; + } + + if(!itemToSspMap.contains(item)) { + qDebug() << "SSP save: no associated ssp handle"; + return; + } + + sspHandle *ssp = itemToSspMap[item]; + QFileInfo outFile = mSspFileMap.value(ssp); + + if(outFile.filePath().isEmpty()) { + QString saveFile = QFileDialog::getSaveFileName(this, tr("Save SSP"), QString(), tr("SSP Files (*.ssp);;All Files (*)")); + if(saveFile.isEmpty()) { + return; // canceled + } + outFile.setFile(saveFile); + } + + bool success = (ssp4c_saveSsp(ssp, outFile.absoluteFilePath().toStdString().c_str()) == 0); + if(success) { + qDebug() << "SSP saved:" << outFile.absoluteFilePath(); + } + else { + qDebug() << "SSP save failed:" << outFile.absoluteFilePath(); + } +} + void SSPWidget::openSSVEditor(QTreeWidgetItem *item, int) { if (!item || !itemToSsvMap.contains(item)) { diff --git a/HopsanGUI/Widgets/SSPWidget.h b/HopsanGUI/Widgets/SSPWidget.h index a1dd71c435..c1cbf663af 100644 --- a/HopsanGUI/Widgets/SSPWidget.h +++ b/HopsanGUI/Widgets/SSPWidget.h @@ -23,7 +23,8 @@ class SSPWidget : public QWidget void addSSP(QFileInfo path); - public slots: +public slots: + void saveSspModel(QTreeWidgetItem *item); protected slots: void openSSDModel(QTreeWidgetItem*item, int); @@ -38,6 +39,7 @@ protected slots: QMap itemToSystemMap; QMap itemToSsvMap; QMap itemToSsmMap; + QMap mSspFileMap; }; class SSPTreeWidget : public QTreeWidget From 08673e06a05a618470eb236e14261b6a854a1d88 Mon Sep 17 00:00:00 2001 From: Robert Braun Date: Wed, 8 Apr 2026 14:46:21 +0200 Subject: [PATCH 15/20] Save connector geometries to SSP --- HopsanGUI/Widgets/ModelWidget.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/HopsanGUI/Widgets/ModelWidget.cpp b/HopsanGUI/Widgets/ModelWidget.cpp index 2e1e0efcce..753acf965e 100644 --- a/HopsanGUI/Widgets/ModelWidget.cpp +++ b/HopsanGUI/Widgets/ModelWidget.cpp @@ -1411,14 +1411,14 @@ void ModelWidget::saveModel(SaveTargetEnumT saveAsFlag, SaveContentsEnumT conten if(conGeom) { QVector points = connector->getPoints(); int count = points.size(); - double* xArray = new double[count]; - double* yArray = new double[count]; - for(int i=0; i Date: Tue, 9 Jun 2026 12:57:26 +0200 Subject: [PATCH 16/20] Download ssp4c dependency --- HopsanGenerator/templates/fmu3_model.c | 2 +- dependencies/dependencies.xml | 6 ++++++ dependencies/setupAll.bat | 1 + dependencies/setupAll.sh | 1 + dependencies/setupSsp4c.sh | 2 +- 5 files changed, 10 insertions(+), 2 deletions(-) diff --git a/HopsanGenerator/templates/fmu3_model.c b/HopsanGenerator/templates/fmu3_model.c index ff24969f77..0937ba0be0 100644 --- a/HopsanGenerator/templates/fmu3_model.c +++ b/HopsanGenerator/templates/fmu3_model.c @@ -233,7 +233,7 @@ fmi3Status fmi3EnterInitializationMode(fmi3Instance instance, } if(fmu->loggingOn) { - fmu->logger(fmu->instanceEnvironment, fmi3Error, "info", "Entering initialization mode..."); + fmu->logger(fmu->instanceEnvironment, fmi3Info, "info", "Entering initialization mode..."); } fmu->pSystem->initialize(startTime, stopTime); diff --git a/dependencies/dependencies.xml b/dependencies/dependencies.xml index 96c9f71ab9..3413d3f24f 100644 --- a/dependencies/dependencies.xml +++ b/dependencies/dependencies.xml @@ -121,4 +121,10 @@ + + + https://github.com/robbr48/ssp4c/archive/refs/tags/v1.0.zip + + + diff --git a/dependencies/setupAll.bat b/dependencies/setupAll.bat index 0c80b68e3c..dbc78185fd 100644 --- a/dependencies/setupAll.bat +++ b/dependencies/setupAll.bat @@ -12,3 +12,4 @@ start /wait cmd /c setupLibzip.bat start /wait cmd /c setupXerces.bat REM DCPLib must come after xerces and libzip (zlib before libzip) start /wait cmd /c setupDCPLib.bat +start /wait cmd /c setupSsp4c.bat diff --git a/dependencies/setupAll.sh b/dependencies/setupAll.sh index 3653311667..66cb19dd0e 100755 --- a/dependencies/setupAll.sh +++ b/dependencies/setupAll.sh @@ -12,3 +12,4 @@ ./setupZeroMQ.sh # DCPLib must come after xerces and libzip ./setupDCPLib.sh +./setupSsp4c.sh diff --git a/dependencies/setupSsp4c.sh b/dependencies/setupSsp4c.sh index 7ca294c520..4fbb3fa7d0 100755 --- a/dependencies/setupSsp4c.sh +++ b/dependencies/setupSsp4c.sh @@ -11,7 +11,7 @@ installdir=${basedir}/${name} # Download and verify -#./download-dependencies.py ${name} +./download-dependencies.py ${name} # Include general settings source setHopsanBuildPaths.sh From c25194022f6c9a55f8ecd7b66996c9f7ebbc3c99 Mon Sep 17 00:00:00 2001 From: Robert Braun Date: Tue, 9 Jun 2026 14:22:13 +0200 Subject: [PATCH 17/20] Fix failed merge --- HopsanGUI/CMakeLists.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/HopsanGUI/CMakeLists.txt b/HopsanGUI/CMakeLists.txt index 6e7b6b6fd3..7bc5b10cf2 100644 --- a/HopsanGUI/CMakeLists.txt +++ b/HopsanGUI/CMakeLists.txt @@ -41,15 +41,9 @@ endif() target_link_libraries(${target_name} Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Xml Qt5::Svg Qt5::Network Qt5::Test Qt5::PrintSupport qwt hopsancore hopsangeneratorgui symhop ops hopsandcp xercesc ssp4c ) -<<<<<<< HEAD -<<<<<<< HEAD if(UNIX) target_link_libraries(${target_name} Qt5::WebEngineWidgets) endif() -======= ->>>>>>> 226b534b5 (SSP support (wip)) -======= ->>>>>>> 8bad348f9 (SSP support (wip)) target_link_optional_libraries(${target_name} libhopsanremoteclient hopsanhdf5exporter discount katex) From 0322f66ae78d507937b06d6b4de5b10e450dbc2f Mon Sep 17 00:00:00 2001 From: Robert Braun Date: Tue, 9 Jun 2026 15:35:18 +0200 Subject: [PATCH 18/20] Add ssp4c to workflows --- .appveyor.yml | 3 ++- .github/workflows/ci-build.yaml | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 9f51d4dbb2..9c770cae6d 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -9,7 +9,7 @@ configuration: Release install: - git submodule update --init - cd dependencies - - download-dependencies.py discount qwt zeromq cppzmq msgpack-c katex tclap fmi4c xerces asio zlib libzip dcplib + - download-dependencies.py discount qwt zeromq cppzmq msgpack-c katex tclap fmi4c xerces asio zlib libzip dcplib ssp4c - cd .. build_script: # Remove sh.exe from PATH so that CMake works @@ -28,6 +28,7 @@ build_script: - setupQwt.bat - setupZeroMQ.bat - setupMsgpack.bat + setupSsp4c.bat - call setHopsanBuildPaths.bat - cd .. - mkdir build diff --git a/.github/workflows/ci-build.yaml b/.github/workflows/ci-build.yaml index 04fe127a9c..fd86150961 100644 --- a/.github/workflows/ci-build.yaml +++ b/.github/workflows/ci-build.yaml @@ -38,7 +38,7 @@ jobs: - name: Download dependencies shell: cmd working-directory: dependencies - run: python download-dependencies.py discount tclap qwt fmi4c xerces asio zlib libzip dcplib + run: python download-dependencies.py discount tclap qwt fmi4c xerces asio zlib libzip dcplib ssp4c - name: Build dependencies shell: cmd @@ -53,6 +53,7 @@ jobs: call setupXerces.bat call setupDCPLib.bat call setupQwt.bat + call setupSsp4c.bat - name: Configure working-directory: hopsan-build @@ -94,7 +95,7 @@ jobs: - name: Download dependencies shell: bash working-directory: dependencies - run: ./download-dependencies.py discount tclap fmi4c xerces asio dcplib libzip qwt + run: ./download-dependencies.py discount tclap fmi4c xerces asio dcplib libzip qwt ssp4c - name: Build dependencies shell: bash @@ -107,6 +108,7 @@ jobs: ./setupXerces.sh ./setupDCPLib.sh ./setupQwt.sh + ./setupSsp4c.sh - name: Configure working-directory: hopsan-build From 376a4fe90a56c33b265ea4703a4680af982e56ba Mon Sep 17 00:00:00 2001 From: Robert Braun Date: Wed, 10 Jun 2026 09:43:02 +0200 Subject: [PATCH 19/20] Fix checksum of ssp4c --- dependencies/dependencies.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/dependencies.xml b/dependencies/dependencies.xml index 3413d3f24f..7171049e1b 100644 --- a/dependencies/dependencies.xml +++ b/dependencies/dependencies.xml @@ -122,7 +122,7 @@ - + https://github.com/robbr48/ssp4c/archive/refs/tags/v1.0.zip From d9a06a02f0e8305003894685d8611a35e8582f68 Mon Sep 17 00:00:00 2001 From: Robert Braun Date: Wed, 10 Jun 2026 10:30:57 +0200 Subject: [PATCH 20/20] Fix typo --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 9c770cae6d..83cb2f5059 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -28,7 +28,7 @@ build_script: - setupQwt.bat - setupZeroMQ.bat - setupMsgpack.bat - setupSsp4c.bat + - setupSsp4c.bat - call setHopsanBuildPaths.bat - cd .. - mkdir build