Skip to content

Commit

Permalink
Experimental support for OPC UA embedded server
Browse files Browse the repository at this point in the history
We support:

- OPC UA subscriptions for boolean/real variables
- Changing boolean/real variables (but not triggering an event)
- Boolean/Real aliases
- Not Real discrete variables and parameters
- Real-time synchronization support (via OPC or simflags: -rt 1.0)
- Having no stop-time (OPC only)
- Handling lowlatency kernel as root, allowing a tighter real-time
  frequency (best-case 100 Hz as regular user, 2 kHz as root without
  OPC communication or writing simulation results)

We no longer trigger time events at stopTime+1. This causes problems
when the stopTime is not used. Instead, we put a value of NAN for the
next sample event when there are no samples which effectively disables
time events.

There is a simple Python client for testing using a GUI. A testcase
for OPC is still missing.
  • Loading branch information
sjoelund authored and OpenModelica-Hudson committed Mar 22, 2016
1 parent 2d44ae9 commit 0424117
Show file tree
Hide file tree
Showing 35 changed files with 1,789 additions and 111 deletions.
2 changes: 1 addition & 1 deletion 3rdParty
5 changes: 4 additions & 1 deletion Makefile.common
Expand Up @@ -58,7 +58,7 @@ bootstrap-dependencies: graphstream $(BOOTSTRAP_FMIL_DEP) $(MINGW_EXTRA_LIBS)
$(MAKE) -f $(defaultMakefileTarget) -C Compiler builtin OMBUILDDIR=$(OMBUILDDIR)
$(MAKE) -f $(defaultMakefileTarget) -C Parser install_bootstrapping OMBUILDDIR=$(OMBUILDDIR)

sim-dependencies: fmi fmil opencl_rt lis metis fmi fmil
sim-dependencies: fmi fmil opencl_rt lis metis fmi fmil opc

install-openturns: mkbuilddirs
(time cp SimulationRuntime/OpenTurns/* $(builddir_share)/omc/scripts/OpenTurns/)
Expand All @@ -76,6 +76,9 @@ interactive-short: .testvariables interactive-common
interactive: .testvariables interactive-common fmil lis Cdaskr CMinpack docs $(IPOPT_TARGET) $(UMFPACK_TARGET) $(MINGW_EXTRA_LIBS)
$(MAKE) -C SimulationRuntime/c -f $(defaultMakefileTarget) OMBUILDDIR=$(OMBUILDDIR)

opc: .testvariables mkbuilddirs
$(MAKE) -C SimulationRuntime/opc/ua -f $(defaultMakefileTarget) OMBUILDDIR=$(OMBUILDDIR)

docs:
$(MAKE) -C Compiler/Translation OMBUILDDIR=$(OMBUILDDIR) release
(cp -p Examples/*.* $(builddir_doc)/omc/testmodels/)
Expand Down
1 change: 1 addition & 0 deletions SimulationRuntime/c/Makefile.common
Expand Up @@ -62,6 +62,7 @@ RUNTIMESIMSOLVER_HEADERS = ./simulation/solver/delay.h \
./simulation/solver/nonlinearSolverHomotopy.h \
./simulation/solver/nonlinearSolverHybrd.h \
./simulation/solver/stateset.h \
./simulation/solver/real_time_sync.h \
./simulation/solver/perform_simulation.c \
./simulation/solver/perform_qss_simulation.c \
./simulation/solver/dassl.h \
Expand Down
2 changes: 1 addition & 1 deletion SimulationRuntime/c/Makefile.objs
Expand Up @@ -38,7 +38,7 @@ MATH_HFILES = blaswrap.h

SOLVER_OBJS_FMU=delay$(OBJ_EXT) linearSystem$(OBJ_EXT) linearSolverLapack$(OBJ_EXT) linearSolverTotalPivot$(OBJ_EXT) mixedSystem$(OBJ_EXT) mixedSearchSolver$(OBJ_EXT) nonlinearSystem$(OBJ_EXT) nonlinearValuesList$(OBJ_EXT) nonlinearSolverHybrd$(OBJ_EXT) nonlinearSolverHomotopy$(OBJ_EXT) omc_math$(OBJ_EXT) model_help$(OBJ_EXT) stateset$(OBJ_EXT) synchronous$(OBJ_EXT)
ifeq ($(OMC_FMI_RUNTIME),)
SOLVER_OBJS_MINIMAL=$(SOLVER_OBJS_FMU) events$(OBJ_EXT) external_input$(OBJ_EXT) solver_main$(OBJ_EXT) embedded_server$(OBJ_EXT)
SOLVER_OBJS_MINIMAL=$(SOLVER_OBJS_FMU) events$(OBJ_EXT) external_input$(OBJ_EXT) solver_main$(OBJ_EXT) real_time_sync$(OBJ_EXT) embedded_server$(OBJ_EXT)

else
SOLVER_OBJS_MINIMAL=$(SOLVER_OBJS_FMU)
Expand Down
2 changes: 1 addition & 1 deletion SimulationRuntime/c/Makefile.omdev.mingw
Expand Up @@ -17,7 +17,7 @@ AR = ar -ru
CC = gcc
FC = g77
#include the sundials, ipopt from OMDev and the top_builddir for revision.h
CONFIG_CFLAGS = -O2 -g -falign-functions -msse2 -mfpmath=sse \
CONFIG_CFLAGS = -O2 -falign-functions -msse2 -mfpmath=sse \
-I$(OMDEV)/lib/3rdParty/Ipopt/include/ \
-I$(top_builddir)/3rdParty/sundials/build/include \
-I$(OMDEV)/include/lis -I$(top_builddir)/ -I$(builddir_inc)/ \
Expand Down
45 changes: 38 additions & 7 deletions SimulationRuntime/c/simulation/simulation_runtime.cpp
Expand Up @@ -212,7 +212,7 @@ void setGlobalVerboseLevel(int argc, char**argv)
delete flags;
}

int getNonlinearSolverMethod(int argc, char**argv)
static int getNonlinearSolverMethod()
{
int i;
const char *cflags = omc_flagValue[FLAG_NLS];
Expand All @@ -234,7 +234,7 @@ int getNonlinearSolverMethod(int argc, char**argv)
return NLS_NONE;
}

int getlinearSolverMethod(int argc, char**argv)
static int getlinearSolverMethod()
{
int i;
const char *cflags = omc_flagValue[FLAG_LS];
Expand All @@ -256,7 +256,7 @@ int getlinearSolverMethod(int argc, char**argv)
return LS_NONE;
}

int getNewtonStrategy(int argc, char**argv)
static int getNewtonStrategy()
{
int i;
const char *cflags = omc_flagValue[FLAG_NEWTON_STRATEGY];
Expand All @@ -278,6 +278,20 @@ int getNewtonStrategy(int argc, char**argv)
return NEWTON_NONE;
}

static double getFlagReal(enum _FLAG flag, double res)
{
const char *flagStr = omc_flagValue[flag];
char *endptr;
if (flagStr==NULL || *flagStr=='\0') {
return res;
}
res = strtod(flagStr, &endptr);
if (*endptr) {
throwStreamPrint(NULL, "Simulation flag %s expects a real number, got: %s", FLAG_NAME[flag], flagStr);
}
return res;
}

/**
* Read the variable filter and mark variables that should not be part of the result file.
* This phase is skipped for interactive simulations
Expand Down Expand Up @@ -608,6 +622,7 @@ static int callSolver(DATA* simData, threadData_t *threadData, string init_initM
TRACE_POP
return -1;
}
simData->real_time_sync.scaling = getFlagReal(FLAG_RT, 0.0);

if(std::string("") == simData->simulationInfo->solverMethod) {
#if defined(WITH_DASSL)
Expand Down Expand Up @@ -741,9 +756,9 @@ int initRuntimeAndSimulation(int argc, char**argv, DATA *data, threadData_t *thr
EXIT(1);
}

data->simulationInfo->nlsMethod = getNonlinearSolverMethod(argc, argv);
data->simulationInfo->lsMethod = getlinearSolverMethod(argc, argv);
data->simulationInfo->newtonStrategy = getNewtonStrategy(argc, argv);
data->simulationInfo->nlsMethod = getNonlinearSolverMethod();
data->simulationInfo->lsMethod = getlinearSolverMethod();
data->simulationInfo->newtonStrategy = getNewtonStrategy();
data->simulationInfo->nlsCsvInfomation = omc_flag[FLAG_NLS_INFO];

rt_tick(SIM_TIMER_INIT_XML);
Expand Down Expand Up @@ -866,8 +881,24 @@ int _main_SimulationRuntime(int argc, char**argv, DATA *data, threadData_t *thre
}
#endif

fprintf(stderr, "_main_SimulationRuntime done\n");
return retVal;
}

const char* prettyPrintNanoSec(int64_t ns, int *v)
{
if (ns > 100000000000L || ns < -100000000000L) {
*v = ns / 1000000000L;
return "s";
} if (ns > 100000000L || ns < -100000000L) {
*v = ns / 1000000L;
return "ms";
} else if (ns > 100000L || ns < -100000L) {
*v = ns / 1000L;
return "µs";
} else {
*v = ns;
return "ns";
}
}

} // extern "C"
3 changes: 3 additions & 0 deletions SimulationRuntime/c/simulation/simulation_runtime.h
Expand Up @@ -82,6 +82,9 @@ extern void communicateMsg(char id, unsigned int size, const char *data);
*/
extern int _main_SimulationRuntime(int argc, char**argv, DATA *data, threadData_t *threadData);


const char* prettyPrintNanoSec(int64_t ns, int *v);

#ifdef __cplusplus
}
#endif
Expand Down
78 changes: 52 additions & 26 deletions SimulationRuntime/c/simulation/solver/embedded_server.c
Expand Up @@ -30,60 +30,86 @@

#include "embedded_server.h"

#if defined(__MINGW32__) || defined(_MSC_VER)
#include <windows.h>
#if defined(__MINGW32__) || defined(_MSC_VER)
#include "util/omc_msvc.h"
#define UPC_DA
#define DLL_EXT ".dll"
#elif defined(__APPLE__)
#include <dlfcn.h>
#define DLL_EXT ".dylib"
#else
#include <dlfcn.h>
#define DLL_EXT ".so"
#endif

void no_embedded_server_init(DATA *data, double tout, double step, const char *argv_0)
void* no_embedded_server_init(DATA *data, double tout, double step, const char *argv_0, void (*omc_real_time_sync_update)(DATA *data, double scaling))
{
return NULL;
}

void no_embedded_server_deinit()
void no_embedded_server_deinit(void *handle)
{
}

void no_embedded_server_update(double tout)
void no_embedded_server_update(void *handle, double tout)
{
}

void (*embedded_server_init)(DATA *data, double tout, double step, const char *argv_0) = no_embedded_server_init;
void (*embedded_server_deinit)() = no_embedded_server_deinit;
void* (*embedded_server_init)(DATA *data, double tout, double step, const char *argv_0, void (*omc_real_time_sync_update)(DATA *data, double scaling)) = no_embedded_server_init;
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
void (*embedded_server_update)(double tout) = no_embedded_server_update;
void (*embedded_server_update)(void*, double tout) = no_embedded_server_update;

void embedded_server_load_functions()
void* embedded_server_load_functions(const char *server_name)
{
fprintf(stderr, "embedded_server_load_functions\n");
void *dll, *funcInit, *funcDeinit, *funcUpdate;
if (NULL==server_name || 0==strcmp("none", server_name)) {
return NULL;
}
if (0==strcmp("opc-ua", server_name)) {
server_name = "libomopcua" DLL_EXT;
} else if (0==strcmp("opc-da", server_name)) {
#if defined(UPC_DA)
const char *dllFile = "C:\\OpenModelica\\OMCompiler\\SimulationRuntime\\opc\\da\\libomopcda.dll";
HINSTANCE dll = LoadLibrary(dllFile);
void *funcInit, *funcDeinit, *funcUpdate;
server_name = "libomopcda" DLL_EXT;
#else
errorStreamPrint(LOG_DEBUG, 0, "OPC DA interface is not available on this platform (requires WIN32)");
MMC_THROW();
#endif
}
infoStreamPrint(LOG_DEBUG, 0, "Try to load embedded server %s", server_name);
dll = dlopen(server_name, RTLD_LAZY);

if (dll == NULL) {
infoStreamPrint(LOG_DEBUG, 0, "Failed to load dll: %s\n", dllFile);
return;
errorStreamPrint(LOG_DEBUG, 0, "Failed to load shared object %s: %s\n", server_name, dlerror());
MMC_THROW();
}

funcInit = GetProcAddress(dll, "opc_da_init");
funcInit = dlsym(dll, "omc_embedded_server_init");
if (!funcInit) {
infoStreamPrint(LOG_DEBUG, 0, "Failed to load function opc_da_init\n");
return;
errorStreamPrint(LOG_DEBUG, 0, "Failed to load function opc_da_init: %s\n", dlerror());
MMC_THROW();
}
funcDeinit = GetProcAddress(dll, "opc_da_deinit");
funcDeinit = dlsym(dll, "omc_embedded_server_deinit");
if (!funcDeinit) {
infoStreamPrint(LOG_DEBUG, 0, "Failed to load function opc_da_deinit\n");
return;
errorStreamPrint(LOG_DEBUG, 0, "Failed to load function opc_da_deinit: %s\n", dlerror());
MMC_THROW();
}
funcUpdate = GetProcAddress(dll, "opc_da_new_iteration");
funcUpdate = dlsym(dll, "omc_embedded_server_update");
if (!funcUpdate) {
infoStreamPrint(LOG_DEBUG, 0, "Failed to load function opc_da_new_iteration\n");
return;
errorStreamPrint(LOG_DEBUG, 0, "Failed to load function opc_da_new_iteration: %s\n", dlerror());
MMC_THROW();
}
embedded_server_init = funcInit;
embedded_server_deinit = funcDeinit;
embedded_server_update = funcUpdate;
infoStreamPrint(LOG_DEBUG, 0, "Using embedded server=opc_da\n");
#endif
infoStreamPrint(LOG_DEBUG, 0, "Loaded embedded server");
return dll;
}

void embedded_server_unload_functions(void *dllHandle)
{
if (dllHandle) {
dlclose(dllHandle);
}
}
10 changes: 6 additions & 4 deletions SimulationRuntime/c/simulation/solver/embedded_server.h
Expand Up @@ -37,12 +37,14 @@
extern "C" {
#endif

extern void (*embedded_server_init)(DATA *data, double tout, double step, const char *argv_0);
extern void (*embedded_server_deinit)();
extern void* (*embedded_server_init)(DATA *data, double tout, double step, const char *argv_0, void (*omc_real_time_sync_update)(DATA *data, double scaling));
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 void (*embedded_server_update)(double tout);
extern void embedded_server_load_functions();
extern void (*embedded_server_update)(void *handle, double tout);
/* 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);

#if defined(__cplusplus)
}
Expand Down
Expand Up @@ -492,7 +492,7 @@ void initSample(DATA* data, threadData_t *threadData, double startTime, double s
long i;

data->callback->function_initSample(data, threadData); /* set-up sample */
data->simulationInfo->nextSampleEvent = stopTime + 1.0; /* should never be reached */
data->simulationInfo->nextSampleEvent = NAN; /* should never be reached */
for(i=0; i<data->modelData->nSamples; ++i) {
if(startTime < data->modelData->samplesInfo[i].start) {
data->simulationInfo->nextSampleTimes[i] = data->modelData->samplesInfo[i].start;
Expand Down
2 changes: 1 addition & 1 deletion SimulationRuntime/c/simulation/solver/linearSolverLapack.c
Expand Up @@ -161,7 +161,7 @@ static int wrapper_fvec_lapack(_omc_vector* x, _omc_vector* f, int* iflag, void*
int solveLapack(DATA *data, threadData_t *threadData, int sysNumber)
{
void *dataAndThreadData[2] = {data, threadData};
int i, j, iflag = 1;
int i, iflag = 1;
LINEAR_SYSTEM_DATA* systemData = &(data->simulationInfo->linearSystemData[sysNumber]);
DATA_LAPACK* solverData = (DATA_LAPACK*)systemData->solverData;

Expand Down
21 changes: 17 additions & 4 deletions SimulationRuntime/c/simulation/solver/perform_simulation.c
Expand Up @@ -50,6 +50,7 @@

#include "simulation/solver/synchronous.h"
#include "simulation/solver/embedded_server.h"
#include "simulation/solver/real_time_sync.h"

/*! \fn updateContinuousSystem
*
Expand Down Expand Up @@ -100,7 +101,6 @@ static int simulationUpdate(DATA* data, threadData_t *threadData, SOLVER_INFO* s
cleanUpOldValueListAfterEvent(data, solverInfo->currentTime);
messageClose(LOG_EVENTS);
threadData->currentErrorStage = ERROR_SIMULATION;

solverInfo->didEventStep = 1;
overwriteOldSimulationData(data);
}
Expand Down Expand Up @@ -216,8 +216,21 @@ static void fmtEmitStep(DATA* data, threadData_t *threadData, MEASURE_TIME* mt,
if ((omc_flag[FLAG_NOEVENTEMIT] && didEventStep == 0) || !omc_flag[FLAG_NOEVENTEMIT]) {
sim_result.emit(&sim_result, data, threadData);
}
fprintf(stderr, "call embedded_server_update\n");
embedded_server_update(data->localData[0]->timeValue);
embedded_server_update(data->embeddedServerState, data->localData[0]->timeValue);
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));
int64_t maxLateNano = data->simulationInfo->stepSize*1e9*0.1*data->real_time_sync.scaling /* Maximum late time: 10% of step size */;
if (res > maxLateNano) {
int t=0,tMaxLate=0;
const char *unit = prettyPrintNanoSec(res, &t);
const char *unit2 = prettyPrintNanoSec(maxLateNano, &tMaxLate);
errorStreamPrint(LOG_RT, 0, "Missed deadline at time %g; delta was %d %s (maxLate=%d %s)", time, t, unit, tMaxLate, unit2);
}
if (res > data->real_time_sync.maxLate) {
data->real_time_sync.maxLate = res;
}
}

printAllVarsDebug(data, 0, LOG_DEBUG); /* ??? */
}
Expand Down Expand Up @@ -316,7 +329,7 @@ int prefixedName_performSimulation(DATA* data, threadData_t *threadData, SOLVER_
modelica_boolean syncStep = 0;

/***** Start main simulation loop *****/
while(solverInfo->currentTime < simInfo->stopTime)
while(solverInfo->currentTime < simInfo->stopTime || !simInfo->useStopTime)
{
int success = 0;
threadData->currentErrorStage = ERROR_SIMULATION;
Expand Down

0 comments on commit 0424117

Please sign in to comment.