OMCompiler/SimulationRuntime/c/simulation/solver/synchronous.c
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * This file belongs to the OpenModelica Run-Time System | ||
| 3 | * | ||
| 4 | * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC), c/o Linköpings | ||
| 5 | * universitet, Department of Computer and Information Science, SE-58183 Linköping, Sweden. All rights | ||
| 6 | * reserved. | ||
| 7 | * | ||
| 8 | * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF THE BSD NEW LICENSE OR THE | ||
| 9 | * AGPL VERSION 3 LICENSE OR THE OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8. ANY | ||
| 10 | * USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES RECIPIENT'S | ||
| 11 | * ACCEPTANCE OF THE BSD NEW LICENSE OR THE OSMC PUBLIC LICENSE OR THE AGPL | ||
| 12 | * VERSION 3, ACCORDING TO RECIPIENTS CHOICE. | ||
| 13 | * | ||
| 14 | * The OpenModelica software and the OSMC (Open Source Modelica Consortium) Public License | ||
| 15 | * (OSMC-PL) are obtained from OSMC, either from the above address, from the URLs: | ||
| 16 | * http://www.openmodelica.org or https://github.com/OpenModelica/ or | ||
| 17 | * http://www.ida.liu.se/projects/OpenModelica, and in the OpenModelica distribution. GNU | ||
| 18 | * AGPL version 3 is obtained from: https://www.gnu.org/licenses/licenses.html#GPL. The BSD NEW | ||
| 19 | * License is obtained from: http://www.opensource.org/licenses/BSD-3-Clause. | ||
| 20 | * | ||
| 21 | * This program is distributed WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY | ||
| 23 | * SET FORTH IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF | ||
| 24 | * OSMC-PL. | ||
| 25 | * | ||
| 26 | */ | ||
| 27 | |||
| 28 | #include "synchronous.h" | ||
| 29 | #include "epsilon.h" | ||
| 30 | #include "../results/simulation_result.h" | ||
| 31 | |||
| 32 | #ifdef __cplusplus | ||
| 33 | extern "C" { | ||
| 34 | #endif | ||
| 35 | |||
| 36 | /* Function prototypes */ | ||
| 37 | void printClocks(BASECLOCK_DATA* baseClocks, int nBaseCllocks); | ||
| 38 | void printSyncTimer(void* data, int stream, void* elemPointer); | ||
| 39 | |||
| 40 | /** | ||
| 41 | * @brief Initialize memory for synchronous functionalities. | ||
| 42 | * | ||
| 43 | * @param data Pointer to data. | ||
| 44 | * @param threadData Pointer to thread data. | ||
| 45 | * @param startTime Start time of simulation. | ||
| 46 | */ | ||
| 47 | 1 | void initSynchronous(DATA* data, threadData_t *threadData, modelica_real startTime) | |
| 48 | { | ||
| 49 | int i,j; | ||
| 50 | BASECLOCK_DATA* baseClock; | ||
| 51 | |||
| 52 | /* Initialize clocks */ | ||
| 53 | 1 | data->callback->function_initSynchronous(data, threadData); | |
| 54 | |||
| 55 | /* Error check */ | ||
| 56 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
|
1 | for(i=0; i<data->modelData->nBaseClocks; i++) { |
| 57 | ✗ | for(j=0; j<data->simulationInfo->baseClocks[i].nSubClocks; j++) { | |
| 58 | ✗ | assertStreamPrint(threadData, data->simulationInfo->baseClocks[i].subClocks != NULL, "Initialization of synchronous systems failed: baseclocks[%i]->subClocks is NULL!", i); | |
| 59 | ✗ | assertStreamPrint(threadData, data->simulationInfo->baseClocks[i].subClocks[j].solverMethod != NULL, "Continuous clocked systems aren't supported yet."); | |
| 60 | ✗ | assertStreamPrint(threadData, floorRat(data->simulationInfo->baseClocks[i].subClocks[j].shift) >= 0, "Shift of sub-clock is negative. Sub-clocks aren't allowed to fire before base-clock."); | |
| 61 | } | ||
| 62 | ✗ | if (data->simulationInfo->baseClocks[i].isEventClock) { /*event clock*/ | |
| 63 | ✗ | for(j=0; j<data->simulationInfo->baseClocks[i].nSubClocks; j++) { | |
| 64 | ✗ | assertStreamPrint(threadData, data->simulationInfo->baseClocks[i].subClocks[j].factor.den == 1, "Factor of sub-clock of event-clock is not an integer, this is not allowed."); | |
| 65 | } | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
|
1 | for(i=0; i<data->modelData->nBaseClocks; i++) |
| 70 | { | ||
| 71 | ✗ | baseClock = &data->simulationInfo->baseClocks[i]; | |
| 72 | |||
| 73 | ✗ | data->callback->function_updateSynchronous(data, threadData, i); | |
| 74 | ✗ | if (!baseClock->isEventClock) { | |
| 75 | // Add base-clock activation time to data->simulationInfo->intvlTimers | ||
| 76 | ✗ | SYNC_TIMER timer = (SYNC_TIMER){ | |
| 77 | .base_idx = i, | ||
| 78 | .sub_idx = -1, | ||
| 79 | .type = SYNC_BASE_CLOCK, | ||
| 80 | .activationTime = startTime | ||
| 81 | }; | ||
| 82 | ✗ | listPushFront(data->simulationInfo->intvlTimers, &timer); | |
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | /* Debug print */ | ||
| 87 | 1 | printClocks(data->simulationInfo->baseClocks, data->modelData->nBaseClocks); | |
| 88 | 1 | } | |
| 89 | |||
| 90 | /** | ||
| 91 | * @brief Insert given timer into ordered list of timers. | ||
| 92 | * | ||
| 93 | * Timer with lowest activation time is at the start of the list, last at the end. | ||
| 94 | * | ||
| 95 | * @param list List with timers | ||
| 96 | * @param timer Timer to insert into list. | ||
| 97 | */ | ||
| 98 | ✗ | static void insertTimer(LIST* list, SYNC_TIMER* timer) | |
| 99 | { | ||
| 100 | LIST_NODE *it, *prevNode = NULL; | ||
| 101 | ✗ | for(it = listFirstNode(list); it; it = listNextNode(it)) | |
| 102 | { | ||
| 103 | ✗ | SYNC_TIMER *tmpTimer = listNodeData(it); | |
| 104 | ✗ | if(tmpTimer->activationTime > timer->activationTime) | |
| 105 | break; | ||
| 106 | prevNode = it; | ||
| 107 | } | ||
| 108 | ✗ | if (prevNode) listInsert(list, prevNode, timer); | |
| 109 | ✗ | else listPushFront(list, timer); | |
| 110 | ✗ | } | |
| 111 | |||
| 112 | |||
| 113 | /** | ||
| 114 | * @brief Check when next clock needs to fire. | ||
| 115 | * | ||
| 116 | * If next activation time is smaller then time on next step reduce step size | ||
| 117 | * to hit activation time of clock exactly. | ||
| 118 | * | ||
| 119 | * @param data Pointer to data. | ||
| 120 | * @param solverInfo Solver info, containing next activation time of clocks. | ||
| 121 | */ | ||
| 122 | 1 | void checkForSynchronous(DATA *data, SOLVER_INFO* solverInfo) | |
| 123 | { | ||
| 124 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
1 | if (data->simulationInfo->intvlTimers != NULL && listLen(data->simulationInfo->intvlTimers) > 0) |
| 125 | { | ||
| 126 | ✗ | SYNC_TIMER* nextTimer = (SYNC_TIMER*)listNodeData(listFirstNode(data->simulationInfo->intvlTimers)); | |
| 127 | ✗ | double nextTimeStep = solverInfo->currentTime + solverInfo->currentStepSize; | |
| 128 | |||
| 129 | ✗ | if ((nextTimer->activationTime <= nextTimeStep + SYNC_EPS) && (nextTimer->activationTime >= solverInfo->currentTime)) | |
| 130 | { | ||
| 131 | ✗ | solverInfo->currentStepSize = nextTimer->activationTime - solverInfo->currentTime; | |
| 132 | } | ||
| 133 | } | ||
| 134 | 1 | } | |
| 135 | |||
| 136 | |||
| 137 | /** | ||
| 138 | * @brief Update base clock and get activation times for all sub-clocks. | ||
| 139 | * | ||
| 140 | * @param data Pointer to data. | ||
| 141 | * @param threadData Pointer to thread data. | ||
| 142 | * @param idx Index of timer to handle. | ||
| 143 | * @param curTime Current activation time. | ||
| 144 | */ | ||
| 145 | ✗ | modelica_boolean handleBaseClock(DATA* data, threadData_t *threadData, long idx, double curTime) | |
| 146 | { | ||
| 147 | modelica_boolean frstSubClockIsBaseClock = 0 /* false */; | ||
| 148 | |||
| 149 | /* Special case for event-clocks activated at initialization */ | ||
| 150 | ✗ | if (data->simulationInfo->initial) { | |
| 151 | ✗ | SYNC_TIMER nextTimer = (SYNC_TIMER){ | |
| 152 | .base_idx = idx, | ||
| 153 | .sub_idx = -1, | ||
| 154 | .type = SYNC_BASE_CLOCK, | ||
| 155 | ✗ | .activationTime = data->simulationInfo->startTime}; | |
| 156 | ✗ | insertTimer(data->simulationInfo->intvlTimers, &nextTimer); | |
| 157 | return frstSubClockIsBaseClock; | ||
| 158 | } | ||
| 159 | |||
| 160 | ✗ | BASECLOCK_DATA* baseClock = &(data->simulationInfo->baseClocks[idx]); | |
| 161 | SUBCLOCK_DATA* subClock; | ||
| 162 | SYNC_TIMER nextTimer, firstSubTimer; | ||
| 163 | SYNC_TIMER* nextSubTimer; | ||
| 164 | double nextBaseTime, nextSubTime, absoluteSubTime, activationTime; | ||
| 165 | RATIONAL subTimer; | ||
| 166 | int i; | ||
| 167 | |||
| 168 | /* Update base clock */ | ||
| 169 | ✗ | baseClock->stats.count++; | |
| 170 | // Event clocks can't use baseClock->interval | ||
| 171 | ✗ | if (baseClock->isEventClock) { | |
| 172 | ✗ | if (baseClock->stats.count > 1) { | |
| 173 | ✗ | baseClock->stats.previousInterval = curTime - baseClock->stats.lastActivationTime; | |
| 174 | } | ||
| 175 | } else { | ||
| 176 | ✗ | baseClock->stats.previousInterval = baseClock->interval; | |
| 177 | } | ||
| 178 | ✗ | baseClock->stats.lastActivationTime = curTime; | |
| 179 | |||
| 180 | ✗ | subClock = &baseClock->subClocks[0]; | |
| 181 | ✗ | if (subClock->shift.num == 0 && subClock->factor.num == 1 && subClock->factor.den == 1) { | |
| 182 | frstSubClockIsBaseClock = 1 /* true */; | ||
| 183 | #if !defined(OMC_MINIMAL_RUNTIME) | ||
| 184 | // Save result before clock tick, then evaluate equations | ||
| 185 | ✗ | sim_result.emit(&sim_result, data, threadData); | |
| 186 | #endif /* #if !defined(OMC_MINIMAL_RUNTIME) */ | ||
| 187 | ✗ | subClock->stats.count++; | |
| 188 | ✗ | subClock->stats.previousInterval = baseClock->stats.previousInterval; | |
| 189 | ✗ | subClock->stats.lastActivationTime = baseClock->stats.lastActivationTime; | |
| 190 | ✗ | data->callback->function_equationsSynchronous(data, threadData, idx, 0); | |
| 191 | } | ||
| 192 | ✗ | if (!baseClock->isEventClock) { | |
| 193 | ✗ | data->callback->function_updateSynchronous(data, threadData, idx); /* Update interval */ | |
| 194 | ✗ | nextBaseTime = curTime + baseClock->interval; | |
| 195 | |||
| 196 | // Next base clock activation | ||
| 197 | ✗ | nextTimer = (SYNC_TIMER){ | |
| 198 | .base_idx = idx, | ||
| 199 | .sub_idx = -1, | ||
| 200 | .type = SYNC_BASE_CLOCK, | ||
| 201 | .activationTime = nextBaseTime}; | ||
| 202 | ✗ | insertTimer(data->simulationInfo->intvlTimers, &nextTimer); | |
| 203 | ✗ | infoStreamPrint(OMC_LOG_SYNCHRONOUS, 0, "Activated base-clock %li at time %f", idx, curTime); | |
| 204 | } else { | ||
| 205 | ✗ | infoStreamPrint(OMC_LOG_SYNCHRONOUS, 0, "Activated event-clock %li at time %f", idx, curTime); | |
| 206 | } | ||
| 207 | |||
| 208 | // Add sub-clocks to timer that will fire during this base-clock interval. | ||
| 209 | // s = subClock->shift + subClock->stats.count * subClock->factor - (baseClock->stats.count-1); | ||
| 210 | // timer = base.prevTick + s * baseClock->interval | ||
| 211 | |||
| 212 | // Skip first subClock if is equivalent to the baseClock | ||
| 213 | ✗ | i = frstSubClockIsBaseClock ? 1 : 0; | |
| 214 | ✗ | for (/* init above */; i < baseClock->nSubClocks; ++i) { | |
| 215 | ✗ | subClock = &baseClock->subClocks[i]; | |
| 216 | ✗ | subTimer = addRat(subRat(subClock->shift, int2Rat(baseClock->stats.count-1)), mulRat(int2Rat(subClock->stats.count), subClock->factor)); | |
| 217 | ✗ | while (floorRat(subTimer) == 0) { | |
| 218 | ✗ | activationTime = curTime + rat2Real(subTimer)*baseClock->interval; | |
| 219 | ✗ | nextTimer = (SYNC_TIMER){ | |
| 220 | .base_idx = idx, | ||
| 221 | .sub_idx = i, | ||
| 222 | .type = SYNC_SUB_CLOCK, | ||
| 223 | .activationTime = activationTime}; | ||
| 224 | ✗ | insertTimer(data->simulationInfo->intvlTimers, &nextTimer); | |
| 225 | ✗ | subTimer = addRat(subTimer, subClock->factor); | |
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | return frstSubClockIsBaseClock; | ||
| 230 | } | ||
| 231 | |||
| 232 | #if !defined(OMC_MINIMAL_RUNTIME) | ||
| 233 | /** | ||
| 234 | * @brief Handle timer clocks. | ||
| 235 | * | ||
| 236 | * Loop over all timers and check if a timer fired. | ||
| 237 | * If there are no timers return NO_TIMER_FIRED. | ||
| 238 | * | ||
| 239 | * @param data Pointer to data. | ||
| 240 | * @param threadData Pointer to thread data. | ||
| 241 | * @param solverInfo Pointer to solver info. | ||
| 242 | * @return fire_timer_t Return NO_TIMER_FIRED, if there are no fired timers; | ||
| 243 | * TIMER_FIRED, if there is a fired timer; | ||
| 244 | * TIMER_FIRED_EVENT, if there is a fired timer which triggers an event. | ||
| 245 | */ | ||
| 246 | 2 | fire_timer_t handleTimers(DATA* data, threadData_t *threadData, SOLVER_INFO* solverInfo) | |
| 247 | { | ||
| 248 | int base_idx, sub_idx; | ||
| 249 | double activationTime; | ||
| 250 | modelica_boolean frstSubClockIsBaseClock = 0 /* false */; | ||
| 251 | SYNC_TIMER_TYPE type; | ||
| 252 | SYNC_TIMER* nextTimer; | ||
| 253 | fire_timer_t ret = NO_TIMER_FIRED; | ||
| 254 | SUBCLOCK_DATA* subClock; | ||
| 255 | |||
| 256 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
2 | if (data->simulationInfo->intvlTimers == NULL || listLen(data->simulationInfo->intvlTimers) <= 0) { |
| 257 | 2 | return ret; | |
| 258 | } | ||
| 259 | |||
| 260 | /* Fire all timers at current time step */ | ||
| 261 | ✗ | nextTimer = (SYNC_TIMER*)listNodeData(listFirstNode(data->simulationInfo->intvlTimers)); | |
| 262 | ✗ | while(nextTimer->activationTime <= solverInfo->currentTime + SYNC_EPS) | |
| 263 | { | ||
| 264 | ✗ | base_idx = nextTimer->base_idx; | |
| 265 | ✗ | sub_idx = nextTimer->sub_idx; | |
| 266 | ✗ | type = nextTimer->type; | |
| 267 | activationTime = nextTimer->activationTime; | ||
| 268 | ✗ | listRemoveFront(data->simulationInfo->intvlTimers); | |
| 269 | ✗ | switch(type) | |
| 270 | { | ||
| 271 | ✗ | case SYNC_BASE_CLOCK: | |
| 272 | ✗ | frstSubClockIsBaseClock = handleBaseClock(data, threadData, base_idx, activationTime); | |
| 273 | ✗ | if (frstSubClockIsBaseClock && data->simulationInfo->baseClocks[base_idx].subClocks[0].holdEvents) { | |
| 274 | ret = TIMER_FIRED_EVENT; | ||
| 275 | } else { | ||
| 276 | ret = TIMER_FIRED; | ||
| 277 | } | ||
| 278 | break; | ||
| 279 | ✗ | case SYNC_SUB_CLOCK: | |
| 280 | // Save result before clock tick, then evaluate equations | ||
| 281 | ✗ | sim_result.emit(&sim_result, data, threadData); | |
| 282 | ✗ | subClock = &data->simulationInfo->baseClocks[base_idx].subClocks[sub_idx]; | |
| 283 | ✗ | subClock->stats.count++; | |
| 284 | ✗ | subClock->stats.previousInterval = solverInfo->currentTime - subClock->stats.lastActivationTime; | |
| 285 | ✗ | subClock->stats.lastActivationTime = solverInfo->currentTime; | |
| 286 | ✗ | data->callback->function_equationsSynchronous(data, threadData, base_idx, sub_idx); /* TODO: Fix indices. Now indices for base and sub-clocks */ | |
| 287 | ✗ | if (subClock->holdEvents) { | |
| 288 | ret = TIMER_FIRED_EVENT; | ||
| 289 | ✗ | infoStreamPrint(OMC_LOG_SYNCHRONOUS, 0, "Activated sub-clock (%i,%i) which triggered event at time %f", | |
| 290 | base_idx, sub_idx, solverInfo->currentTime); | ||
| 291 | } else { | ||
| 292 | ret = TIMER_FIRED; | ||
| 293 | ✗ | infoStreamPrint(OMC_LOG_SYNCHRONOUS, 0, "Activated sub-clock (%i,%i) at time %f", | |
| 294 | base_idx, sub_idx, solverInfo->currentTime); | ||
| 295 | } | ||
| 296 | break; | ||
| 297 | } | ||
| 298 | ✗ | if (listLen(data->simulationInfo->intvlTimers) == 0){ | |
| 299 | break; | ||
| 300 | } | ||
| 301 | ✗ | nextTimer = (SYNC_TIMER*)listNodeData(listFirstNode(data->simulationInfo->intvlTimers)); | |
| 302 | } | ||
| 303 | return ret; | ||
| 304 | } | ||
| 305 | #endif /* #if !defined(OMC_MINIMAL_RUNTIME) */ | ||
| 306 | |||
| 307 | |||
| 308 | /** | ||
| 309 | * @brief Handle timer clocks and return next time a timer will fire | ||
| 310 | * | ||
| 311 | * Update timers and output when the next timer will fire. | ||
| 312 | * Used for Synchronous features in FMUs. | ||
| 313 | * | ||
| 314 | * @param data data | ||
| 315 | * @param threadData thread data, for errro handling | ||
| 316 | * @param currentTime Current solver timer. | ||
| 317 | * @param nextTimerDefined FALSE if no next timer is defined. | ||
| 318 | * TRUE if a next timer is defined. Then the time is outputted in nextTimerActivationTime. | ||
| 319 | * @param nextTimerActivationTime If nextTimerDefined is true it will contain the next time a timer will fire. | ||
| 320 | * @return int Return 0, if there is no fired timers; | ||
| 321 | * 1, if there is a fired timer; | ||
| 322 | * 2, if there is a fired timer which trigger event; | ||
| 323 | */ | ||
| 324 | ✗ | int handleTimersFMI(DATA* data, threadData_t *threadData, double currentTime, modelica_boolean *nextTimerDefined, double *nextTimerActivationTime) | |
| 325 | { | ||
| 326 | int base_idx, sub_idx; | ||
| 327 | double activationTime; | ||
| 328 | modelica_boolean frstSubClockIsBaseClock = 0 /* false */; | ||
| 329 | SYNC_TIMER_TYPE type; | ||
| 330 | SYNC_TIMER* nextTimer; | ||
| 331 | fire_timer_t ret = NO_TIMER_FIRED; | ||
| 332 | SUBCLOCK_DATA* subClock; | ||
| 333 | |||
| 334 | ✗ | *nextTimerDefined = FALSE; | |
| 335 | |||
| 336 | ✗ | if (data->simulationInfo->intvlTimers == NULL || listLen(data->simulationInfo->intvlTimers) <= 0) { | |
| 337 | ✗ | return (int) ret; | |
| 338 | } | ||
| 339 | |||
| 340 | /* Fire all timers at current time step */ | ||
| 341 | ✗ | nextTimer = (SYNC_TIMER*)listNodeData(listFirstNode(data->simulationInfo->intvlTimers)); | |
| 342 | ✗ | while(nextTimer->activationTime <= currentTime + SYNC_EPS) | |
| 343 | { | ||
| 344 | ✗ | base_idx = nextTimer->base_idx; | |
| 345 | ✗ | sub_idx = nextTimer->sub_idx; | |
| 346 | ✗ | type = nextTimer->type; | |
| 347 | activationTime = nextTimer->activationTime; | ||
| 348 | ✗ | listRemoveFront(data->simulationInfo->intvlTimers); | |
| 349 | ✗ | switch(type) | |
| 350 | { | ||
| 351 | ✗ | case SYNC_BASE_CLOCK: | |
| 352 | ✗ | frstSubClockIsBaseClock = handleBaseClock(data, threadData, base_idx, activationTime); | |
| 353 | ✗ | if (frstSubClockIsBaseClock && data->simulationInfo->baseClocks[base_idx].subClocks[0].holdEvents) { | |
| 354 | ret = TIMER_FIRED_EVENT; | ||
| 355 | } else { | ||
| 356 | ret = TIMER_FIRED; | ||
| 357 | } | ||
| 358 | break; | ||
| 359 | ✗ | case SYNC_SUB_CLOCK: | |
| 360 | ✗ | subClock = &data->simulationInfo->baseClocks[base_idx].subClocks[sub_idx]; | |
| 361 | ✗ | subClock->stats.count++; | |
| 362 | ✗ | subClock->stats.previousInterval = currentTime - subClock->stats.lastActivationTime; | |
| 363 | ✗ | subClock->stats.lastActivationTime = currentTime; | |
| 364 | ✗ | data->callback->function_equationsSynchronous(data, threadData, base_idx, sub_idx); /* TODO: Fix indices. Now indices for base and sub-clocks */ | |
| 365 | ✗ | if (subClock->holdEvents) { | |
| 366 | ret = TIMER_FIRED_EVENT; | ||
| 367 | ✗ | infoStreamPrint(OMC_LOG_SYNCHRONOUS, 0, "Activated sub-clock (%i,%i) which triggered event at time %f", | |
| 368 | base_idx, sub_idx, currentTime); | ||
| 369 | } else { | ||
| 370 | ret = TIMER_FIRED; | ||
| 371 | ✗ | infoStreamPrint(OMC_LOG_SYNCHRONOUS, 0, "Activated sub-clock (%i,%i) at time %f", | |
| 372 | base_idx, sub_idx, currentTime); | ||
| 373 | } | ||
| 374 | break; | ||
| 375 | } | ||
| 376 | ✗ | if (listLen(data->simulationInfo->intvlTimers) == 0){ | |
| 377 | break; | ||
| 378 | } | ||
| 379 | ✗ | nextTimer = (SYNC_TIMER*)listNodeData(listFirstNode(data->simulationInfo->intvlTimers)); | |
| 380 | } | ||
| 381 | /* Next time a timer will activate, whether or not one fired now. */ | ||
| 382 | ✗ | if (listLen(data->simulationInfo->intvlTimers) > 0) { | |
| 383 | ✗ | nextTimer = (SYNC_TIMER*)listNodeData(listFirstNode(data->simulationInfo->intvlTimers)); | |
| 384 | ✗ | *nextTimerActivationTime = nextTimer->activationTime; | |
| 385 | ✗ | *nextTimerDefined = TRUE; | |
| 386 | } | ||
| 387 | ✗ | return (int) ret; | |
| 388 | } | ||
| 389 | |||
| 390 | /** | ||
| 391 | * @brief Print all base-clocks and sub-clocks. | ||
| 392 | * | ||
| 393 | * @param baseClocks Pointer to array of size nClocks with base clock data. | ||
| 394 | * @param nBaseClocks Number of base clocks. | ||
| 395 | */ | ||
| 396 | 1 | void printClocks(BASECLOCK_DATA* baseClocks, int nBaseClocks) | |
| 397 | { | ||
| 398 | int i,j; | ||
| 399 | BASECLOCK_DATA* baseClock; | ||
| 400 | SUBCLOCK_DATA* subClock; | ||
| 401 | |||
| 402 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
|
1 | if(omc_useStream[OMC_LOG_SYNCHRONOUS]) { |
| 403 | ✗ | infoStreamPrint(OMC_LOG_SYNCHRONOUS, 1, "Initialized synchronous timers."); | |
| 404 | ✗ | infoStreamPrint(OMC_LOG_SYNCHRONOUS, 0, "Number of base clocks: %i", nBaseClocks); | |
| 405 | ✗ | for(i=0; i<nBaseClocks; i++) { | |
| 406 | ✗ | baseClock = &baseClocks[i]; | |
| 407 | ✗ | infoStreamPrint(OMC_LOG_SYNCHRONOUS, 1, "Base clock %i", i+1); | |
| 408 | ✗ | if (baseClock->isEventClock) { | |
| 409 | ✗ | infoStreamPrint(OMC_LOG_SYNCHRONOUS, 0, "is event clock"); | |
| 410 | ✗ | } else if (baseClock->intervalCounter==-1) { | |
| 411 | ✗ | infoStreamPrint(OMC_LOG_SYNCHRONOUS, 0, "interval: %e", baseClock->interval); | |
| 412 | } else { | ||
| 413 | ✗ | infoStreamPrint(OMC_LOG_SYNCHRONOUS, 0, "intervalCounter/resolution = : %i/%i", baseClock->intervalCounter, baseClock->resolution); | |
| 414 | ✗ | infoStreamPrint(OMC_LOG_SYNCHRONOUS, 0, "interval: %e", baseClock->interval); | |
| 415 | } | ||
| 416 | ✗ | infoStreamPrint(OMC_LOG_SYNCHRONOUS, 0, "Number of sub-clocks: %i", baseClock->nSubClocks); | |
| 417 | ✗ | for(j=0; j<baseClock->nSubClocks; j++) { | |
| 418 | ✗ | subClock = &baseClock->subClocks[j]; | |
| 419 | ✗ | infoStreamPrint(OMC_LOG_SYNCHRONOUS, 1, "Sub-clock %i of base clock %i", j+1, i+1); | |
| 420 | ✗ | infoStreamPrint(OMC_LOG_SYNCHRONOUS, 0, "shift: "RAT_FMT"/"RAT_FMT, subClock->shift.num, subClock->shift.den); | |
| 421 | ✗ | infoStreamPrint(OMC_LOG_SYNCHRONOUS, 0, "factor: "RAT_FMT"/"RAT_FMT, subClock->factor.num, subClock->factor.den); | |
| 422 | ✗ | infoStreamPrint(OMC_LOG_SYNCHRONOUS, 0, "solverMethod: %s", strlen(subClock->solverMethod)>0?subClock->solverMethod:"none"); | |
| 423 | ✗ | infoStreamPrint(OMC_LOG_SYNCHRONOUS, 0, "holdEvents: %s", subClock->holdEvents?"true":"false"); | |
| 424 | ✗ | messageClose(OMC_LOG_SYNCHRONOUS); | |
| 425 | } | ||
| 426 | ✗ | messageClose(OMC_LOG_SYNCHRONOUS); | |
| 427 | } | ||
| 428 | ✗ | messageClose(OMC_LOG_SYNCHRONOUS); | |
| 429 | } | ||
| 430 | 1 | } | |
| 431 | |||
| 432 | /** | ||
| 433 | * @brief Print synchronous timer. | ||
| 434 | * | ||
| 435 | * Prints tuple (base_idx, sub_idx, type, activationTime). | ||
| 436 | * | ||
| 437 | * @param data Void pointer to sync timer element. | ||
| 438 | * Will be casted to SYNC_TIMER*. | ||
| 439 | * @param stream Stream of OMC_LOG_STREAM type. | ||
| 440 | * @param elemPointer Address of element storing this data. | ||
| 441 | */ | ||
| 442 | ✗ | void printSyncTimer(void* data, int stream, void* elemPointer) | |
| 443 | { | ||
| 444 | SYNC_TIMER* syncTimerElem = (SYNC_TIMER*) data; | ||
| 445 | ✗ | switch (syncTimerElem->type) | |
| 446 | { | ||
| 447 | ✗ | case SYNC_BASE_CLOCK: | |
| 448 | ✗ | infoStreamPrint(stream, 0, "%p: (base_idx :%i, type: %s, activationTime: %e)", elemPointer, syncTimerElem->base_idx, "base-clock", syncTimerElem->activationTime); | |
| 449 | ✗ | break; | |
| 450 | ✗ | case SYNC_SUB_CLOCK: | |
| 451 | ✗ | infoStreamPrint(stream, 0, "%p: (base_idx: %i, sub_idx: %i, type: %s, activationTime: %e)", elemPointer, syncTimerElem->base_idx, syncTimerElem->sub_idx, "sub-clock", syncTimerElem->activationTime); | |
| 452 | ✗ | break; | |
| 453 | |||
| 454 | ✗ | default: | |
| 455 | ✗ | infoStreamPrint(stream, 0, "%p: ERROR: Unknown type", elemPointer); | |
| 456 | ✗ | break; | |
| 457 | } | ||
| 458 | ✗ | } | |
| 459 | |||
| 460 | #ifdef __cplusplus | ||
| 461 | } | ||
| 462 | #endif | ||
| 463 |