From 6da353f180d2ad3303e651e870f9fcdc7ce2036a Mon Sep 17 00:00:00 2001 From: Bernhard Thiele Date: Fri, 26 May 2017 17:26:45 +0200 Subject: [PATCH] Feature real-time scaling #215 --- .../Blocks/OperatingSystem.mo | 15 +++++- .../ClockedBlocks/OperatingSystem.mo | 14 +++++- .../RealTimeSynchronization.mo | 2 +- .../OperatingSystem/realtimeSynchronize.mo | 4 +- .../Include/MDDRealtimeSynchronize.h | 49 ++++++++++++++----- 5 files changed, 69 insertions(+), 15 deletions(-) diff --git a/Modelica_DeviceDrivers/Blocks/OperatingSystem.mo b/Modelica_DeviceDrivers/Blocks/OperatingSystem.mo index 7f38e435..a4504be3 100644 --- a/Modelica_DeviceDrivers/Blocks/OperatingSystem.mo +++ b/Modelica_DeviceDrivers/Blocks/OperatingSystem.mo @@ -38,14 +38,27 @@ package OperatingSystem parameter Boolean setPriority = true "true, if process priority is to be set, otherwise false"; parameter Types.ProcessPriority priority = "Normal" "Priority of the simulation process" annotation(Dialog(enable=setPriority)); + parameter Boolean enableRealTimeScaling = false + "true, enable external real-time scaling input signal" + annotation (Dialog(group="Advanced"), choices(checkBox=true)); + Modelica.Blocks.Interfaces.RealInput scaling if enableRealTimeScaling + "Real-time scaling factor; > 1 means the simulation is made slower than real-time" + annotation (Placement(transformation(extent={{-140,-20},{-100,20}}))); output Modelica.SIunits.Time calculationTime "Time needed for calculation"; output Modelica.SIunits.Time availableTime "Time available for calculation (integrator step size)"; protected ProcessPriority procPrio(priority = priority) if setPriority; Real dummyState(start = 0, fixed=true) "dummy state to be integrated, to force synchronization in every integration step"; Modelica_DeviceDrivers.OperatingSystem.RealTimeSynchronization rtSync = Modelica_DeviceDrivers.OperatingSystem.RealTimeSynchronization(); + /* Connectors for conditional connect equations */ + Modelica.Blocks.Interfaces.RealInput defaultScaling = 1 if not enableRealTimeScaling "Default real-time scaling"; + Modelica.Blocks.Interfaces.RealInput actScaling annotation (HideResult=true); equation - (calculationTime, availableTime) = Modelica_DeviceDrivers.OperatingSystem.realtimeSynchronize(rtSync, time); + /* Conditional connect equations to either use external real-time scaling input or default scaling */ + connect(defaultScaling, actScaling); + connect(scaling, actScaling); + + (calculationTime, availableTime) = Modelica_DeviceDrivers.OperatingSystem.realtimeSynchronize(rtSync, time, enableRealTimeScaling, actScaling); der(dummyState) = calculationTime; annotation (preferredView="info", Icon(coordinateSystem(preserveAspectRatio=true, extent={{-100, diff --git a/Modelica_DeviceDrivers/ClockedBlocks/OperatingSystem.mo b/Modelica_DeviceDrivers/ClockedBlocks/OperatingSystem.mo index 68404215..ead5b745 100644 --- a/Modelica_DeviceDrivers/ClockedBlocks/OperatingSystem.mo +++ b/Modelica_DeviceDrivers/ClockedBlocks/OperatingSystem.mo @@ -40,6 +40,12 @@ package OperatingSystem parameter Boolean setPriority = true "true, if process priority is to be set, otherwise false"; parameter Types.ProcessPriority priority = "Normal" "Priority of the simulation process" annotation(Dialog(enable=setPriority)); + parameter Boolean enableRealTimeScaling = false + "true, enable external real-time scaling input signal" + annotation (Dialog(group="Advanced"), choices(checkBox=true)); + Modelica.Blocks.Interfaces.RealInput scaling if enableRealTimeScaling + "Real-time scaling factor; > 1 means the simulation is made slower than real-time" + annotation (Placement(transformation(extent={{-140,-20},{-100,20}}))); output Modelica.SIunits.Time calculationTime "Time needed for calculation"; output Modelica.SIunits.Time availableTime "Time available for calculation (integrator step size)"; protected @@ -47,11 +53,17 @@ package OperatingSystem Modelica.Blocks.Interfaces.BooleanInput initialized; Modelica.Blocks.Interfaces.BooleanOutput dummyTrue = true if not setPriority; Modelica_DeviceDrivers.OperatingSystem.RealTimeSynchronization rtSync = Modelica_DeviceDrivers.OperatingSystem.RealTimeSynchronization(); + /* Connectors for conditional connect equations */ + Modelica.Blocks.Interfaces.RealInput defaultScaling = 1 if not enableRealTimeScaling "Default real-time scaling"; + Modelica.Blocks.Interfaces.RealInput actScaling annotation (HideResult=true); algorithm if initialized then - (calculationTime, availableTime) := Modelica_DeviceDrivers.OperatingSystem.realtimeSynchronize(rtSync, time); + (calculationTime, availableTime) := Modelica_DeviceDrivers.OperatingSystem.realtimeSynchronize(rtSync, time, enableRealTimeScaling, actScaling); end if; equation + /* Condional connect equations */ + connect(defaultScaling, actScaling); + connect(scaling, actScaling); connect(procPrio.initialized, initialized); connect(dummyTrue, initialized); annotation (preferredView="info", diff --git a/Modelica_DeviceDrivers/OperatingSystem/RealTimeSynchronization.mo b/Modelica_DeviceDrivers/OperatingSystem/RealTimeSynchronization.mo index fe8203bd..9d3868ec 100644 --- a/Modelica_DeviceDrivers/OperatingSystem/RealTimeSynchronization.mo +++ b/Modelica_DeviceDrivers/OperatingSystem/RealTimeSynchronization.mo @@ -5,7 +5,7 @@ class RealTimeSynchronization "An object for real-time synchronization." import Modelica; extends Modelica.Icons.Function; output RealTimeSynchronization rtSync; - external "C" rtSync= MDD_realtimeSynchronizeConstructor() + external "C" rtSync = MDD_realtimeSynchronizeConstructor() annotation(Include = "#include \"MDDRealtimeSynchronize.h\"", Library = {"rt", "Winmm"}, __iti_dll = "ITI_MDD.dll", diff --git a/Modelica_DeviceDrivers/OperatingSystem/realtimeSynchronize.mo b/Modelica_DeviceDrivers/OperatingSystem/realtimeSynchronize.mo index 94aa4c11..fe2b2d1a 100644 --- a/Modelica_DeviceDrivers/OperatingSystem/realtimeSynchronize.mo +++ b/Modelica_DeviceDrivers/OperatingSystem/realtimeSynchronize.mo @@ -5,9 +5,11 @@ function realtimeSynchronize extends Modelica.Icons.Function; input Modelica_DeviceDrivers.OperatingSystem.RealTimeSynchronization rtSync; input Real simTime; + input Boolean enableScaling = false "true, enable real-time scaling, else disable scaling"; + input Real scaling(min=0) = 1 "real-time scaling factor; > 1 means the simulation is made slower than real-time"; output Real calculationTime; output Real availableTime; - external "C" calculationTime = MDD_realtimeSynchronize(rtSync, simTime, availableTime) + external "C" calculationTime = MDD_realtimeSynchronize(rtSync, simTime, enableScaling, scaling, availableTime) annotation(Include = "#include \"MDDRealtimeSynchronize.h\"", Library = {"rt", "Winmm"}, __iti_dll = "ITI_MDD.dll", diff --git a/Modelica_DeviceDrivers/Resources/Include/MDDRealtimeSynchronize.h b/Modelica_DeviceDrivers/Resources/Include/MDDRealtimeSynchronize.h index 72b9a331..2b321ab5 100644 --- a/Modelica_DeviceDrivers/Resources/Include/MDDRealtimeSynchronize.h +++ b/Modelica_DeviceDrivers/Resources/Include/MDDRealtimeSynchronize.h @@ -171,6 +171,7 @@ typedef struct { double lastSimTime; double lastIntegratorTimeStep; double lastCalculationTime; + double lastSyncTime; /* last (potentially scaled) syncronization time */ } RTSync; DllExport void* MDD_realtimeSynchronizeConstructor(void) { @@ -187,6 +188,7 @@ DllExport void* MDD_realtimeSynchronizeConstructor(void) { rtSync->lastSimTime = 0.; rtSync->lastIntegratorTimeStep = 0.; rtSync->lastCalculationTime = 0.; + rtSync->lastSyncTime = 0.; } timeBeginPeriod(1); return (void*) rtSync; @@ -216,10 +218,12 @@ DllExport double MDD_getTimeMS(double dummy) { * * @param[in] Real-time synchronization object * @param[in] simTime (s) simulation time - * @param[out] availableTime time that is left before realtime deadline is reached. + * @param[in] enableScaling if true enable real-time scaling, else disable scaling + * @param[in] scaling real-time scaling factor; > 1 means the simulation is made slower than real-time + * @param[out] availableTime time that is left before realtime deadline is reached BUG Windows-Linux implementation differ! * @return (s) Time between invocation of this function, i.e. "computing time" in seconds */ -DllExport double MDD_realtimeSynchronize(void* rtSyncObj, double simTime, double * integratorTimeStep) { +DllExport double MDD_realtimeSynchronize(void* rtSyncObj, double simTime, int enableScaling, double scaling, double * integratorTimeStep) { double calculationTime = 0.; RTSync* rtSync = (RTSync*) rtSyncObj; if (rtSync && integratorTimeStep) { @@ -229,10 +233,16 @@ DllExport double MDD_realtimeSynchronize(void* rtSyncObj, double simTime, double double timeLeft; QueryPerformanceCounter(&now); calculationTime = (double)(now.QuadPart * 1e6 - rtSync->lastTime.QuadPart * 1e6)/(double)rtSync->frequency.QuadPart*1.e-6; + *integratorTimeStep = simTime - rtSync->lastSimTime; + + /* scaled syncronization time. "scaling > 1" means the simulation takes longer than real-time. + No scaling => syncTime == simTime */ + double syncTime = enableScaling ? rtSync->lastSyncTime + (simTime - rtSync->lastSimTime)*scaling : simTime; + do { QueryPerformanceCounter(&now); - timeLeft = simTime - (double)(now.QuadPart - rtSync->startTime.QuadPart)/(double)rtSync->frequency.QuadPart; + timeLeft = syncTime - (double)(now.QuadPart - rtSync->startTime.QuadPart)/(double)rtSync->frequency.QuadPart; if (timeLeft > 0) { if (timeLeft > 0.002) { Sleep(1); @@ -249,6 +259,7 @@ DllExport double MDD_realtimeSynchronize(void* rtSyncObj, double simTime, double rtSync->lastSimTime = simTime; rtSync->lastIntegratorTimeStep = *integratorTimeStep; rtSync->lastCalculationTime = calculationTime; + rtSync->lastSyncTime = syncTime; } else { calculationTime = rtSync->lastCalculationTime; @@ -375,9 +386,15 @@ void MDD_setPriority(void* dummyPrioObj, int priority) { typedef struct { struct timespec t_start; struct timespec t_clockRealtime; /* current/last time of real time clock */ + double lastSimTime; /* last simulation time */ + double lastSyncTime; /* last (potentially scaled) syncronization time */ } RTSync; -void* MDD_realtimeSynchronizeConstructor(void) { +/** RTSync constructor + * + * @return RTSync object + */ +void* MDD_realtimeSynchronizeConstructor() { RTSync* rtSync = (RTSync*) malloc(sizeof(RTSync)); if (rtSync) { int ret = clock_gettime(CLOCK_MONOTONIC, &rtSync->t_start); @@ -387,6 +404,8 @@ void* MDD_realtimeSynchronizeConstructor(void) { ModelicaError("MDDRealtimeSynchronize.h: clock_gettime(..) failed\n"); } rtSync->t_clockRealtime = rtSync->t_start; + rtSync->lastSimTime = 0; /* Presume we start from initial time = 0 */ + rtSync->lastSyncTime = 0; /* Presume we start from initial time = 0 */ } return (void*) rtSync; } @@ -402,16 +421,18 @@ void MDD_realtimeSynchronizeDestructor(void* rtSyncObj) { * * @param[in] Real-time synchronization object * @param[in] simTime (s) simulation time - * @param[out] availableTime time that is left before realtime deadline is reached. + * @param[in] enableScaling if true enable real-time scaling, else disable scaling + * @param[in] scaling real-time scaling factor; > 1 means the simulation is made slower than real-time + * @param[out] availableTime time that is left before realtime deadline is reached BUG Windows-Linux implementation differ! * @return (s) Time between invocation of this function, i.e. "computing time" in seconds */ -double MDD_realtimeSynchronize(void* rtSyncObj, double simTime, double * availableTime) { +double MDD_realtimeSynchronize(void* rtSyncObj, double simTime, int enableScaling, double scaling, double * availableTime) { double deltaTime = 0.; - RTSync* rtSync = (RTSync*) rtSyncObj; + RTSync* rtSync = (RTSync*) rtSyncObj; if (rtSync && availableTime) { struct timespec t_abs; /* Absolute time until which execution will be delayed (to catch up with real-time) */ double fractpart, intpart; - int ret; + int ret; /* save away value of last time that the real-time clock was inquired */ deltaTime = rtSync->t_clockRealtime.tv_sec + (double)rtSync->t_clockRealtime.tv_nsec/NSEC_PER_SEC; @@ -421,8 +442,12 @@ double MDD_realtimeSynchronize(void* rtSyncObj, double simTime, double * availab * old value of the real-time clock from the current (new) value of the real-time clock */ deltaTime = (rtSync->t_clockRealtime.tv_sec + (double)rtSync->t_clockRealtime.tv_nsec/NSEC_PER_SEC) - deltaTime; - /* convert simulation time to corresponding real-time clock value */ - fractpart = modf(simTime, &intpart); + /* scaled syncronization time. "scaling > 1" means the simulation takes longer than real-time. + No scaling => syncTime == simTime */ + double syncTime = enableScaling ? rtSync->lastSyncTime + (simTime - rtSync->lastSimTime)*scaling : simTime; + + /* convert (scaled) simulation time to corresponding real-time clock value */ + fractpart = modf(syncTime, &intpart); t_abs.tv_sec = rtSync->t_start.tv_sec + (time_t) intpart; t_abs.tv_nsec = rtSync->t_start.tv_nsec + (long) floor(fractpart*NSEC_PER_SEC); while (t_abs.tv_nsec >= NSEC_PER_SEC) { @@ -435,7 +460,7 @@ double MDD_realtimeSynchronize(void* rtSyncObj, double simTime, double * availab /* printf("t_abs.tv_sec: %d, t_cr.tv_sec: %d, t_abs.tv_nsec: %d, t_cr.tv_nsec: %d\n", t_abs.tv_sec, rtSync->t_clockRealtime.tv_sec, t_abs.tv_nsec, rtSync->t_clockRealtime.tv_nsec); */ - /* wait until simulation time == real-time */ + /* wait until (scaled) simulation time == real-time */ ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &t_abs, NULL); if (ret) { ModelicaError("MDDRealtimeSynchronize.h: clock_nanosleep(..) failed\n"); @@ -443,6 +468,8 @@ double MDD_realtimeSynchronize(void* rtSyncObj, double simTime, double * availab /* get value the current time of the real-time clock (should be equal to t_abs if everything is OK) */ clock_gettime(CLOCK_MONOTONIC, &rtSync->t_clockRealtime); + rtSync->lastSimTime = simTime; + rtSync->lastSyncTime = syncTime; } return deltaTime;