From 834a38bc5f746cb922642017eef145852f7dc160 Mon Sep 17 00:00:00 2001 From: Rahul P <26484864+rahulp13@users.noreply.github.com> Date: Mon, 11 Apr 2022 18:13:12 +0530 Subject: [PATCH] Revert #8358 (#8646) * Revert "revert the last OPC UA commit as it breaks the Windows builds" (#8358) * Fixed MinGW 'omc_terminate' symbol reference issue --- .../c/simulation/solver/embedded_server.c | 4 +- .../c/simulation/solver/embedded_server.h | 2 +- .../solver/perform_simulation.c.inc | 16 ++++- .../SimulationRuntime/opc/ua/omc_opc_ua.c | 67 ++++++++++++++++--- .../SimulationRuntime/opc/ua/omc_opc_ua.h | 3 +- 5 files changed, 77 insertions(+), 15 deletions(-) diff --git a/OMCompiler/SimulationRuntime/c/simulation/solver/embedded_server.c b/OMCompiler/SimulationRuntime/c/simulation/solver/embedded_server.c index 1ff4f00a95b..d923215fe5d 100644 --- a/OMCompiler/SimulationRuntime/c/simulation/solver/embedded_server.c +++ b/OMCompiler/SimulationRuntime/c/simulation/solver/embedded_server.c @@ -55,7 +55,7 @@ void no_embedded_server_deinit(void *handle) { } -int no_embedded_server_update(void *handle, double tout) +int no_embedded_server_update(void *handle, double tout, int *terminate) { return 0; } @@ -65,7 +65,7 @@ void (*wait_for_step)(void*) = no_wait_for_step; void (*embedded_server_deinit)(void*) = no_embedded_server_deinit; // Tells the embedded server that a simulation step has passed; the server // can read/write values from/to the simulator -int (*embedded_server_update)(void*, double tout) = no_embedded_server_update; +int (*embedded_server_update)(void*, double tout, int*) = no_embedded_server_update; void* embedded_server_load_functions(const char *server_name) { diff --git a/OMCompiler/SimulationRuntime/c/simulation/solver/embedded_server.h b/OMCompiler/SimulationRuntime/c/simulation/solver/embedded_server.h index 4056b32c163..cc40a08bd6c 100644 --- a/OMCompiler/SimulationRuntime/c/simulation/solver/embedded_server.h +++ b/OMCompiler/SimulationRuntime/c/simulation/solver/embedded_server.h @@ -43,7 +43,7 @@ extern void (*embedded_server_deinit)(void *handle); /* Tells the embedded server that a simulation step has passed; the server * can read/write values from/to the simulator */ -extern int (*embedded_server_update)(void *handle, double tout); +extern int (*embedded_server_update)(void *handle, double tout, int *terminate); /* Give the filename or generic name to use for loading an embedded server */ extern void* embedded_server_load_functions(const char *name); extern void embedded_server_unload_functions(void *dllHandle); diff --git a/OMCompiler/SimulationRuntime/c/simulation/solver/perform_simulation.c.inc b/OMCompiler/SimulationRuntime/c/simulation/solver/perform_simulation.c.inc index 19cb16ec2fe..6e253eaac3f 100644 --- a/OMCompiler/SimulationRuntime/c/simulation/solver/perform_simulation.c.inc +++ b/OMCompiler/SimulationRuntime/c/simulation/solver/perform_simulation.c.inc @@ -273,13 +273,20 @@ static void fmtEmitStep(DATA* data, threadData_t *threadData, MEASURE_TIME* mt, sim_result.emit(&sim_result, data, threadData); } #if !defined(OMC_MINIMAL_RUNTIME) - if (embedded_server_update(data->embeddedServerState, data->localData[0]->timeValue)) { + int terminate=0; + + if (embedded_server_update(data->embeddedServerState, data->localData[0]->timeValue, &terminate)) { solverInfo->didEventStep = 1; overwriteOldSimulationData(data); storePreValues(data); // Maybe?? storeOldValues(data); // Maybe?? sim_result.emit(&sim_result, data, threadData); } + + if (terminate) { + omc_terminate((FILE_INFO) omc_dummyFileInfo, "The embedded server received command to terminate."); + } + if (data->real_time_sync.enabled) { double time = data->localData[0]->timeValue; int64_t res = rt_ext_tp_sync_nanosec(&data->real_time_sync.clock, (uint64_t) (data->real_time_sync.scaling*(time-data->real_time_sync.time)*1e9)); @@ -317,8 +324,11 @@ static void checkSimulationTerminated(DATA* data, SOLVER_INFO* solverInfo) { if(terminationTerminate) { - printInfo(stdout, TermInfo); - fputc('\n', stdout); + if (TermInfo.filename != NULL && TermInfo.filename[0] != '\0') { + printInfo(stdout, TermInfo); + fputc('\n', stdout); + } + infoStreamPrint(LOG_STDOUT, 0, "Simulation call terminate() at time %f\nMessage : %s", data->localData[0]->timeValue, TermMsg); data->simulationInfo->stopTime = solverInfo->currentTime; } diff --git a/OMCompiler/SimulationRuntime/opc/ua/omc_opc_ua.c b/OMCompiler/SimulationRuntime/opc/ua/omc_opc_ua.c index 35b645bb59c..88b94137f48 100644 --- a/OMCompiler/SimulationRuntime/opc/ua/omc_opc_ua.c +++ b/OMCompiler/SimulationRuntime/opc/ua/omc_opc_ua.c @@ -46,6 +46,8 @@ typedef struct { UA_Boolean server_running; UA_Boolean run; UA_Boolean step; + UA_Boolean terminate; + UA_Boolean oldUseStopTime; pthread_mutex_t mutex_pause; pthread_cond_t cond_pause; double time[2]; @@ -126,6 +128,8 @@ readBoolean(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp, co val = state->run; } else if (nodeid.identifier.numeric == OMC_OPC_NODEID_ENABLE_STOP_TIME) { val = state->data->simulationInfo->useStopTime; + } else if (nodeid.identifier.numeric == OMC_OPC_NODEID_TERMINATE) { + val = state->terminate; } else if (nodeid.identifier.numeric >= VARKIND_BOOL*MAX_VARS_KIND && nodeid.identifier.numeric < (1+VARKIND_BOOL)*MAX_VARS_KIND) { int index1 = nodeid.identifier.numeric-VARKIND_BOOL*MAX_VARS_KIND; int index = index1 >= ALIAS_START_ID ? modelData->booleanAlias[index1-ALIAS_START_ID].nameID : index1; @@ -170,10 +174,27 @@ writeBoolean(void *handle, const UA_NodeId nodeid, const UA_Variant *data, const pthread_mutex_unlock(&state->mutex_pause); pthread_cond_signal(&state->cond_pause); } else if (nodeid.identifier.numeric==OMC_OPC_NODEID_ENABLE_STOP_TIME) { + if (!(state->terminate)) { /* prevent writes to useStopTime if terminate flag is set */ + pthread_mutex_lock(&state->mutex_pause); + state->data->simulationInfo->useStopTime = newVal; + pthread_mutex_unlock(&state->mutex_pause); + } else { + statusCode = UA_STATUSCODE_BADREQUESTNOTALLOWED; + } + } else if (nodeid.identifier.numeric==OMC_OPC_NODEID_TERMINATE) { pthread_mutex_lock(&state->mutex_pause); - state->data->simulationInfo->useStopTime = newVal; + + if (newVal) { + /* Store prev. useStopTime state in case terminate flag is reset in same step */ + state->oldUseStopTime = state->data->simulationInfo->useStopTime; + state->data->simulationInfo->useStopTime = newVal; /* enable stop time for termination */ + } + else if (state->terminate) /* Falling edge of this flag occurred in the same step, restore prev. val of useStopTime */ + state->data->simulationInfo->useStopTime = state->oldUseStopTime; + + state->terminate = newVal; + pthread_mutex_unlock(&state->mutex_pause); - pthread_cond_signal(&state->cond_pause); } else if (nodeid.identifier.numeric >= VARKIND_BOOL*MAX_VARS_KIND && nodeid.identifier.numeric < (1+VARKIND_BOOL)*MAX_VARS_KIND) { int index1 = nodeid.identifier.numeric-VARKIND_BOOL*MAX_VARS_KIND; int index = index1 >= ALIAS_START_ID ? modelData->booleanAlias[index1-ALIAS_START_ID].nameID : index1; @@ -504,10 +525,12 @@ void* omc_embedded_server_init(DATA *data, double t, double step, const char *ar state->run = 0; state->step = 0; + state->terminate = 0; + state->oldUseStopTime = data->simulationInfo->useStopTime; pthread_create(&state->thread, NULL, (void*) &threadWork, state); - /* add a variable node to the address space */ + /* add variable for a simulation step */ UA_NodeId stepNodeId = UA_NODEID_NUMERIC(0, OMC_OPC_NODEID_STEP); UA_QualifiedName stepName = UA_QUALIFIEDNAME(1, "OpenModelica.step"); UA_DataSource stepDataSource = (UA_DataSource) { @@ -523,7 +546,7 @@ void* omc_embedded_server_init(DATA *data, double t, double step, const char *ar UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), stepName, UA_NODEID_NULL, attr, stepDataSource, NULL); - /* Run variable */ + /* add variable for simulation run */ UA_NodeId runNodeId = UA_NODEID_NUMERIC(0, OMC_OPC_NODEID_RUN); UA_QualifiedName runName = UA_QUALIFIEDNAME(1, "OpenModelica.run"); UA_VariableAttributes runAttr; @@ -571,7 +594,7 @@ void* omc_embedded_server_init(DATA *data, double t, double step, const char *ar name, UA_NODEID_NULL, attr, dataSource, NULL); } - /* add a variable node to the address space */ + /* add variable for current simulation time */ UA_NodeId timeNodeId = UA_NODEID_NUMERIC(0, OMC_OPC_NODEID_TIME); UA_QualifiedName timeName = UA_QUALIFIEDNAME(1, "time"); UA_DataSource timeDataSource = (UA_DataSource) { @@ -585,6 +608,24 @@ void* omc_embedded_server_init(DATA *data, double t, double step, const char *ar UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), timeName, UA_NODEID_NULL, timeAttr, timeDataSource, NULL); + { + /* add variable for graceful termination of the simulation */ + UA_NodeId terminateNodeId = UA_NODEID_NUMERIC(0, OMC_OPC_NODEID_TERMINATE); + UA_QualifiedName terminateName = UA_QUALIFIEDNAME(1, "OpenModelica.terminate"); + UA_DataSource dataSource = (UA_DataSource) { + .handle = state, .read = readBoolean, .write = writeBoolean}; + UA_VariableAttributes attr; + UA_VariableAttributes_init(&attr); + attr.description = UA_LOCALIZEDTEXT("en_US", "When set to true, the simulation is terminated gracefully"); + attr.displayName = UA_LOCALIZEDTEXT("en_US", "terminate"); + attr.writeMask = 1; + attr.userWriteMask = 1; + UA_Server_addDataSourceVariableNode(state->server, terminateNodeId, + UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), + UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), + terminateName, UA_NODEID_NULL, attr, dataSource, NULL); + } + state->gotNewInput = 0; state->inputVarsBackup = malloc(modelData->nInputVars * sizeof(double)); memcpy(state->inputVarsBackup, data->simulationInfo->inputVars, modelData->nInputVars * sizeof(double)); @@ -640,7 +681,7 @@ void omc_embedded_server_deinit(void *state_vp) free(state); } -int omc_embedded_server_update(void *state_vp, double t) +int omc_embedded_server_update(void *state_vp, double t, int *terminate) { omc_opc_ua_state *state = (omc_opc_ua_state*) state_vp; int i, realIndex=0, boolIndex=0, res=0; @@ -648,8 +689,6 @@ int omc_embedded_server_update(void *state_vp, double t) MODEL_DATA *modelData = data->modelData; int latestValues; - waitForStep(state); - latestValues = state->latestValues ? 0 : 1; state->time[latestValues] = t; @@ -664,11 +703,17 @@ int omc_embedded_server_update(void *state_vp, double t) state->latestValues = latestValues; pthread_mutex_unlock(&state->mutex_values); + + waitForStep(state); + + pthread_mutex_lock(&state->write_values); if (state->gotNewInput) { res = 1; /* Trigger an event in the solver, restarting it */ memcpy(data->simulationInfo->inputVars, state->inputVarsBackup, modelData->nInputVars * sizeof(double)); + + state->gotNewInput = 0; } if (state->reinitStateFlag) { @@ -679,7 +724,13 @@ int omc_embedded_server_update(void *state_vp, double t) (data->localData[0])->realVars[i] = state->updatedStates[i]; } } + + state->reinitStateFlag = 0; } + + if (state->terminate) + *terminate = 1; + pthread_mutex_unlock(&state->write_values); return res; diff --git a/OMCompiler/SimulationRuntime/opc/ua/omc_opc_ua.h b/OMCompiler/SimulationRuntime/opc/ua/omc_opc_ua.h index 7df6f2c061f..031d38bc1aa 100644 --- a/OMCompiler/SimulationRuntime/opc/ua/omc_opc_ua.h +++ b/OMCompiler/SimulationRuntime/opc/ua/omc_opc_ua.h @@ -50,13 +50,14 @@ OPC_UA_EXPORT void* omc_embedded_server_init(DATA *data, double t, double step, const char *argv_0, void (*omc_real_time_sync_update)(DATA *data, double scaling), int port); OPC_UA_EXPORT void omc_wait_for_step(void*); OPC_UA_EXPORT void omc_embedded_server_deinit(void*); -OPC_UA_EXPORT int omc_embedded_server_update(void*, double t); +OPC_UA_EXPORT int omc_embedded_server_update(void*, double t, int*); #define OMC_OPC_NODEID_STEP 10000 #define OMC_OPC_NODEID_RUN 10001 #define OMC_OPC_NODEID_REAL_TIME_SCALING_FACTOR 10002 #define OMC_OPC_NODEID_ENABLE_STOP_TIME 10003 #define OMC_OPC_NODEID_TIME 10004 +#define OMC_OPC_NODEID_TERMINATE 10005 #define MAX_VARS_KIND 100000000 #define ALIAS_START_ID (MAX_VARS_KIND/2)