diff --git a/libs/util/src/win/libs.c b/libs/util/src/win/libs.c index 9f0d20b..7f6bc34 100644 --- a/libs/util/src/win/libs.c +++ b/libs/util/src/win/libs.c @@ -10,6 +10,7 @@ #define _WINSOCKAPI_ // stops windows.h including winsock.h #include +#include #include "common/logging.h" #include "common/memory.h" @@ -23,24 +24,12 @@ extern "C" { #endif /* __cplusplus */ -McxStatus mcx_dll_load(DllHandle * handle, const char * dllPath) { - DllHandleCheck compValue; - - wchar_t * wDllPath = mcx_string_to_widechar(dllPath); - - * handle = LoadLibraryExW(wDllPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); - mcx_free(wDllPath); - - compValue = (DllHandleCheck) * handle; - if (compValue <= HINSTANCE_ERROR) { - LPVOID lpMsgBuf; - DWORD err = GetLastError(); - - mcx_log(LOG_ERROR, "Util: Dll (%s) could not be loaded", dllPath); - - switch (err) { +static void print_last_error() { + LPVOID lpMsgBuf; + DWORD err = GetLastError(); + switch (err) { case ERROR_BAD_EXE_FORMAT: - mcx_log(LOG_ERROR, "Util: There is a mismatch in bitness (32/64) between current Model.CONNECT Execution Engine and the dynamic library", dllPath); + mcx_log(LOG_ERROR, "Util: There is a mismatch in bitness (32/64) between current Model.CONNECT Execution Engine and the dynamic library"); break; default: FormatMessage( @@ -56,11 +45,62 @@ McxStatus mcx_dll_load(DllHandle * handle, const char * dllPath) { mcx_log(LOG_ERROR, "Util: Error %d: %s", err, lpMsgBuf); LocalFree(lpMsgBuf); break; + } +} + + +McxStatus mcx_dll_load(DllHandle * handle, const char * dllPath) { + DllHandleCheck compValue; + + wchar_t * wDllPath = mcx_string_to_widechar(dllPath); + + McxStatus retVal = RETURN_OK; + + if (PathIsRelativeW(wDllPath)) { + wchar_t * wFullDllPath = NULL; + DWORD length = GetFullPathNameW(wDllPath, 0, NULL, NULL); + if (length == 0) { + mcx_log(LOG_ERROR, "Util: Error retrieving length of absolute path of Dll (%s)", dllPath); + print_last_error(); + retVal = RETURN_ERROR; + goto relpath_cleanup; + } + wFullDllPath = (wchar_t *) mcx_malloc(sizeof(wchar_t) * length); + length = GetFullPathNameW(wDllPath, length, wFullDllPath, NULL); + if (length == 0) { + mcx_log(LOG_ERROR, "Util: Error creating absolute path for Dll (%s)", dllPath); + print_last_error(); + retVal = RETURN_ERROR; + goto relpath_cleanup; } - return RETURN_ERROR; +relpath_cleanup: + if (wDllPath) { + mcx_free(wDllPath); + } + if (retVal != RETURN_OK) { + goto cleanup; + } + wDllPath = wFullDllPath; + } + + mcx_log(LOG_DEBUG, "Util: Loading Dll %S", wDllPath); + + * handle = LoadLibraryExW(wDllPath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + + compValue = (DllHandleCheck) * handle; + if (compValue <= HINSTANCE_ERROR) { + mcx_log(LOG_ERROR, "Util: Dll (%s) could not be loaded", dllPath); + print_last_error(); + retVal = RETURN_ERROR; + goto cleanup; + } + +cleanup: + if (wDllPath) { + mcx_free(wDllPath); } - return RETURN_OK; + return retVal; } diff --git a/mcx/mcx.c b/mcx/mcx.c index c180997..d3afd34 100644 --- a/mcx/mcx.c +++ b/mcx/mcx.c @@ -573,6 +573,10 @@ McxStatus RunMCX(int argc, char *argv[]) { cleanup: + if (reader) { + reader->Cleanup(reader); + object_destroy(reader); + } if (mcxInput) { object_destroy(mcxInput); } if (model) { object_destroy(model); } diff --git a/scripts/SSP/Ports.xsd b/scripts/SSP/Ports.xsd index dee8489..448802c 100644 --- a/scripts/SSP/Ports.xsd +++ b/scripts/SSP/Ports.xsd @@ -25,10 +25,6 @@ - - - - @@ -38,7 +34,7 @@ - + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2de6bcc..5808f1e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,7 @@ file(GLOB MCX_SRC_COMMON "core/channels/*.c" "core/channels/*.cpp" "core/connections/*.c" "core/connections/*.cpp" "core/connections/filters/*.c" "core/connections/filters/*.cpp" + "core/parameters/*.c" "core/parameters/*.cpp" "components/*.c" "components/*.cpp" "fmu/*.c" "fmu/*.cpp" "objects/*.c" "objects/*.cpp" diff --git a/src/components/comp_constant.c b/src/components/comp_constant.c index 95daef9..57556fb 100644 --- a/src/components/comp_constant.c +++ b/src/components/comp_constant.c @@ -10,6 +10,7 @@ #include "components/comp_constant.h" +#include "core/channels/ChannelValue.h" #include "core/Databus.h" #include "reader/model/components/specific_data/ConstantInput.h" @@ -23,31 +24,27 @@ static McxStatus Read(Component * comp, ComponentInput * input, const struct Con ConstantInput * constantInput = (ConstantInput *)input; Databus * db = comp->GetDatabus(comp); - size_t numVecOut = DatabusGetOutVectorChannelsNum(db); + size_t numOut = DatabusGetOutChannelsNum(db); - // We read each vector channel (scalars are vectors of length 1) - // and the corresponding values from the input file. - compConstant->values = (ChannelValue **)mcx_calloc(numVecOut, sizeof(ChannelValue *)); + compConstant->values = (ChannelValue **)mcx_calloc(numOut, sizeof(ChannelValue *)); if (constantInput->values) { ConstantValuesInput * values = constantInput->values; size_t i = 0; - for (i = 0; i < numVecOut; i++) { + for (i = 0; i < numOut; i++) { ConstantValueInput * value = (ConstantValueInput *)values->values->At(values->values, i); if (value->type == CONSTANT_VALUE_SCALAR) { - compConstant->values[i] = (ChannelValue *)mcx_calloc(1, sizeof(ChannelValue)); - ChannelValueInit(compConstant->values[i], value->value.scalar->type); - ChannelValueSetFromReference(compConstant->values[i], &value->value.scalar->value); + compConstant->values[i] = ChannelValueNewScalar(value->value.scalar->type, &value->value.scalar->value); + if (!compConstant->values[i]) { + ComponentLog(comp, LOG_ERROR, "Could not set channel value"); + return RETURN_ERROR; + } } else { - size_t j = 0; - size_t size = ChannelValueTypeSize(value->value.array->type); - compConstant->values[i] = (ChannelValue *)mcx_calloc(value->value.array->numValues, - sizeof(ChannelValue)); - for (j = 0; j < value->value.array->numValues; j++) { - ChannelValueInit(compConstant->values[i] + j, value->value.array->type); - ChannelValueSetFromReference(compConstant->values[i] + j, - (char *)value->value.array->values + j * size); + compConstant->values[i] = ChannelValueNewArray(1, &value->value.array->numValues, value->value.array->type, value->value.array->values); + if (!compConstant->values[i]) { + ComponentLog(comp, LOG_ERROR, "Could not set channel value"); + return RETURN_ERROR; } } } @@ -59,36 +56,15 @@ static McxStatus Read(Component * comp, ComponentInput * input, const struct Con static ChannelValue * GetValue(CompConstant * compConstant, size_t idx) { Component * comp = (Component *) (compConstant); Databus * db = comp->GetDatabus(comp); - size_t numVecOut = DatabusGetOutVectorChannelsNum(db); - size_t i = 0; - size_t sum = 0; + size_t numOut = DatabusGetOutChannelsNum(db); ChannelValue * value = NULL; - VectorChannelInfo * vInfo = NULL; - size_t numCh = 0; - size_t startIdx = 0; - size_t endIdx = 0; - - for (i = 0; i < numVecOut; i++) { - vInfo = DatabusGetOutVectorChannelInfo(db, i); - startIdx = vInfo->GetStartIndex(vInfo); - endIdx = vInfo->GetEndIndex(vInfo); - numCh = endIdx - startIdx + 1; - - sum += numCh; - if (sum > idx) { - break; - } - } - if (i >= numVecOut) { + if (idx >= numOut) { ComponentLog(comp, LOG_ERROR, "GetValue: Invalid index (%zu) provided", idx); return NULL; } - value = compConstant->values[i]; - if (!vInfo->IsScalar(vInfo)) { - value += idx - (sum - numCh); - } + value = compConstant->values[idx]; return value; } @@ -97,16 +73,12 @@ static McxStatus Setup(Component * comp) { CompConstant * constComp = (CompConstant *)comp; McxStatus retVal = RETURN_OK; Databus * db = comp->GetDatabus(comp); - size_t numVecOut = DatabusGetOutVectorChannelsNum(db); + size_t numOut = DatabusGetOutChannelsNum(db); size_t i = 0; - for (i = 0; i < numVecOut; i++) { - VectorChannelInfo * vInfo = DatabusGetOutVectorChannelInfo(db, i); - size_t startIdx = vInfo->GetStartIndex(vInfo); - size_t endIdx = vInfo->GetEndIndex(vInfo); - size_t numCh = endIdx - startIdx; - retVal = DatabusSetOutRefVectorChannel(db, i, startIdx, endIdx, constComp->values[i]); + for (i = 0; i < numOut; i++) { + retVal = DatabusSetOutReference(db, i, (void *) &constComp->values[i]->value, constComp->values[i]->type); if (RETURN_OK != retVal) { ComponentLog(comp, LOG_ERROR, "Could not register out channel reference"); return RETURN_ERROR; @@ -128,18 +100,12 @@ static void CompConstantDestructor(CompConstant * compConst) { Component * comp = (Component *)compConst; McxStatus retVal = RETURN_OK; Databus * db = comp->GetDatabus(comp); - size_t numVecOut = DatabusGetOutVectorChannelsNum(db); - - if (numVecOut > 0 && compConst->values != NULL) { - size_t i, j; - for (i = 0; i < numVecOut; i++) { - VectorChannelInfo * vInfo = DatabusGetOutVectorChannelInfo(db, i); - size_t startIdx = vInfo->GetStartIndex(vInfo); - size_t endIdx = vInfo->GetEndIndex(vInfo); - size_t numCh = endIdx - startIdx + 1; - for (j = 0; j < numCh; j++) { - ChannelValueDestructor(&compConst->values[i][j]); - } + size_t numOut = DatabusGetOutChannelsNum(db); + + if (numOut > 0 && compConst->values != NULL) { + size_t i; + for (i = 0; i < numOut; i++) { + ChannelValueDestructor(compConst->values[i]); mcx_free(compConst->values[i]); } mcx_free(compConst->values); diff --git a/src/components/comp_fmu.c b/src/components/comp_fmu.c index 8496501..2024c88 100644 --- a/src/components/comp_fmu.c +++ b/src/components/comp_fmu.c @@ -15,6 +15,7 @@ #include "core/Databus.h" #include "core/Component_impl.h" #include "core/channels/ChannelInfo.h" +#include "core/connections/ConnectionInfoFactory.h" #include "fmilib.h" #include "fmu/Fmu1Value.h" #include "fmu/Fmu2Value.h" @@ -49,9 +50,12 @@ static McxStatus Fmu1SetupDatabus(Component * comp) { numChannels = DatabusInfoGetChannelNum(dbInfo); vals = fmu1->in; for (i = 0; i < numChannels; i++) { + Channel * ch = (Channel *) DatabusGetInChannel(db, i); ChannelInfo * info = DatabusInfoGetChannel(dbInfo, i); + ChannelDimension * dimension = info->dimension; + ChannelType * type = info->type; - if (DatabusChannelInIsValid(db, i)) { + if (ch->IsConnected(ch) || ch->info.defaultValue) { Fmu1Value * val = NULL; fmi1_import_variable_t * var = NULL; @@ -62,33 +66,24 @@ static McxStatus Fmu1SetupDatabus(Component * comp) { channelName = ChannelInfoGetName(info); } - var = fmi1_import_get_variable_by_name(fmu1->fmiImport, channelName); - if (!var) { - ComponentLog(comp, LOG_ERROR, "Could not get variable %s", channelName); - return RETURN_ERROR; - } - - if (info->type != Fmi1TypeToChannelType(fmi1_import_get_variable_base_type(var))) { - ComponentLog(comp, LOG_ERROR, "Variable types of %s do not match", channelName); - ComponentLog(comp, LOG_ERROR, "Expected: %s, Imported from FMU: %s", - ChannelTypeToString(info->type), ChannelTypeToString(Fmi1TypeToChannelType(fmi1_import_get_variable_base_type(var)))); - return RETURN_ERROR; + if (dimension) { // arrays + val = Fmu1ValueReadArray(comp->GetName(comp), type, info->channel, channelName, dimension, fmu1->fmiImport); + } else { // scalars + val = Fmu1ValueReadScalar(comp->GetName(comp), type, info->channel, channelName, fmu1->fmiImport); } - val = Fmu1ValueMake(channelName, var, info->channel); if (!val) { - ComponentLog(comp, LOG_ERROR, "Could not set value for channel %s", channelName); + ComponentLog(comp, LOG_ERROR, "Could not create value for channel %s", channelName); return RETURN_ERROR; } - retVal = vals->PushBackNamed(vals, (Object *)val, channelName); + retVal = vals->PushBackNamed(vals, (Object *) val, channelName); if (RETURN_OK != retVal) { ComponentLog(comp, LOG_ERROR, "Could not store value for %s", channelName); return RETURN_ERROR; } - retVal = DatabusSetInReference(comp->GetDatabus(comp), i, ChannelValueReference(&val->val), - ChannelValueType(&val->val)); + retVal = DatabusSetInReference(db, i, ChannelValueDataPointer(&val->val), ChannelValueType(&val->val)); if (RETURN_OK != retVal) { ComponentLog(comp, LOG_ERROR, "Could not set reference for channel %s", channelName); return RETURN_ERROR; @@ -101,6 +96,8 @@ static McxStatus Fmu1SetupDatabus(Component * comp) { vals = fmu1->out; for (i = 0; i < numChannels; i++) { ChannelInfo * info = DatabusInfoGetChannel(dbInfo, i); + ChannelDimension * dimension = info->dimension; + ChannelType * type = info->type; Fmu1Value * val = NULL; fmi1_import_variable_t * var = NULL; @@ -111,33 +108,24 @@ static McxStatus Fmu1SetupDatabus(Component * comp) { channelName = ChannelInfoGetName(info); } - var = fmi1_import_get_variable_by_name(fmu1->fmiImport, channelName); - if (!var) { - ComponentLog(comp, LOG_ERROR, "Could not get variable %s", channelName); - return RETURN_ERROR; - } - - if (info->type != Fmi1TypeToChannelType(fmi1_import_get_variable_base_type(var))) { - ComponentLog(comp, LOG_ERROR, "Variable types of %s do not match", channelName); - ComponentLog(comp, LOG_ERROR, "Expected: %s, Imported from FMU: %s", - ChannelTypeToString(info->type), ChannelTypeToString(Fmi1TypeToChannelType(fmi1_import_get_variable_base_type(var)))); - return RETURN_ERROR; + if (dimension) { // arrays + val = Fmu1ValueReadArray(comp->GetName(comp), type, info->channel, channelName, dimension, fmu1->fmiImport); + } else { // scalars + val = Fmu1ValueReadScalar(comp->GetName(comp), type, info->channel, channelName, fmu1->fmiImport); } - val = Fmu1ValueMake(channelName, var, NULL); if (!val) { - ComponentLog(comp, LOG_ERROR, "Could not set value for channel %s", channelName); + ComponentLog(comp, LOG_ERROR, "Could not create value for channel %s", channelName); return RETURN_ERROR; } - retVal = vals->PushBack(vals, (Object *)val); + retVal = vals->PushBackNamed(vals, (Object *) val, channelName); if (RETURN_OK != retVal) { ComponentLog(comp, LOG_ERROR, "Could not store value for %s", channelName); return RETURN_ERROR; } - retVal = DatabusSetOutReference(comp->GetDatabus(comp), i, ChannelValueReference(&val->val), - ChannelValueType(&val->val)); + retVal = DatabusSetOutReference(db, i, ChannelValueDataPointer(&val->val), ChannelValueType(&val->val)); if (RETURN_OK != retVal) { ComponentLog(comp, LOG_ERROR, "Could not set reference for channel %s", channelName); return RETURN_ERROR; @@ -388,25 +376,21 @@ static McxStatus Fmu2SetupChannelIn(ObjectContainer /* Fmu2Values */ * vals, Dat numChannels = DatabusInfoGetChannelNum(dbInfo); for (i = 0; i < numChannels; i++) { + Channel * ch = (Channel *)DatabusGetInChannel(db, i); ChannelInfo * info = DatabusInfoGetChannel(dbInfo, i); Fmu2Value * val = (Fmu2Value *) vals->At(vals, i); - if (DatabusChannelInIsValid(db, i)) { - const char * channelName = info->nameInTool; - if (NULL == channelName) { - channelName = ChannelInfoGetName(info); - } - + if (ch->IsConnected(ch) || ch->info.defaultValue) { val->SetChannel(val, info->channel); - if (val->val.type != info->type) { - ChannelValueInit(&val->val, info->type); + if (!ChannelTypeIsValid(val->val.type)) { + ChannelValueInit(&val->val, ChannelTypeClone(info->type)); } retVal = DatabusSetInReference(db, i, - ChannelValueReference(&val->val), + ChannelValueDataPointer(&val->val), ChannelValueType(&val->val)); if (RETURN_OK != retVal) { - mcx_log(LOG_ERROR, "%s: Could not set reference for channel %s", logPrefix, channelName); + mcx_log(LOG_ERROR, "%s: Could not set reference for channel %s", logPrefix, val->name); return RETURN_ERROR; } } @@ -435,11 +419,13 @@ static McxStatus Fmu2SetupChannelOut(ObjectContainer /* Fmu2Values */ * vals, Da channelName = ChannelInfoGetName(info); } - if (val->val.type != info->type) { - ChannelValueInit(&val->val, info->type); + val->SetChannel(val, info->channel); + + if (!ChannelTypeEq(val->val.type, info->type)) { + ChannelValueInit(&val->val, ChannelTypeClone(info->type)); } retVal = DatabusSetOutReference(db, i, - ChannelValueReference(&val->val), + ChannelValueDataPointer(&val->val), ChannelValueType(&val->val)); if (RETURN_OK != retVal) { mcx_log(LOG_ERROR, "%s: Could not set reference for channel %s", logPrefix, channelName); @@ -550,24 +536,24 @@ static McxStatus Fmu2ReadChannelIn(ObjectContainer /* Fmu2Value */ * vals, Datab return RETURN_ERROR; } - if (CHANNEL_INTEGER != Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varLo))) { + if (!ChannelTypeEq(&ChannelTypeInteger, Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varLo)))) { mcx_log(LOG_ERROR, "%s: Variable types of %s do not match", logPrefix, channelNameLo); mcx_log(LOG_ERROR, "%s: Expected: %s, Imported from FMU: %s", logPrefix, - ChannelTypeToString(CHANNEL_INTEGER), ChannelTypeToString(Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varLo)))); + ChannelTypeToString(&ChannelTypeInteger), ChannelTypeToString(Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varLo)))); return RETURN_ERROR; } - if (CHANNEL_INTEGER != Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varHi))) { + if (!ChannelTypeEq(&ChannelTypeInteger, Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varHi)))) { mcx_log(LOG_ERROR, "%s: Variable types of %s do not match", logPrefix, channelNameHi); mcx_log(LOG_ERROR, "%s: Expected: %s, Imported from FMU: %s", logPrefix, - ChannelTypeToString(CHANNEL_INTEGER), ChannelTypeToString(Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varHi)))); + ChannelTypeToString(&ChannelTypeInteger), ChannelTypeToString(Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varHi)))); return RETURN_ERROR; } - if (CHANNEL_INTEGER != Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varSize))) { + if (!ChannelTypeEq(&ChannelTypeInteger, Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varSize)))) { mcx_log(LOG_ERROR, "%s: Variable types of %s do not match", logPrefix, channelNameSize); mcx_log(LOG_ERROR, "%s: Expected: %s, Imported from FMU: %s", logPrefix, - ChannelTypeToString(CHANNEL_INTEGER), ChannelTypeToString(Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varSize)))); + ChannelTypeToString(&ChannelTypeInteger), ChannelTypeToString(Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varSize)))); return RETURN_ERROR; } @@ -580,32 +566,40 @@ static McxStatus Fmu2ReadChannelIn(ObjectContainer /* Fmu2Value */ * vals, Datab mcx_free(channelNameLo); mcx_free(channelNameHi); mcx_free(channelNameSize); - } else { // scalar - var = fmi2_import_get_variable_by_name(fmiImport, channelName); - if (!var) { - mcx_log(LOG_ERROR, "%s: Could not get variable %s", logPrefix, channelName); + + retVal = vals->PushBack(vals, (Object *)val); + if (RETURN_OK != retVal) { + mcx_log(LOG_ERROR, "%s: Could not store value for %s", logPrefix, channelName); return RETURN_ERROR; } - - if (info->type != Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(var))) { - mcx_log(LOG_ERROR, "%s: Variable types of %s do not match", logPrefix, channelName); - mcx_log(LOG_ERROR, "%s: Expected: %s, Imported from FMU: %s", logPrefix, - ChannelTypeToString(info->type), - ChannelTypeToString(Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(var)))); + } else if (info->dimension) { + val = Fmu2ReadFmu2ArrayValue(logPrefix, info->type, channelName, info->dimension, info->unitString, fmiImport); + if (!val) { + mcx_log(LOG_ERROR, "%s: Could not create value for %s", logPrefix, channelName); return RETURN_ERROR; } - val = Fmu2ValueScalarMake(channelName, var, info->unitString, NULL); + val->SetChannel(val, info->channel); + + retVal = vals->PushBack(vals, (Object *)val); + if (RETURN_OK != retVal) { + mcx_log(LOG_ERROR, "%s: Could not store value for %s", logPrefix, channelName); + return RETURN_ERROR; + } + } else { // scalar + val = Fmu2ReadFmu2ScalarValue(logPrefix, info->type, channelName, info->unitString, fmiImport); if (!val) { - mcx_log(LOG_ERROR, "%s: Could not set value for channel %s", logPrefix, channelName); + mcx_log(LOG_ERROR, "%s: Could not create value for %s", logPrefix, channelName); return RETURN_ERROR; } - } - retVal = vals->PushBack(vals, (Object *)val); - if (RETURN_OK != retVal) { - mcx_log(LOG_ERROR, "%s: Could not store value for %s", logPrefix, channelName); - return RETURN_ERROR; + val->SetChannel(val, info->channel); + + retVal = vals->PushBack(vals, (Object *)val); + if (RETURN_OK != retVal) { + mcx_log(LOG_ERROR, "%s: Could not store value for %s", logPrefix, channelName); + return RETURN_ERROR; + } } } @@ -659,24 +653,24 @@ static McxStatus Fmu2ReadChannelOut(ObjectContainer /* Fmu2Value */ * vals, Data return RETURN_ERROR; } - if (CHANNEL_INTEGER != Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varLo))) { + if (!ChannelTypeEq(&ChannelTypeInteger, Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varLo)))) { mcx_log(LOG_ERROR, "%s: Variable types of %s do not match", logPrefix , channelNameLo); mcx_log(LOG_ERROR, "%s: Expected: %s, Imported from FMU: %s", - ChannelTypeToString(CHANNEL_INTEGER), ChannelTypeToString(Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varLo)))); + ChannelTypeToString(&ChannelTypeInteger), ChannelTypeToString(Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varLo)))); return RETURN_ERROR; } - if (CHANNEL_INTEGER != Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varHi))) { + if (!ChannelTypeEq(&ChannelTypeInteger, Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varHi)))) { mcx_log(LOG_ERROR, "%s: Variable types of %s do not match", logPrefix , channelNameHi); mcx_log(LOG_ERROR, "%s: Expected: %s, Imported from FMU: %s", - ChannelTypeToString(CHANNEL_INTEGER), ChannelTypeToString(Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varHi)))); + ChannelTypeToString(&ChannelTypeInteger), ChannelTypeToString(Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varHi)))); return RETURN_ERROR; } - if (CHANNEL_INTEGER != Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varSize))) { + if (!ChannelTypeEq(&ChannelTypeInteger, Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varSize)))) { mcx_log(LOG_ERROR, "%s: Variable types of %s do not match", logPrefix , channelNameSize); mcx_log(LOG_ERROR, "%s: Expected: %s, Imported from FMU: %s", - ChannelTypeToString(CHANNEL_INTEGER), ChannelTypeToString(Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varSize)))); + ChannelTypeToString(&ChannelTypeInteger), ChannelTypeToString(Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(varSize)))); return RETURN_ERROR; } @@ -689,32 +683,29 @@ static McxStatus Fmu2ReadChannelOut(ObjectContainer /* Fmu2Value */ * vals, Data mcx_free(channelNameLo); mcx_free(channelNameHi); mcx_free(channelNameSize); - } else { // scalar - var = fmi2_import_get_variable_by_name(fmiImport, channelName); - if (!var) { - mcx_log(LOG_ERROR, "%s: Could not get variable %s", logPrefix , channelName); + + retVal = vals->PushBack(vals, (Object *)val); + if (RETURN_OK != retVal) { + mcx_log(LOG_ERROR, "%s: Could not store value for %s", logPrefix , channelName); return RETURN_ERROR; } + } else if (info->dimension) { + val = Fmu2ReadFmu2ArrayValue(logPrefix, info->type, channelName, info->dimension, info->unitString, fmiImport); - if (info->type != Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(var))) { - mcx_log(LOG_ERROR, "%s: Variable types of %s do not match", logPrefix , channelName); - mcx_log(LOG_ERROR, "%s: Expected: %s, Imported from FMU: %s", - ChannelTypeToString(info->type), ChannelTypeToString(Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(var)))); + retVal = vals->PushBack(vals, (Object *)val); + if (RETURN_OK != retVal) { + mcx_log(LOG_ERROR, "%s: Could not store value for %s", logPrefix, channelName); return RETURN_ERROR; } + } else { // scalar + val = Fmu2ReadFmu2ScalarValue(logPrefix, info->type, channelName, info->unitString, fmiImport); - val = Fmu2ValueScalarMake(channelName, var, info->unitString, NULL); - if (!val) { - mcx_log(LOG_ERROR, "%s: Could not set value for channel %s", logPrefix , channelName); + retVal = vals->PushBack(vals, (Object *)val); + if (RETURN_OK != retVal) { + mcx_log(LOG_ERROR, "%s: Could not store value for %s", logPrefix, channelName); return RETURN_ERROR; } } - - retVal = vals->PushBack(vals, (Object *)val); - if (RETURN_OK != retVal) { - mcx_log(LOG_ERROR, "%s: Could not store value for %s", logPrefix , channelName); - return RETURN_ERROR; - } } return RETURN_OK; @@ -802,20 +793,17 @@ static McxStatus Fmu2Read(Component * comp, ComponentInput * input, const struct ParametersInput * parametersInput = input->parameters; if (parametersInput) { - vals = Fmu2ReadParams(parametersInput, - compFmu->fmu2.fmiImport, - NULL + retVal = Fmu2ReadParams( + fmu2->params, + fmu2->arrayParams, + parametersInput, + compFmu->fmu2.fmiImport, + NULL ); - if (!vals) { - ComponentLog(comp, LOG_ERROR, "Could not read parameters"); - return RETURN_ERROR; - } - retVal = fmu2->params->Append(fmu2->params, vals); if (RETURN_OK != retVal) { - ComponentLog(comp, LOG_ERROR, "Could not add parameters"); + ComponentLog(comp, LOG_ERROR, "Could not read parameters"); return RETURN_ERROR; } - object_destroy(vals); } } @@ -824,17 +812,11 @@ static McxStatus Fmu2Read(Component * comp, ComponentInput * input, const struct ParametersInput * parametersInput = input->initialValues; if (parametersInput) { - vals = Fmu2ReadParams(parametersInput, compFmu->fmu2.fmiImport, NULL); - if (!vals) { - ComponentLog(comp, LOG_ERROR, "Could not read initial values"); - return RETURN_ERROR; - } - retVal = fmu2->initialValues->Append(fmu2->initialValues, vals); + retVal = Fmu2ReadParams(fmu2->initialValues, NULL, parametersInput, compFmu->fmu2.fmiImport, NULL); if (RETURN_OK != retVal) { - ComponentLog(comp, LOG_ERROR, "Could not add initial values"); + ComponentLog(comp, LOG_ERROR, "Could not read initial values"); return RETURN_ERROR; } - object_destroy(vals); } } @@ -1108,164 +1090,6 @@ static ChannelMode GetInChannelDefaultMode(struct Component * comp) { return CHANNEL_OPTIONAL; } -static McxStatus SetDependenciesFMU2(CompFMU *compFmu, struct Dependencies *deps) { - Component * comp = (Component *) compFmu; - McxStatus ret_val = RETURN_OK; - - size_t *start_index = NULL; - size_t *dependency = NULL; - char *factor_kind = NULL; - - size_t i = 0, j = 0, k = 0; - size_t num_dependencies = 0; - size_t dep_idx = 0; - - SizeTSizeTMap *dependencies_to_in_channels = (SizeTSizeTMap*)object_create(SizeTSizeTMap); - // dictionary used to store input connection information - SizeTSizeTMap *in_channel_connectivity = (SizeTSizeTMap*)object_create(SizeTSizeTMap); - SizeTSizeTMap *unknowns_to_out_channels = (SizeTSizeTMap*)object_create(SizeTSizeTMap); - // dictionary used to later find ommitted elements - SizeTSizeTMap *processed_out_channels = (SizeTSizeTMap*)object_create(SizeTSizeTMap); - - // get dependency information via the fmi library - fmi2_import_variable_list_t * init_unknowns = fmi2_import_get_initial_unknowns_list(compFmu->fmu2.fmiImport); - size_t num_init_unknowns = fmi2_import_get_variable_list_size(init_unknowns); - - fmi2_import_get_initial_unknowns_dependencies(compFmu->fmu2.fmiImport, &start_index, &dependency, &factor_kind); - - // the dependency information in is encoded via variable indices in modelDescription.xml - // our dependency matrix uses channel indices - // to align those 2 index types we use helper dictionaries which store the mapping between them - - // map each dependency index to an input channel index - ObjectContainer *in_vars = compFmu->fmu2.in; - size_t num_in_vars = in_vars->Size(in_vars); - - Databus * db = comp->GetDatabus(comp); - DatabusInfo * db_info = DatabusGetInInfo(db); - size_t num_in_channels = DatabusInfoGetChannelNum(db_info); - - for (i = 0; i < num_in_vars; ++i) { - Fmu2Value *val = (Fmu2Value *)in_vars->At(in_vars, i); - ChannelInfo * info = DatabusInfoGetChannel(db_info, i); - if (DatabusChannelInIsValid(db, k) && info->connected) { - // key i in the map means channel i is connected - in_channel_connectivity->Add(in_channel_connectivity, i, 1 /* true */); - } - - if (val->data->type == FMU2_VALUE_SCALAR) { - fmi2_import_variable_t *var = val->data->data.scalar; - size_t idx = fmi2_import_get_variable_original_order(var) + 1; - dependencies_to_in_channels->Add(dependencies_to_in_channels, idx, i); - } - } - - // element is not present in modelDescription.xml - // The dependency matrix consists of only 1 (if input is connected) - if (start_index == NULL) { - for (i = 0; i < GetDependencyNumOut(deps); ++i) { - for (j = 0; j < GetDependencyNumIn(deps); ++j) { - SizeTSizeTElem * elem = in_channel_connectivity->Get(in_channel_connectivity, j); - if (elem) { - ret_val = SetDependency(deps, j, i, DEP_DEPENDENT); - if (RETURN_OK != ret_val) { - goto cleanup; - } - } - } - } - - goto cleanup; - } - - // map each initial_unkown index to an output channel index - ObjectContainer *out_vars = compFmu->fmu2.out; - size_t num_out_vars = out_vars->Size(out_vars); - - for (i = 0; i < num_out_vars; ++i) { - Fmu2Value *val = (Fmu2Value *)out_vars->At(out_vars, i); - - if (val->data->type == FMU2_VALUE_SCALAR) { - fmi2_import_variable_t *var = val->data->data.scalar; - size_t idx = fmi2_import_get_variable_original_order(var) + 1; - unknowns_to_out_channels->Add(unknowns_to_out_channels, idx, i); - } - } - - // fill up the dependency matrix - for (i = 0; i < num_init_unknowns; ++i) { - fmi2_import_variable_t *init_unknown = fmi2_import_get_variable(init_unknowns, i); - size_t init_unknown_idx = fmi2_import_get_variable_original_order(init_unknown) + 1; - - SizeTSizeTElem * out_pair = unknowns_to_out_channels->Get(unknowns_to_out_channels, init_unknown_idx); - if (out_pair == NULL) { - continue; // in case some variables are ommitted from the input file - } - - processed_out_channels->Add(processed_out_channels, out_pair->value, 1); - - num_dependencies = start_index[i + 1] - start_index[i]; - for (j = 0; j < num_dependencies; ++j) { - dep_idx = dependency[start_index[i] + j]; - if (dep_idx == 0) { - // The element does not explicitly define a `dependencies` attribute - // In this case it depends on all inputs - for (k = 0; k < num_in_channels; ++k) { - SizeTSizeTElem * elem = in_channel_connectivity->Get(in_channel_connectivity, k); - if (elem) { - ret_val = SetDependency(deps, k, out_pair->value, DEP_DEPENDENT); - if (RETURN_OK != ret_val) { - goto cleanup; - } - } - } - } else { - // The element explicitly defines its dependencies - SizeTSizeTElem * in_pair = dependencies_to_in_channels->Get(dependencies_to_in_channels, dep_idx); - - if (in_pair) { - SizeTSizeTElem * elem = in_channel_connectivity->Get(in_channel_connectivity, in_pair->value); - if (elem) { - ret_val = SetDependency(deps, in_pair->value, out_pair->value, DEP_DEPENDENT); - if (RETURN_OK != ret_val) { - goto cleanup; - } - } - } - } - } - } - - // Initial unknowns which are ommitted from the element in - // modelDescription.xml file depend on all inputs - for (i = 0; i < num_out_vars; ++i) { - if (processed_out_channels->Get(processed_out_channels, i) == NULL) { - Fmu2Value *val = (Fmu2Value *)out_vars->At(out_vars, i); - - if (fmi2_import_get_initial(val->data->data.scalar) != fmi2_initial_enu_exact) { - for (k = 0; k < num_in_channels; ++k) { - SizeTSizeTElem * elem = in_channel_connectivity->Get(in_channel_connectivity, k); - if (elem) { - ret_val = SetDependency(deps, k, i, DEP_DEPENDENT); - if (RETURN_OK != ret_val) { - goto cleanup; - } - } - } - } - } - } - -cleanup: // free dynamically allocated objects - object_destroy(dependencies_to_in_channels); - object_destroy(in_channel_connectivity); - object_destroy(unknowns_to_out_channels); - object_destroy(processed_out_channels); - fmi2_import_free_variable_list(init_unknowns); - - return ret_val; -} - static struct Dependencies* Fmu2GetInOutGroupsInitialDependency(const Component * comp) { CompFMU *comp_fmu = (CompFMU *)comp; struct Dependencies *dependencies = NULL; @@ -1282,7 +1106,8 @@ static struct Dependencies* Fmu2GetInOutGroupsInitialDependency(const Component size_t dummy_num_out = 1; dependencies = DependenciesCreate(num_in, dummy_num_out); for (j = 0; j < num_in; ++j) { - if (DatabusChannelInIsValid(db, j)) { + Channel * ch = (Channel *) DatabusGetInChannel(db, j); + if (ch->IsConnected(ch) || ch->info.defaultValue) { retVal = SetDependency(dependencies, j, 0, DEP_DEPENDENT); if (RETURN_OK != retVal) { mcx_log(LOG_ERROR, "Initial dependency matrix for %s could not be created", comp->GetName(comp)); @@ -1292,7 +1117,7 @@ static struct Dependencies* Fmu2GetInOutGroupsInitialDependency(const Component } } else { dependencies = DependenciesCreate(num_in, num_out); - if (SetDependenciesFMU2(comp_fmu, dependencies) != RETURN_OK) { + if (Fmu2SetDependencies(&comp_fmu->fmu2, db, dependencies, TRUE) != RETURN_OK) { mcx_log(LOG_ERROR, "Initial dependency matrix for %s could not be created", comp->GetName(comp)); return NULL; } diff --git a/src/components/comp_integrator.c b/src/components/comp_integrator.c index 968c261..c05c538 100644 --- a/src/components/comp_integrator.c +++ b/src/components/comp_integrator.c @@ -56,13 +56,13 @@ static McxStatus Setup(Component * comp) { CompIntegrator * integrator = (CompIntegrator *) comp; McxStatus retVal = RETURN_OK; - retVal = DatabusSetInReference(comp->GetDatabus(comp), 0, &integrator->deriv, CHANNEL_DOUBLE); + retVal = DatabusSetInReference(comp->GetDatabus(comp), 0, &integrator->deriv, &ChannelTypeDouble); if (RETURN_OK != retVal) { ComponentLog(comp, LOG_ERROR, "Could not register in channel reference"); return RETURN_ERROR; } - retVal = DatabusSetOutReference(comp->GetDatabus(comp), 0, &integrator->state, CHANNEL_DOUBLE); + retVal = DatabusSetOutReference(comp->GetDatabus(comp), 0, &integrator->state, &ChannelTypeDouble); if (RETURN_OK != retVal) { ComponentLog(comp, LOG_ERROR, "Could not register out channel reference"); return RETURN_ERROR; diff --git a/src/components/comp_vector_integrator.c b/src/components/comp_vector_integrator.c index 57b6bc1..efd1ba7 100644 --- a/src/components/comp_vector_integrator.c +++ b/src/components/comp_vector_integrator.c @@ -11,6 +11,7 @@ #include "components/comp_vector_integrator.h" #include "core/Databus.h" +#include "core/channels/ChannelValue.h" #include "reader/model/components/specific_data/VectorIntegratorInput.h" #ifdef __cplusplus @@ -20,9 +21,10 @@ extern "C" { typedef struct CompVectorIntegrator { Component _; - size_t numStates; - double * state; - double * deriv; + size_t num; + + ChannelValue * state; + ChannelValue * deriv; double initialState; @@ -33,32 +35,52 @@ static McxStatus Read(Component * comp, ComponentInput * input, const struct Con CompVectorIntegrator * integrator = (CompVectorIntegrator *) comp; VectorIntegratorInput * integratorInput = (VectorIntegratorInput *) input; - size_t numAllIn = 0, numAllOut = 0; size_t i = 0; Databus * db = comp->GetDatabus(comp); - size_t numVecIn = DatabusGetInVectorChannelsNum(db); - size_t numVecOut = DatabusGetOutVectorChannelsNum(db); + size_t numIn = DatabusGetInChannelsNum(db); + size_t numOut = DatabusGetOutChannelsNum(db); - for (i = 0; i < numVecIn; i++) { - VectorChannelInfo *vInfo = DatabusGetInVectorChannelInfo(db, i); - size_t numCh = vInfo->GetEndIndex(vInfo) - vInfo->GetStartIndex(vInfo) + 1; - numAllIn += numCh; + if (numIn != numOut) { + ComponentLog(comp, LOG_ERROR, "#inports (%zu) does not match the #outports (%zu)", numIn, numOut); + return RETURN_ERROR; } - for (i = 0; i < numVecOut; i++) { - VectorChannelInfo *vInfo = DatabusGetOutVectorChannelInfo(db, i); - size_t numCh = vInfo->GetEndIndex(vInfo) - vInfo->GetStartIndex(vInfo) + 1; - numAllOut += numCh; + + integrator->num = numOut; + + integrator->deriv = mcx_calloc(sizeof(ChannelValue), integrator->num); + if (!integrator->deriv) { + return RETURN_ERROR; } - if (numAllIn != numAllOut) { - ComponentLog(comp, LOG_ERROR, "#inports (%d) does not match the #outports (%d)", numAllIn, numAllOut); + integrator->state = mcx_calloc(sizeof(ChannelValue), integrator->num); + if (!integrator->state) { return RETURN_ERROR; } - integrator->numStates = numAllOut; integrator->initialState = integratorInput->initialState.defined ? integratorInput->initialState.value : 0.0; + for (i = 0; i < integrator->num; i++) { + ChannelInfo * inInfo = DatabusGetInChannelInfo(db, i); + ChannelInfo * outInfo = DatabusGetOutChannelInfo(db, i); + + if (!ChannelTypeEq(inInfo->type, outInfo->type)) { + ComponentLog(comp, LOG_ERROR, "Types of inport %s and outport %s do not match", ChannelInfoGetName(inInfo), ChannelInfoGetName(outInfo)); + return RETURN_ERROR; + } + + ChannelInfo * info = outInfo; + ChannelType * type = info->type; + + if (!ChannelTypeEq(ChannelTypeBaseType(type), &ChannelTypeDouble)) { + ComponentLog(comp, LOG_ERROR, "Inport %s: Invalid type", ChannelInfoGetName(info)); + return RETURN_ERROR; + } + + ChannelValueInit(&integrator->deriv[i], ChannelTypeClone(type)); + ChannelValueInit(&integrator->state[i], ChannelTypeClone(type)); + } + return RETURN_OK; } @@ -66,40 +88,26 @@ static McxStatus Setup(Component * comp) { CompVectorIntegrator * integrator = (CompVectorIntegrator *) comp; McxStatus retVal = RETURN_OK; Databus * db = comp->GetDatabus(comp); - size_t numVecIn = DatabusGetInVectorChannelsNum(db); - size_t numVecOut = DatabusGetOutVectorChannelsNum(db); + size_t numIn = DatabusGetInChannelsNum(db); + size_t numOut = DatabusGetOutChannelsNum(db); size_t i = 0; - size_t nextIdx = 0; - - integrator->deriv = (double *) mcx_malloc(integrator->numStates * sizeof(double)); - integrator->state = (double *) mcx_malloc(integrator->numStates * sizeof(double)); - - nextIdx = 0; - for (i = 0; i < numVecIn; i++) { - VectorChannelInfo *vInfo = DatabusGetInVectorChannelInfo(db, i); - size_t startIdx = vInfo->GetStartIndex(vInfo); - size_t endIdx = vInfo->GetEndIndex(vInfo); - size_t numCh = endIdx - startIdx; - retVal = DatabusSetInRefVector(db, i, startIdx, endIdx, integrator->deriv + nextIdx, CHANNEL_DOUBLE); + + for (i = 0; i < integrator->num; i++) { + ChannelInfo * inInfo = DatabusGetInChannelInfo(db, i); + ChannelInfo * outInfo = DatabusGetOutChannelInfo(db, i); + + retVal = DatabusSetInReference(db, i, ChannelValueDataPointer(&integrator->deriv[i]), inInfo->type); if (RETURN_OK != retVal) { ComponentLog(comp, LOG_ERROR, "Could not register in channel reference"); return RETURN_ERROR; } - nextIdx = nextIdx + numCh + 1; - } - nextIdx = 0; - for (i = 0; i < numVecOut; i++) { - VectorChannelInfo *vInfo = DatabusGetOutVectorChannelInfo(db, i); - size_t startIdx = vInfo->GetStartIndex(vInfo); - size_t endIdx = vInfo->GetEndIndex(vInfo); - size_t numCh = endIdx - startIdx; - retVal = DatabusSetOutRefVector(db, i, startIdx, endIdx, integrator->state + nextIdx, CHANNEL_DOUBLE); + retVal = DatabusSetOutReference(db, i, ChannelValueDataPointer(&integrator->state[i]), outInfo->type); if (RETURN_OK != retVal) { ComponentLog(comp, LOG_ERROR, "Could not register out channel reference"); return RETURN_ERROR; } - nextIdx = nextIdx + numCh + 1; + } return RETURN_OK; @@ -109,8 +117,22 @@ static McxStatus Setup(Component * comp) { static McxStatus DoStep(Component * comp, size_t group, double time, double deltaTime, double endTime, int isNewStep) { CompVectorIntegrator * integrator = (CompVectorIntegrator *) comp; size_t i; - for (i = 0; i < integrator->numStates; i++) { - integrator->state[i] = integrator->state[i] + integrator->deriv[i] * deltaTime; + for (i = 0; i < integrator->num; i++) { + if (ChannelTypeIsArray(ChannelValueType(&integrator->state[i]))) { + mcx_array * state = ChannelValueDataPointer(&integrator->state[i]); + mcx_array * deriv = ChannelValueDataPointer(&integrator->deriv[i]); + + size_t j = 0; + for (j = 0; j < mcx_array_num_elements(state); j++) { + ((double *)state->data)[j] = ((double *)state->data)[j] + ((double *)deriv->data)[j] * deltaTime; + } + + } else { + double * state = (double *) ChannelValueDataPointer(&integrator->state[i]); + double * deriv = (double *) ChannelValueDataPointer(&integrator->deriv[i]); + + (*state) = (*state) + (*deriv) * deltaTime; + } } return RETURN_OK; @@ -120,8 +142,17 @@ static McxStatus DoStep(Component * comp, size_t group, double time, double delt static McxStatus Initialize(Component * comp, size_t idx, double startTime) { CompVectorIntegrator * integrator = (CompVectorIntegrator *) comp; size_t i; - for (i = 0; i < integrator->numStates; i++) { - integrator->state[i] = integrator->initialState; + for (i = 0; i < integrator->num; i++) { + if (ChannelTypeIsArray(ChannelValueType(&integrator->state[i]))) { + mcx_array * a = (mcx_array *) ChannelValueDataPointer(&integrator->state[i]); + size_t j; + + for (j = 0; j < mcx_array_num_elements(a); j++) { + ((double *) a->data)[j] = integrator->initialState; + } + } else { + ChannelValueSetFromReference(&integrator->state[i], &integrator->initialState); + } } return RETURN_OK; } @@ -146,7 +177,7 @@ static Component * CompVectorIntegratorCreate(Component * comp) { // local values self->initialState = 0.; - self->numStates = 0; + self->num = 0; self->state = NULL; self->deriv = NULL; diff --git a/src/core/Component.c b/src/core/Component.c index a533d95..b8070aa 100644 --- a/src/core/Component.c +++ b/src/core/Component.c @@ -216,7 +216,7 @@ static McxStatus DefineTimingChannel(Component * comp, const char * chName, cons return RETURN_ERROR; } - retVal = DatabusAddRTFactorChannel(comp->data->databus, chName, id, unit, reference, CHANNEL_DOUBLE); + retVal = DatabusAddRTFactorChannel(comp->data->databus, chName, id, unit, reference, &ChannelTypeDouble); mcx_free(id); @@ -384,7 +384,7 @@ McxStatus ComponentRegisterStorage(Component * comp, ResultsStorage * storage) { for (i = 0; i < numInChannels; i++) { Channel * channel = (Channel *) DatabusGetInChannel(db, i); - if (channel->IsValid(channel)) { + if (channel->ProvidesValue(channel)) { retVal = compStore->RegisterChannel(compStore, CHANNEL_STORE_IN, channel); if (RETURN_OK != retVal) { ComponentLog(comp, LOG_ERROR, "Could not register inport %d at storage", i); @@ -507,9 +507,11 @@ McxStatus ComponentDoStep(Component * comp, size_t group, double time, double de retVal = comp->DoStep(comp, group, time, deltaTime, endTime, isNewStep); mcx_signal_handler_unset_function(); mcx_signal_handler_unset_name(); - if (RETURN_OK != retVal) { - ComponentLog(comp, LOG_DEBUG, "Component specific DoStep failed"); + if (RETURN_ERROR == retVal) { + ComponentLog(comp, LOG_ERROR, "Component specific DoStep failed"); return RETURN_ERROR; + } else if (RETURN_WARNING == retVal) { + ComponentLog(comp, LOG_DEBUG, "Component specific DoStep returned with a warning"); } } @@ -648,7 +650,7 @@ static McxStatus AddObservableChannels(const Component * comp, StringContainer * for (i = 0; i < numIn; i++) { Channel * channel = (Channel *) DatabusGetInChannel(db, i); char * id = channel->info.id; - int isValid = DatabusChannelInIsValid(db, i); + int isValid = channel->IsConnected(channel) || channel->info.defaultValue; if (NULL != id && isValid) { StringContainerSetKeyValue(container, *count, id, channel); @@ -659,7 +661,7 @@ static McxStatus AddObservableChannels(const Component * comp, StringContainer * for (i = 0; i < numLocal; i++) { Channel * channel = (Channel *) DatabusGetLocalChannel(db, i); char * id = channel->info.id; - int isValid = DatabusChannelLocalIsValid(db, i); + int isValid = channel->ProvidesValue(channel); if (NULL != id && isValid) { StringContainerSetKeyValue(container, *count, id, channel); @@ -670,7 +672,7 @@ static McxStatus AddObservableChannels(const Component * comp, StringContainer * for (i = 0; i < numRTFactor; i++) { Channel * channel = (Channel *) DatabusGetRTFactorChannel(db, i); char * id = channel->info.id; - int isValid = DatabusChannelRTFactorIsValid(db, i); + int isValid = channel->ProvidesValue(channel); if (NULL != id && isValid) { StringContainerSetKeyValue(container, *count, id, channel); @@ -688,7 +690,7 @@ static size_t ComponentGetNumConnectedOutChannels(const Component * comp) { for (i = 0; (int) i < DatabusInfoGetChannelNum(DatabusGetOutInfo(comp->data->databus)); i++) { Channel * channel = (Channel *) DatabusGetOutChannel(comp->data->databus, i); - if (channel->IsValid(channel)) { + if (channel->IsConnected(channel)) { count++; } } @@ -838,22 +840,22 @@ static void ComponentSetModel(Component * comp, Model * model) { comp->data->model = model; } -ConnectionInfo * GetInConnectionInfo(const Component * comp, size_t channelID) { +Vector * GetInConnectionInfos(const Component * comp, size_t channelID) { size_t channelNum = DatabusInfoGetChannelNum(DatabusGetInInfo(comp->data->databus)); if (channelID < channelNum) { ChannelIn * in = DatabusGetInChannel(comp->data->databus, channelID); - return in->GetConnectionInfo(in); + return in->GetConnectionInfos(in); } return NULL; } -Connection * GetInConnection(const Component * comp, size_t channelID) { +ObjectContainer * GetInConnections(const Component * comp, size_t channelID) { size_t channelNum = DatabusInfoGetChannelNum(DatabusGetInInfo(comp->data->databus)); if (channelID < channelNum) { ChannelIn * in = DatabusGetInChannel(comp->data->databus, channelID); - return in->GetConnection(in); + return in->GetConnections(in); } return NULL; @@ -1290,6 +1292,7 @@ static Component * ComponentCreate(Component * comp) { comp->data->typeString = NULL; + StepTypeSynchronizationInit(&comp->syncHints); comp->GetNumObservableChannels = ComponentGetNumObservableChannels; comp->AddObservableChannels = AddObservableChannels; diff --git a/src/core/Component.h b/src/core/Component.h index 9d6f1cc..4893dae 100644 --- a/src/core/Component.h +++ b/src/core/Component.h @@ -16,8 +16,10 @@ #include "core/Component_interface.h" #include "core/Dependency.h" #include "objects/StringContainer.h" +#include "objects/Vector.h" #include "reader/model/components/ComponentInput.h" #include "core/connections/ConnectionInfo.h" +#include "steptypes/StepType.h" #ifdef __cplusplus extern "C" { @@ -227,6 +229,8 @@ struct Component { fOnConnectionsDone OnConnectionsDone; struct ComponentData * data; + + StepTypeSynchronization syncHints; }; /* these functions have to be called by subclasses */ @@ -250,8 +254,8 @@ McxStatus ComponentDoCommunicationStep(Component * comp, size_t group, struct St McxStatus ComponentEnterCommunicationPoint(Component * comp, TimeInterval * time); McxStatus ComponentEnterCommunicationPointForConnections(Component * comp, ObjectList * connections, TimeInterval * time); -ConnectionInfo * GetInConnectionInfo(const Component * comp, size_t channelID); -struct Connection * GetInConnection(const Component * comp, size_t channelID); +Vector * GetInConnectionInfos(const Component * comp, size_t channelID); +struct ObjectContainer * GetInConnections(const Component * comp, size_t channelID); size_t ComponentGetNumOutGroups(const Component * comp); size_t ComponentGetNumInitialOutGroups(const Component * comp); diff --git a/src/core/Config.c b/src/core/Config.c index 6842a0b..d94f3b5 100644 --- a/src/core/Config.c +++ b/src/core/Config.c @@ -416,6 +416,18 @@ static McxStatus ConfigSetupFromEnvironment(Config * config) { } } + { + char * wrongInitBehaviorDisabled = NULL; + + wrongInitBehaviorDisabled = mcx_os_get_env_var("MCX_WRONG_INIT_BEHAVIOR"); + if (wrongInitBehaviorDisabled) { + if (is_on(wrongInitBehaviorDisabled)) { + config->patchWrongInitBehavior = FALSE; + } + mcx_free(wrongInitBehaviorDisabled); + } + } + { char * numWarnings = mcx_os_get_env_var("NUM_TIME_SNAP_WARNINGS"); if (numWarnings) { @@ -571,6 +583,7 @@ static Config * ConfigCreate(Config * config) { config->writeAllLogFile = FALSE; config->cosimInitEnabled = FALSE; + config->patchWrongInitBehavior = TRUE; config->maxNumTimeSnapWarnings = MAX_NUM_MSGS; diff --git a/src/core/Config.h b/src/core/Config.h index 7cd97eb..6ae1643 100644 --- a/src/core/Config.h +++ b/src/core/Config.h @@ -72,6 +72,7 @@ struct Config { size_t memFilterHistoryExtra; int cosimInitEnabled; + int patchWrongInitBehavior; int profilingMode; diff --git a/src/core/Conversion.c b/src/core/Conversion.c index 886def6..6ccec92 100644 --- a/src/core/Conversion.c +++ b/src/core/Conversion.c @@ -10,6 +10,8 @@ #include "CentralParts.h" #include "core/Conversion.h" +#include "core/channels/ChannelValueReference.h" +#include "core/channels/ChannelValue.h" #include "units/Units.h" #ifdef __cplusplus @@ -34,15 +36,158 @@ OBJECT_CLASS(Conversion, Object); // ---------------------------------------------------------------------- // Range Conversion +static int RangeConversionElemwiseLeq(void * first, void * second, ChannelType * type) { + switch (type->con) { + case CHANNEL_DOUBLE: + return *(double *) first <= *(double *) second; + case CHANNEL_INTEGER: + return *(int *) first <= *(int *) second; + default: + return 0; + } +} + +static int RangeConversionElemwiseGeq(void * first, void * second, ChannelType * type) { + switch (type->con) { + case CHANNEL_DOUBLE: + return *(double *) first >= *(double *) second; + case CHANNEL_INTEGER: + return *(int *) first >= *(int *) second; + default: + return 0; + } +} + +static McxStatus RangeConversionConvert(Conversion * conversion, ChannelValue * value) { + RangeConversion * rangeConversion = (RangeConversion *) conversion; + McxStatus retVal = RETURN_OK; + + if (!ChannelTypeEq(ChannelValueType(value), rangeConversion->type)) { + mcx_log(LOG_ERROR, + "Range conversion: Value has wrong type %s, expected: %s", + ChannelTypeToString(ChannelValueType(value)), + ChannelTypeToString(rangeConversion->type)); + return RETURN_ERROR; + } + + if (rangeConversion->min) { + retVal = ChannelValueDataSetFromReferenceIfElemwisePred(&value->value, + value->type, + &rangeConversion->min->value, + RangeConversionElemwiseLeq); + if (RETURN_OK != retVal) { + mcx_log(LOG_ERROR, "Range conversion: Set value to min failed"); + return RETURN_ERROR; + } + } + + if (rangeConversion->max) { + retVal = ChannelValueDataSetFromReferenceIfElemwisePred(&value->value, + value->type, + &rangeConversion->max->value, + RangeConversionElemwiseGeq); + if (RETURN_OK != retVal) { + mcx_log(LOG_ERROR, "Range conversion: Set value to max failed"); + return RETURN_ERROR; + } + } + + return RETURN_OK; +} + +McxStatus RangeConversionMinValueRefConversion(void * element, size_t idx, ChannelType * type, void * ctx) { + ChannelValue * min = (ChannelValue *) ctx; + void * minElem = mcx_array_get_elem_reference(&min->value.a, idx); + if (RangeConversionElemwiseLeq(element, minElem, type)) { + switch (type->con) + { + case CHANNEL_DOUBLE: + { + double * elem = (double *) element; + *elem = *((double *)minElem); + break; + } + case CHANNEL_INTEGER: + { + int * elem = (int *) element; + *elem = *((int *)minElem); + break; + } + default: + mcx_log(LOG_ERROR, "RangeConversion: Unsupported type"); + return RETURN_ERROR; + } + } + + return RETURN_OK; +} + +McxStatus RangeConversionMaxValueRefConversion(void * element, size_t idx, ChannelType * type, void * ctx) { + ChannelValue * max = (ChannelValue *) ctx; + void * maxElem = mcx_array_get_elem_reference(&max->value.a, idx); + if (RangeConversionElemwiseGeq(element, maxElem, type)) { + switch (type->con) + { + case CHANNEL_DOUBLE: + { + double * elem = (double *) element; + *elem = *((double *)maxElem); + break; + } + case CHANNEL_INTEGER: + { + int * elem = (int *) element; + *elem = *((int *)maxElem); + break; + } + default: + mcx_log(LOG_ERROR, "RangeConversion: Unsupported type"); + return RETURN_ERROR; + } + } + + return RETURN_OK; +} + +static McxStatus RangeConversionConvertValueRef(RangeConversion * conversion, ChannelValueReference * ref) { + RangeConversion * rangeConversion = (RangeConversion *) conversion; + McxStatus retVal = RETURN_OK; + + if (!ChannelTypeEq(ChannelValueReferenceGetType(ref), rangeConversion->type)) { + mcx_log(LOG_ERROR, + "Range conversion: Value has wrong type %s, expected: %s", + ChannelTypeToString(ChannelValueReferenceGetType(ref)), + ChannelTypeToString(rangeConversion->type)); + return RETURN_ERROR; + } + + if (rangeConversion->min) { + retVal = ChannelValueReferenceElemMap(ref, RangeConversionMinValueRefConversion, rangeConversion->min); + if (RETURN_OK != retVal) { + mcx_log(LOG_ERROR, "Range conversion: Set value to min failed"); + return RETURN_ERROR; + } + } + + if (rangeConversion->max) { + retVal = ChannelValueReferenceElemMap(ref, RangeConversionMaxValueRefConversion, rangeConversion->max); + if (RETURN_OK != retVal) { + mcx_log(LOG_ERROR, "Range conversion: Set value to max failed"); + return RETURN_ERROR; + } + } + + return RETURN_OK; +} -McxStatus ConvertRange(ChannelValue * min, ChannelValue * max, ChannelValue * value) { +McxStatus ConvertRange(ChannelValue * min, ChannelValue * max, ChannelValue * value, ChannelDimension * slice) { RangeConversion * rangeConv = NULL; ChannelValue * minToUse = NULL; ChannelValue * maxToUse = NULL; McxStatus retVal = RETURN_OK; - if (value->type != CHANNEL_DOUBLE && value->type != CHANNEL_INTEGER) { + if (!ChannelTypeEq(ChannelTypeBaseType(value->type), &ChannelTypeDouble) && !ChannelTypeEq(ChannelTypeBaseType(value->type), &ChannelTypeInteger)) { return RETURN_OK; } @@ -80,9 +225,15 @@ McxStatus ConvertRange(ChannelValue * min, ChannelValue * max, ChannelValue * va } if (rangeConv) { - Conversion * conversion = (Conversion *) rangeConv; - retVal = conversion->convert(conversion, value); - if (RETURN_OK != retVal) { + if (slice) { + ChannelValueReference * ref = MakeChannelValueReference(value, slice); + retVal = RangeConversionConvertValueRef(rangeConv, ref); + DestroyChannelValueReference(ref); + } else { + retVal = RangeConversionConvert(rangeConv, value); + } + + if (retVal == RETURN_ERROR) { mcx_log(LOG_ERROR, "ConvertRange: Conversion failed"); goto cleanup; } @@ -102,39 +253,12 @@ McxStatus ConvertRange(ChannelValue * min, ChannelValue * max, ChannelValue * va return retVal; } -static McxStatus RangeConversionConvert(Conversion * conversion, ChannelValue * value) { - RangeConversion * rangeConversion = (RangeConversion *) conversion; - - McxStatus retVal = RETURN_OK; - - if (ChannelValueType(value) != rangeConversion->type) { - mcx_log(LOG_ERROR, "Range conversion: Value has wrong type %s, expected: %s", ChannelTypeToString(ChannelValueType(value)), ChannelTypeToString(rangeConversion->type)); - return RETURN_ERROR; - } - - if (rangeConversion->min && ChannelValueLeq(value, rangeConversion->min)) { - retVal = ChannelValueSet(value, rangeConversion->min); - if (RETURN_OK != retVal) { - mcx_log(LOG_ERROR, "Range conversion: Set value to min failed"); - return RETURN_ERROR; - } - } else if (rangeConversion->max && ChannelValueGeq(value, rangeConversion->max)) { - retVal = ChannelValueSet(value, rangeConversion->max); - if (RETURN_OK != retVal) { - mcx_log(LOG_ERROR, "Range conversion: Set value to max failed"); - return RETURN_ERROR; - } - } - - return RETURN_OK; -} - static McxStatus RangeConversionSetup(RangeConversion * conversion, ChannelValue * min, ChannelValue * max) { if (!min && !max) { return RETURN_OK; } - if (min && max && ChannelValueType(min) != ChannelValueType(max)) { + if (min && max && !ChannelTypeEq(ChannelValueType(min), ChannelValueType(max))) { mcx_log(LOG_ERROR, "Range conversion: Types of max value and min value do not match"); return RETURN_ERROR; } @@ -144,14 +268,11 @@ static McxStatus RangeConversionSetup(RangeConversion * conversion, ChannelValue return RETURN_ERROR; } - if (min) { - conversion->type = ChannelValueType(min); - } else { - conversion->type = ChannelValueType(max); - } + conversion->type = ChannelTypeClone(ChannelValueType(min ? min : max)); - if (!(conversion->type == CHANNEL_DOUBLE - || conversion->type == CHANNEL_INTEGER)) { + if (!(ChannelTypeEq(ChannelTypeBaseType(conversion->type), &ChannelTypeDouble) || + ChannelTypeEq(ChannelTypeBaseType(conversion->type), &ChannelTypeInteger))) + { mcx_log(LOG_ERROR, "Range conversion is not defined for type %s", ChannelTypeToString(conversion->type)); return RETURN_ERROR; } @@ -162,22 +283,49 @@ static McxStatus RangeConversionSetup(RangeConversion * conversion, ChannelValue return RETURN_OK; } +static int RangeConversionElementEqualsMin(void * element, ChannelType * type) { + switch (type->con) { + case CHANNEL_DOUBLE: + return *(double *) element == (-DBL_MAX); + case CHANNEL_INTEGER: + return *(int *) element == INT_MIN; + default: + return 0; + } +} + +static int RangeConversionElementEqualsMax(void * element, ChannelType * type) { + switch (type->con) { + case CHANNEL_DOUBLE: + return *(double *) element == DBL_MAX; + case CHANNEL_INTEGER: + return *(int *) element == INT_MAX; + default: + return 0; + } +} + static int RangeConversionIsEmpty(RangeConversion * conversion) { - switch (conversion->type) { - case CHANNEL_DOUBLE: - return - (!conversion->min || * (double *) ChannelValueReference(conversion->min) == (-DBL_MAX)) && - (!conversion->max || * (double *) ChannelValueReference(conversion->max) == DBL_MAX); - case CHANNEL_INTEGER: - return - (!conversion->min || * (int *) ChannelValueReference(conversion->min) == INT_MIN) && - (!conversion->max || * (int *) ChannelValueReference(conversion->max) == INT_MAX); - default: - return 1; + switch (conversion->type->con) { + case CHANNEL_DOUBLE: + return (!conversion->min || *(double *) ChannelValueDataPointer(conversion->min) == (-DBL_MAX)) && + (!conversion->max || *(double *) ChannelValueDataPointer(conversion->max) == DBL_MAX); + case CHANNEL_INTEGER: + return (!conversion->min || *(int *) ChannelValueDataPointer(conversion->min) == INT_MIN) && + (!conversion->max || *(int *) ChannelValueDataPointer(conversion->max) == INT_MAX); + case CHANNEL_ARRAY: + return (!conversion->min || mcx_array_all(&conversion->min->value.a, RangeConversionElementEqualsMin)) && + (!conversion->max || mcx_array_all(&conversion->max->value.a, RangeConversionElementEqualsMax)); + default: + return 1; } } static void RangeConversionDestructor(RangeConversion * rangeConversion) { + if (rangeConversion->type) { + ChannelTypeDestructor(rangeConversion->type); + } + if (rangeConversion->min) { mcx_free(rangeConversion->min); } @@ -194,7 +342,7 @@ static RangeConversion * RangeConversionCreate(RangeConversion * rangeConversion rangeConversion->Setup = RangeConversionSetup; rangeConversion->IsEmpty = RangeConversionIsEmpty; - rangeConversion->type = CHANNEL_UNKNOWN; + rangeConversion->type = &ChannelTypeUnknown; rangeConversion->min = NULL; rangeConversion->max = NULL; @@ -207,53 +355,18 @@ OBJECT_CLASS(RangeConversion, Conversion); // ---------------------------------------------------------------------- // Unit Conversion +static double UnitConversionConvertValue(UnitConversion * conversion, double value) { + value = (value + conversion->source.offset) * conversion->source.factor; + value = (value / conversion->target.factor) - conversion->target.offset; -McxStatus ConvertUnit(const char * fromUnit, const char * toUnit, ChannelValue * value) { - UnitConversion * unitConv = NULL; - - McxStatus retVal = RETURN_OK; - - unitConv = (UnitConversion *) object_create(UnitConversion); - if (!unitConv) { - mcx_log(LOG_ERROR, "ConvertUnit: Not enough memory"); - return RETURN_ERROR; - } - - retVal = unitConv->Setup(unitConv, fromUnit, toUnit); - if (RETURN_ERROR == retVal) { - mcx_log(LOG_ERROR, "ConvertUnit: Conversion setup failed"); - goto cleanup; - } - - if (unitConv->IsEmpty(unitConv)) { - object_destroy(unitConv); - } - - if (unitConv) { - Conversion * conv = (Conversion *) unitConv; - retVal = conv->convert(conv, value); - if (retVal == RETURN_ERROR) { - mcx_log(LOG_ERROR, "ConvertUnit: Conversion failed"); - goto cleanup; - } - } - -cleanup: - object_destroy(unitConv); - - return retVal; + return value; } static void UnitConversionConvertVector(UnitConversion * unitConversion, double * vector, size_t vectorLength) { size_t i; if (!unitConversion->IsEmpty(unitConversion)) { for (i = 0; i < vectorLength; i++) { - double val = vector[i]; - - val = (val + unitConversion->source.offset) * unitConversion->source.factor; - val = (val / unitConversion->target.factor) - unitConversion->target.offset; - - vector[i] = val; + vector[i] = UnitConversionConvertValue(unitConversion, vector[i]); } } } @@ -261,23 +374,53 @@ static void UnitConversionConvertVector(UnitConversion * unitConversion, double static McxStatus UnitConversionConvert(Conversion * conversion, ChannelValue * value) { UnitConversion * unitConversion = (UnitConversion *) conversion; - double val = 0.0; - - if (ChannelValueType(value) != CHANNEL_DOUBLE) { - mcx_log(LOG_ERROR, "Unit conversion: Value has wrong type %s, expected: %s", ChannelTypeToString(ChannelValueType(value)), ChannelTypeToString(CHANNEL_DOUBLE)); + if (!ChannelTypeEq(ChannelTypeBaseType(ChannelValueType(value)), &ChannelTypeDouble)) { + mcx_log(LOG_ERROR, "Unit conversion: Value has wrong type %s, expected: %s", ChannelTypeToString(ChannelValueType(value)), ChannelTypeToString(&ChannelTypeDouble)); return RETURN_ERROR; } - val = * (double *) ChannelValueReference(value); + if (ChannelTypeIsArray(value->type)) { + size_t i = 0; + + for (i = 0; i < mcx_array_num_elements(&value->value.a); i++) { + double * elem = (double *)mcx_array_get_elem_reference(&value->value.a, i); + if (!elem) { + return RETURN_ERROR; + } + + *elem = UnitConversionConvertValue(conversion, *elem); + } + } else { + double val = UnitConversionConvertValue(unitConversion, *(double *) ChannelValueDataPointer(value)); + if (RETURN_OK != ChannelValueSetFromReference(value, &val)) { + return RETURN_ERROR; + } + } + + return RETURN_OK; +} - val = (val + unitConversion->source.offset) * unitConversion->source.factor; - val = (val / unitConversion->target.factor) - unitConversion->target.offset; +McxStatus UnitConversionValueRefConversion(void * element, size_t idx, ChannelType * type, void * ctx) { + double * elem = (double *) element; + UnitConversion * conversion = (UnitConversion *) ctx; - ChannelValueSetFromReference(value, &val); + *elem = UnitConversionConvertValue(conversion, *elem); return RETURN_OK; } +static McxStatus UnitConversionConvertValueRef(UnitConversion * conversion, ChannelValueReference * ref) { + if (!ChannelTypeEq(ChannelTypeBaseType(ChannelValueReferenceGetType(ref)), &ChannelTypeDouble)) { + mcx_log(LOG_ERROR, + "Unit conversion: Value has wrong type %s, expected: %s", + ChannelTypeToString(ChannelTypeBaseType(ChannelValueReferenceGetType(ref))), + ChannelTypeToString(&ChannelTypeDouble)); + return RETURN_ERROR; + } + + return ChannelValueReferenceElemMap(ref, UnitConversionValueRefConversion, conversion); +} + static McxStatus UnitConversionSetup(UnitConversion * conversion, const char * fromUnit, const char * toUnit) { @@ -316,6 +459,48 @@ static int UnitConversionIsEmpty(UnitConversion * conversion) { || (conversion->target.factor == 0.0 && conversion->target.offset == 0.0); } +McxStatus ConvertUnit(const char * fromUnit, const char * toUnit, ChannelValue * value, ChannelDimension * slice) { + UnitConversion * unitConv = NULL; + + McxStatus retVal = RETURN_OK; + + unitConv = (UnitConversion *) object_create(UnitConversion); + if (!unitConv) { + mcx_log(LOG_ERROR, "ConvertUnit: Not enough memory"); + return RETURN_ERROR; + } + + retVal = unitConv->Setup(unitConv, fromUnit, toUnit); + if (RETURN_ERROR == retVal) { + mcx_log(LOG_ERROR, "ConvertUnit: Conversion setup failed"); + goto cleanup; + } + + if (unitConv->IsEmpty(unitConv)) { + object_destroy(unitConv); + } + + if (unitConv) { + if (slice) { + ChannelValueReference * ref = MakeChannelValueReference(value, slice); + retVal = UnitConversionConvertValueRef(unitConv, ref); + DestroyChannelValueReference(ref); + } else { + retVal = UnitConversionConvert(unitConv, value); + } + + if (retVal == RETURN_ERROR) { + mcx_log(LOG_ERROR, "ConvertUnit: Conversion failed"); + goto cleanup; + } + } + +cleanup: + object_destroy(unitConv); + + return retVal; +} + static void UnitConversionDestructor(UnitConversion * conversion) { } @@ -325,6 +510,7 @@ static UnitConversion * UnitConversionCreate(UnitConversion * unitConversion) { conversion->convert = UnitConversionConvert; unitConversion->convertVector = UnitConversionConvertVector; + unitConversion->ConvertValueReference = UnitConversionConvertValueRef; unitConversion->Setup = UnitConversionSetup; unitConversion->IsEmpty = UnitConversionIsEmpty; @@ -343,74 +529,6 @@ OBJECT_CLASS(UnitConversion, Conversion); // ---------------------------------------------------------------------- // Linear Conversion - -McxStatus ConvertLinear(ChannelValue * factor, ChannelValue * offset, ChannelValue * value) { - LinearConversion * linearConv = NULL; - ChannelValue * factorToUse = NULL; - ChannelValue * offsetToUse = NULL; - - McxStatus retVal = RETURN_OK; - - if (value->type != CHANNEL_DOUBLE && value->type != CHANNEL_INTEGER) { - return RETURN_OK; - } - - linearConv = (LinearConversion *) object_create(LinearConversion); - if (!linearConv) { - mcx_log(LOG_ERROR, "ConvertLinear: Not enough memory"); - return RETURN_ERROR; - } - - factorToUse = factor ? ChannelValueClone(factor) : NULL; - if (factor && !factorToUse) { - mcx_log(LOG_ERROR, "ConvertLinear: Not enough memory for factor"); - retVal = RETURN_ERROR; - goto cleanup; - } - - offsetToUse = offset ? ChannelValueClone(offset) : NULL; - if (offset && !offsetToUse) { - mcx_log(LOG_ERROR, "ConvertLinear: Not enough memory for offset"); - retVal = RETURN_ERROR; - goto cleanup; - } - - retVal = linearConv->Setup(linearConv, factorToUse, offsetToUse); - if (retVal == RETURN_ERROR) { - mcx_log(LOG_ERROR, "ConvertLinear: Conversion setup failed"); - goto cleanup; - } - - factorToUse = NULL; - offsetToUse = NULL; - - if (linearConv->IsEmpty(linearConv)) { - object_destroy(linearConv); - } - - if (linearConv) { - Conversion * conversion = (Conversion *) linearConv; - retVal = conversion->convert(conversion, value); - if (RETURN_OK != retVal) { - mcx_log(LOG_ERROR, "ConvertLinear: Conversion failed"); - goto cleanup; - } - } - -cleanup: - if (factorToUse) { - mcx_free(factorToUse); - } - - if (offsetToUse) { - mcx_free(offsetToUse); - } - - object_destroy(linearConv); - - return retVal; -} - static McxStatus LinearConversionConvert(Conversion * conversion, ChannelValue * value) { LinearConversion * linearConversion = (LinearConversion *) conversion; @@ -435,25 +553,108 @@ static McxStatus LinearConversionConvert(Conversion * conversion, ChannelValue * return RETURN_OK; } -static McxStatus LinearConversionSetup(LinearConversion * conversion, ChannelValue * factor, ChannelValue * offset) { +McxStatus LinearConversionScaleValueRefConversion(void * element, size_t idx, ChannelType * type, void * ctx) { + ChannelValue * factor = (ChannelValue *) ctx; + void * factorElem = mcx_array_get_elem_reference(&factor->value.a, idx); + + if (!ChannelTypeEq(type, factor->type)) { + mcx_log(LOG_ERROR, "Port: Scale: Mismatching types. Value type: %s, factor type: %s", + ChannelTypeToString(type), ChannelTypeToString(ChannelValueType(factor))); + return RETURN_ERROR; + } + + switch (type->con) { + case CHANNEL_DOUBLE: + { + double * elem = (double *) element; + *elem = *elem * *((double *)factorElem); + break; + } + case CHANNEL_INTEGER: + { + int * elem = (int *) element; + *elem = *elem * *((int *)factorElem); + break; + } + default: + mcx_log(LOG_ERROR, "Linear conversion: Unsupported type"); + return RETURN_ERROR; + } + + return RETURN_OK; +} + +McxStatus LinearConversionOffsetValueRefConversion(void * element, size_t idx, ChannelType * type, void * ctx) { + ChannelValue * offset = (ChannelValue *) ctx; + void * offsetElem = mcx_array_get_elem_reference(&offset->value.a, idx); + + if (!ChannelTypeEq(type, offset->type)) { + mcx_log(LOG_ERROR, "Port: Scale: Mismatching types. Value type: %s, factor type: %s", + ChannelTypeToString(type), ChannelTypeToString(ChannelValueType(offset))); + return RETURN_ERROR; + } + + switch (type->con) { + case CHANNEL_DOUBLE: + { + double * elem = (double *) element; + *elem = *elem + *((double *)offsetElem); + break; + } + case CHANNEL_INTEGER: + { + int * elem = (int *) element; + *elem = *elem + *((int *)offsetElem); + break; + } + default: + mcx_log(LOG_ERROR, "Linear conversion: Unsupported type"); + return RETURN_ERROR; + } + + return RETURN_OK; +} + +static McxStatus LinearConversionConvertValueRef(LinearConversion * linearConversion, ChannelValueReference * ref) { + McxStatus retVal = RETURN_OK; + + if (linearConversion->factor) { + retVal = ChannelValueReferenceElemMap(ref, LinearConversionScaleValueRefConversion, linearConversion->factor); + if (RETURN_OK != retVal) { + mcx_log(LOG_ERROR, "Linear conversion: Port value scaling failed"); + return RETURN_ERROR; + } + } + + if (linearConversion->offset) { + retVal = ChannelValueReferenceElemMap(ref, LinearConversionOffsetValueRefConversion, linearConversion->offset); + if (RETURN_OK != retVal) { + mcx_log(LOG_ERROR, "Linear conversion: Adding offset failed"); + return RETURN_ERROR; + } + } + + return RETURN_OK; +} + +static McxStatus LinearConversionSetup(LinearConversion * conversion, ChannelValue * factor, ChannelValue * offset) { if (!factor && !offset) { return RETURN_OK; } - if (factor && offset && ChannelValueType(factor) != ChannelValueType(offset)) { - mcx_log(LOG_WARNING, "Linear conversion: Types of factor value (%s) and offset value (%s) do not match", - ChannelTypeToString(ChannelValueType(factor)), ChannelTypeToString(ChannelValueType(offset))); + if (factor && offset && !ChannelTypeEq(ChannelValueType(factor), ChannelValueType(offset))) { + mcx_log(LOG_WARNING, + "Linear conversion: Types of factor value (%s) and offset value (%s) do not match", + ChannelTypeToString(ChannelValueType(factor)), + ChannelTypeToString(ChannelValueType(offset))); return RETURN_ERROR; } - if (factor) { - conversion->type = ChannelValueType(factor); - } else { - conversion->type = ChannelValueType(offset); - } + conversion->type = ChannelTypeClone(ChannelValueType(factor ? factor : offset)); - if (!(conversion->type == CHANNEL_DOUBLE - || conversion->type == CHANNEL_INTEGER)) { + if (!(ChannelTypeEq(ChannelTypeBaseType(conversion->type), &ChannelTypeDouble) || + ChannelTypeEq(ChannelTypeBaseType(conversion->type), &ChannelTypeInteger))) + { mcx_log(LOG_WARNING, "Linear conversion is not defined for type %s", ChannelTypeToString(conversion->type)); return RETURN_ERROR; } @@ -464,22 +665,124 @@ static McxStatus LinearConversionSetup(LinearConversion * conversion, ChannelVal return RETURN_OK; } +static int LinearConversionElementEqualsOne(void* element, ChannelType* type) { + switch (type->con) { + case CHANNEL_DOUBLE: + return *(double *) element == 1.0; + case CHANNEL_INTEGER: + return *(int *) element == 1; + default: + return 0; + } +} + +static int LinearConversionElementEqualsZero(void * element, ChannelType * type) { + switch (type->con) { + case CHANNEL_DOUBLE: + return *(double *) element == 0.0; + case CHANNEL_INTEGER: + return *(int *) element == 0; + default: + return 0; + } +} + static int LinearConversionIsEmpty(LinearConversion * conversion) { - switch (conversion->type) { + switch (conversion->type->con) { case CHANNEL_DOUBLE: return - (!conversion->factor || * (double *) ChannelValueReference(conversion->factor) == 1.0) && - (!conversion->offset || * (double *) ChannelValueReference(conversion->offset) == 0.0); + (!conversion->factor || * (double *) ChannelValueDataPointer(conversion->factor) == 1.0) && + (!conversion->offset || * (double *) ChannelValueDataPointer(conversion->offset) == 0.0); case CHANNEL_INTEGER: return - (!conversion->factor || * (int *) ChannelValueReference(conversion->factor) == 1) && - (!conversion->offset || * (int *) ChannelValueReference(conversion->offset) == 0); + (!conversion->factor || * (int *) ChannelValueDataPointer(conversion->factor) == 1) && + (!conversion->offset || * (int *) ChannelValueDataPointer(conversion->offset) == 0); + case CHANNEL_ARRAY: + return (!conversion->factor || mcx_array_all(&conversion->factor->value.a, LinearConversionElementEqualsOne)) && + (!conversion->offset || mcx_array_all(&conversion->offset->value.a, LinearConversionElementEqualsZero)); default: return 1; } } +McxStatus ConvertLinear(ChannelValue * factor, ChannelValue * offset, ChannelValue * value, ChannelDimension * slice) { + LinearConversion * linearConv = NULL; + ChannelValue * factorToUse = NULL; + ChannelValue * offsetToUse = NULL; + + McxStatus retVal = RETURN_OK; + + if (!ChannelTypeEq(ChannelTypeBaseType(value->type), &ChannelTypeDouble) && !ChannelTypeEq(ChannelTypeBaseType(value->type), &ChannelTypeInteger)) { + return RETURN_OK; + } + + linearConv = (LinearConversion *) object_create(LinearConversion); + if (!linearConv) { + mcx_log(LOG_ERROR, "ConvertLinear: Not enough memory"); + return RETURN_ERROR; + } + + factorToUse = factor ? ChannelValueClone(factor) : NULL; + if (factor && !factorToUse) { + mcx_log(LOG_ERROR, "ConvertLinear: Not enough memory for factor"); + retVal = RETURN_ERROR; + goto cleanup; + } + + offsetToUse = offset ? ChannelValueClone(offset) : NULL; + if (offset && !offsetToUse) { + mcx_log(LOG_ERROR, "ConvertLinear: Not enough memory for offset"); + retVal = RETURN_ERROR; + goto cleanup; + } + + retVal = linearConv->Setup(linearConv, factorToUse, offsetToUse); + if (retVal == RETURN_ERROR) { + mcx_log(LOG_ERROR, "ConvertLinear: Conversion setup failed"); + goto cleanup; + } + + factorToUse = NULL; + offsetToUse = NULL; + + if (linearConv->IsEmpty(linearConv)) { + object_destroy(linearConv); + } + + if (linearConv) { + if (slice) { + ChannelValueReference * ref = MakeChannelValueReference(value, slice); + retVal = LinearConversionConvertValueRef(linearConv, ref); + DestroyChannelValueReference(ref); + } else { + retVal = LinearConversionConvert(linearConv, value); + } + + if (RETURN_OK != retVal) { + mcx_log(LOG_ERROR, "ConvertLinear: Conversion failed"); + goto cleanup; + } + } + +cleanup: + if (factorToUse) { + mcx_free(factorToUse); + } + + if (offsetToUse) { + mcx_free(offsetToUse); + } + + object_destroy(linearConv); + + return retVal; +} + static void LinearConversionDestructor(LinearConversion * linearConversion) { + if (linearConversion->type) { + ChannelTypeDestructor(linearConversion->type); + } + if (linearConversion->factor) { mcx_free(linearConversion->factor); } @@ -495,7 +798,7 @@ static LinearConversion * LinearConversionCreate(LinearConversion * linearConver linearConversion->Setup = LinearConversionSetup; linearConversion->IsEmpty = LinearConversionIsEmpty; - linearConversion->type = CHANNEL_UNKNOWN; + linearConversion->type = &ChannelTypeUnknown; linearConversion->factor = NULL; linearConversion->offset = NULL; @@ -508,164 +811,725 @@ OBJECT_CLASS(LinearConversion, Conversion); // ---------------------------------------------------------------------- // Type Conversion - -McxStatus ConvertType(ChannelType fromType, ChannelType toType, ChannelValue * value) { +McxStatus ConvertType(ChannelValue * dest, ChannelDimension * destSlice, ChannelValue * src, ChannelDimension * srcSlice) { TypeConversion * typeConv = NULL; - Conversion * conv = NULL; + ChannelValueReference * ref = NULL; McxStatus retVal = RETURN_OK; typeConv = (TypeConversion *) object_create(TypeConversion); if (!typeConv) { - mcx_log(LOG_ERROR, "ConvertType: Not enough memory"); + mcx_log(LOG_ERROR, "ConvertType: Converter allocation failed"); return RETURN_ERROR; } - conv = (Conversion *) typeConv; - - retVal = typeConv->Setup(typeConv, fromType, toType); + retVal = typeConv->Setup(typeConv, src->type, srcSlice, dest->type, destSlice); if (retVal == RETURN_ERROR) { - mcx_log(LOG_ERROR, "ConvertType: Conversion setup failed"); + mcx_log(LOG_ERROR, "ConvertType: Setup failed"); goto cleanup; } - retVal = conv->convert(conv, value); - if (retVal == RETURN_ERROR) { - mcx_log(LOG_ERROR, "Type conversion failed"); + ref = MakeChannelValueReference(dest, destSlice); + if (!ref) { + mcx_log(LOG_ERROR, "ConvertType: Value reference allocation failed"); goto cleanup; } + ChannelValueReferenceSetFromPointer(ref, ChannelValueDataPointer(src), srcSlice, typeConv); + cleanup: object_destroy(typeConv); + DestroyChannelValueReference(ref); return retVal; } -static McxStatus TypeConversionConvertIntDouble(Conversion * conversion, ChannelValue * value) { - if (ChannelValueType(value) != CHANNEL_INTEGER) { - mcx_log(LOG_ERROR, "Type conversion: Value has wrong type %s, expected: %s", ChannelTypeToString(ChannelValueType(value)), ChannelTypeToString(CHANNEL_INTEGER)); +static McxStatus CheckTypesValidForConversion(ChannelType * destType, + ChannelType * expectedDestType) +{ + if (!ChannelTypeEq(destType, expectedDestType)) { + mcx_log(LOG_ERROR, + "Type conversion: Destination value has wrong type %s, expected: %s", + ChannelTypeToString(destType), + ChannelTypeToString(expectedDestType)); + return RETURN_ERROR; + } + + return RETURN_OK; +} + +static McxStatus CheckArrayReferencingOnlyOneElement(ChannelValueReference * dest, ChannelType * expectedDestType) { + if (!ChannelTypeIsArray(ChannelValueReferenceGetType(dest))) { + mcx_log(LOG_ERROR, "Type conversion: Destination value is not an array"); + return RETURN_ERROR; + } + + if (RETURN_ERROR == CheckTypesValidForConversion(ChannelTypeBaseType(ChannelValueReferenceGetType(dest)), expectedDestType)) { + return RETURN_ERROR; + } + + // check only one element referenced + if (dest->type == CHANNEL_VALUE_REF_VALUE) { + if (ChannelTypeNumElements(dest->ref.value->type) != 1) { + mcx_log(LOG_ERROR, "Type conversion: Destination value is not an array of size 1"); + return RETURN_ERROR; + } + } else { + if (ChannelDimensionNumElements(dest->ref.slice.dimension) != 1) { + mcx_log(LOG_ERROR, "Type conversion: Destination value is not an array of size 1"); + return RETURN_ERROR; + } + } + + return RETURN_OK; +} + +static McxStatus ArraysValidForConversion(ChannelValueReference * dest, + ChannelType * expectedDestType, + mcx_array * src, + ChannelDimension * srcDimension) { + if (!ChannelTypeIsArray(ChannelValueReferenceGetType(dest))) { + mcx_log(LOG_ERROR, "Type conversion: Destination value is not an array"); + return RETURN_ERROR; + } + + if (RETURN_ERROR == CheckTypesValidForConversion(ChannelTypeBaseType(ChannelValueReferenceGetType(dest)), expectedDestType)) { + return RETURN_ERROR; + } + + // check dimensions match + if (srcDimension) { + if (dest->type == CHANNEL_VALUE_REF_VALUE) { + return ChannelDimensionConformsTo(srcDimension, dest->ref.value->type->ty.a.dims, dest->ref.value->type->ty.a.numDims) ? RETURN_OK : RETURN_ERROR; + } else { + return ChannelDimensionConformsToDimension(dest->ref.slice.dimension, srcDimension) ? RETURN_OK : RETURN_ERROR; + } + } else { + if (dest->type == CHANNEL_VALUE_REF_VALUE) { + return mcx_array_dims_match(&dest->ref.value->value.a, src) ? RETURN_OK : RETURN_ERROR; + } else { + return ChannelDimensionConformsTo(dest->ref.slice.dimension, src->dims, src->numDims) ? RETURN_OK : RETURN_ERROR; + } + } + + return RETURN_OK; +} + +static McxStatus TypeConversionConvertIntDouble(Conversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckTypesValidForConversion(ChannelValueReferenceGetType(dest), &ChannelTypeDouble)) { return RETURN_ERROR; } - value->type = CHANNEL_DOUBLE; - value->value.d = (double)value->value.i; + dest->ref.value->value.d = (double) *((int *) src); return RETURN_OK; } -static McxStatus TypeConversionConvertDoubleInt(Conversion * conversion, ChannelValue * value) { - if (ChannelValueType(value) != CHANNEL_DOUBLE) { - mcx_log(LOG_ERROR, "Type conversion: Value has wrong type %s, expected: %s", ChannelTypeToString(ChannelValueType(value)), ChannelTypeToString(CHANNEL_DOUBLE)); +static McxStatus TypeConversionConvertDoubleInt(Conversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckTypesValidForConversion(ChannelValueReferenceGetType(dest), &ChannelTypeInteger)) { return RETURN_ERROR; } - value->type = CHANNEL_INTEGER; - value->value.i = (int)floor(value->value.d + 0.5); + dest->ref.value->value.i = (int) floor(*((double *) src) + 0.5); return RETURN_OK; } -static McxStatus TypeConversionConvertBoolDouble(Conversion * conversion, ChannelValue * value) { - if (ChannelValueType(value) != CHANNEL_BOOL) { - mcx_log(LOG_ERROR, "Type conversion: Value has wrong type %s, expected: %s", ChannelTypeToString(ChannelValueType(value)), ChannelTypeToString(CHANNEL_BOOL)); +static McxStatus TypeConversionConvertBoolDouble(Conversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckTypesValidForConversion(ChannelValueReferenceGetType(dest), &ChannelTypeDouble)) { return RETURN_ERROR; } - value->type = CHANNEL_DOUBLE; - value->value.d = (value->value.i != 0) ? 1. : 0.; + dest->ref.value->value.d = *((int *) src) != 0 ? 1. : 0.; return RETURN_OK; } -static McxStatus TypeConversionConvertDoubleBool(Conversion * conversion, ChannelValue * value) { - if (ChannelValueType(value) != CHANNEL_DOUBLE) { - mcx_log(LOG_ERROR, "Type conversion: Value has wrong type %s, expected: %s", ChannelTypeToString(ChannelValueType(value)), ChannelTypeToString(CHANNEL_DOUBLE)); +static McxStatus TypeConversionConvertDoubleBool(Conversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckTypesValidForConversion(ChannelValueReferenceGetType(dest), &ChannelTypeBool)) { return RETURN_ERROR; } - value->type = CHANNEL_BOOL; - value->value.i = (value->value.d > 0) ? 1 : 0; + dest->ref.value->value.i = *((double *) src) > 0 ? 1 : 0; return RETURN_OK; } -static McxStatus TypeConversionConvertBoolInteger(Conversion * conversion, ChannelValue * value) { - if (ChannelValueType(value) != CHANNEL_BOOL) { - mcx_log(LOG_ERROR, "Type conversion: Value has wrong type %s, expected: %s", ChannelTypeToString(ChannelValueType(value)), ChannelTypeToString(CHANNEL_BOOL)); +static McxStatus TypeConversionConvertBoolInteger(Conversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckTypesValidForConversion(ChannelValueReferenceGetType(dest), &ChannelTypeInteger)) { return RETURN_ERROR; } - value->type = CHANNEL_INTEGER; - value->value.i = (value->value.i != 0) ? 1 : 0; + dest->ref.value->value.i = *((int *) src) != 0 ? 1 : 0; return RETURN_OK; } -static McxStatus TypeConversionConvertIntegerBool(Conversion * conversion, ChannelValue * value) { - if (ChannelValueType(value) != CHANNEL_INTEGER) { - mcx_log(LOG_ERROR, "Type conversion: Value has wrong type %s, expected: %s", ChannelTypeToString(ChannelValueType(value)), ChannelTypeToString(CHANNEL_INTEGER)); +static McxStatus TypeConversionConvertIntegerBool(Conversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckTypesValidForConversion(ChannelValueReferenceGetType(dest), &ChannelTypeBool)) { return RETURN_ERROR; } - value->type = CHANNEL_BOOL; - value->value.i = (value->value.i != 0) ? 1 : 0; + dest->ref.value->value.i = *((int *) src) != 0 ? 1 : 0; return RETURN_OK; } -static McxStatus TypeConversionConvertId(Conversion * conversion, ChannelValue * value) { +static McxStatus TypeConversionConvertArrayDoubleToDouble(TypeConversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckTypesValidForConversion(ChannelValueReferenceGetType(dest), &ChannelTypeDouble)) { + return RETURN_ERROR; + } + + if (conversion->sourceSlice) { + dest->ref.value->value.d = *((double *) ((mcx_array *) src)->data + conversion->sourceSlice->startIdxs[0]); + } else { + dest->ref.value->value.d = *(double *) ((mcx_array *) src)->data; + } + return RETURN_OK; } -static McxStatus TypeConversionSetup(TypeConversion * typeConversion, - ChannelType fromType, - ChannelType toType) { - Conversion * conversion = (Conversion *) typeConversion; +static McxStatus TypeConversionConvertArrayIntegerToDouble(TypeConversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckTypesValidForConversion(ChannelValueReferenceGetType(dest), &ChannelTypeDouble)) { + return RETURN_ERROR; + } - if (fromType == toType) { - conversion->convert = TypeConversionConvertId; - } else if (fromType == CHANNEL_INTEGER && toType == CHANNEL_DOUBLE) { - conversion->convert = TypeConversionConvertIntDouble; - } else if (fromType == CHANNEL_DOUBLE && toType == CHANNEL_INTEGER) { - conversion->convert = TypeConversionConvertDoubleInt; - } else if (fromType == CHANNEL_BOOL && toType == CHANNEL_DOUBLE) { - conversion->convert = TypeConversionConvertBoolDouble; - } else if (fromType == CHANNEL_DOUBLE && toType == CHANNEL_BOOL) { - conversion->convert = TypeConversionConvertDoubleBool; - } else if (fromType == CHANNEL_BOOL && toType == CHANNEL_INTEGER) { - conversion->convert = TypeConversionConvertBoolInteger; - } else if (fromType == CHANNEL_INTEGER && toType == CHANNEL_BOOL) { - conversion->convert = TypeConversionConvertIntegerBool; + if (conversion->sourceSlice) { + dest->ref.value->value.d = (double) *((int *) ((mcx_array *) src)->data + conversion->sourceSlice->startIdxs[0]); } else { - mcx_log(LOG_ERROR, "Setup type conversion: Illegal conversion selected"); + dest->ref.value->value.d = (double) *(int *) ((mcx_array *) src)->data; + } + + return RETURN_OK; +} + +static McxStatus TypeConversionConvertArrayBoolToDouble(TypeConversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckTypesValidForConversion(ChannelValueReferenceGetType(dest), &ChannelTypeDouble)) { + return RETURN_ERROR; + } + + if (conversion->sourceSlice) { + dest->ref.value->value.d = *((int *) ((mcx_array *) src)->data + conversion->sourceSlice->startIdxs[0]) != 0 ? 1. : 0.; + } else { + dest->ref.value->value.d = *(int *) ((mcx_array *) src)->data != 0 ? 1. : 0.; + } + + return RETURN_OK; +} + +static McxStatus TypeConversionConvertArrayDoubleToInteger(TypeConversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckTypesValidForConversion(ChannelValueReferenceGetType(dest), &ChannelTypeInteger)) { return RETURN_ERROR; } + if (conversion->sourceSlice) { + dest->ref.value->value.i = (int) floor(*((double *) ((mcx_array *) src)->data + conversion->sourceSlice->startIdxs[0]) + 0.5); + } else { + dest->ref.value->value.i = (int) floor(*(double *) ((mcx_array *) src)->data + 0.5); + } + return RETURN_OK; } -static int TypeConversionIsEmpty(TypeConversion * typeConversion) { - Conversion * conversion = (Conversion *) typeConversion; +static McxStatus TypeConversionConvertArrayIntegerToInteger(TypeConversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckTypesValidForConversion(ChannelValueReferenceGetType(dest), &ChannelTypeInteger)) { + return RETURN_ERROR; + } - return (conversion->convert == TypeConversionConvertId); + if (conversion->sourceSlice) { + dest->ref.value->value.i = *((int *) ((mcx_array *) src)->data + conversion->sourceSlice->startIdxs[0]); + } else { + dest->ref.value->value.i = *(int *) ((mcx_array *) src)->data; + } + + return RETURN_OK; } -static void TypeConversionDestructor(TypeConversion * conversion) { +static McxStatus TypeConversionConvertArrayBoolToInteger(TypeConversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckTypesValidForConversion(ChannelValueReferenceGetType(dest), &ChannelTypeInteger)) { + return RETURN_ERROR; + } + + if (conversion->sourceSlice) { + dest->ref.value->value.i = *((int *) ((mcx_array *) src)->data + conversion->sourceSlice->startIdxs[0]) != 0 ? 1 : 0; + } else { + dest->ref.value->value.i = *(int *) ((mcx_array *) src)->data != 0 ? 1 : 0; + } + return RETURN_OK; } -static TypeConversion * TypeConversionCreate(TypeConversion * typeConversion) { - Conversion * conversion = (Conversion *) typeConversion; +static McxStatus TypeConversionConvertArrayDoubleToBool(TypeConversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckTypesValidForConversion(ChannelValueReferenceGetType(dest), &ChannelTypeBool)) { + return RETURN_ERROR; + } - conversion->convert = TypeConversionConvertId; + if (conversion->sourceSlice) { + dest->ref.value->value.i = *((double *) ((mcx_array *) src)->data + conversion->sourceSlice->startIdxs[0]) > 0 ? 1 : 0; + } else { + dest->ref.value->value.i = *(double *) ((mcx_array *) src)->data > 0 ? 1 : 0; + } - typeConversion->Setup = TypeConversionSetup; - typeConversion->IsEmpty = TypeConversionIsEmpty; + return RETURN_OK; +} + +static McxStatus TypeConversionConvertArrayIntegerToBool(TypeConversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckTypesValidForConversion(ChannelValueReferenceGetType(dest), &ChannelTypeBool)) { + return RETURN_ERROR; + } - return typeConversion; + if (conversion->sourceSlice) { + dest->ref.value->value.i = *((int *) ((mcx_array *) src)->data + conversion->sourceSlice->startIdxs[0]) != 0 ? 1 : 0; + } else { + dest->ref.value->value.i = *(int *) ((mcx_array *) src)->data != 0 ? 1 : 0; + } + + return RETURN_OK; +} + +static McxStatus TypeConversionConvertArrayBoolToBool(TypeConversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckTypesValidForConversion(ChannelValueReferenceGetType(dest), &ChannelTypeBool)) { + return RETURN_ERROR; + } + + if (conversion->sourceSlice) { + dest->ref.value->value.i = *((int *) ((mcx_array *) src)->data + conversion->sourceSlice->startIdxs[0]); + } else { + dest->ref.value->value.i = *(int *) ((mcx_array *) src)->data; + } + + return RETURN_OK; +} + +static McxStatus TypeConversionConvertDoubleToArrayDouble(Conversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckArrayReferencingOnlyOneElement(dest, &ChannelTypeDouble)) { + return RETURN_ERROR; + } + + if (dest->type == CHANNEL_VALUE_REF_VALUE) { + *(double *) dest->ref.value->value.a.data = *(double *) src; + } else { + double * data = dest->ref.slice.ref->value.a.data; + *(data + dest->ref.slice.dimension->startIdxs[0]) = *(double *) src; + } + + return RETURN_OK; +} + +static McxStatus TypeConversionConvertIntegerToArrayDouble(Conversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckArrayReferencingOnlyOneElement(dest, &ChannelTypeDouble)) { + return RETURN_ERROR; + } + + if (dest->type == CHANNEL_VALUE_REF_VALUE) { + *(double *) dest->ref.value->value.a.data = (double) *((int *) src); + } else { + double * data = dest->ref.slice.ref->value.a.data; + *(data + dest->ref.slice.dimension->startIdxs[0]) = (double) *((int *) src); + } + + return RETURN_OK; +} + +static McxStatus TypeConversionConvertBoolToArrayDouble(Conversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckArrayReferencingOnlyOneElement(dest, &ChannelTypeDouble)) { + return RETURN_ERROR; + } + + if (dest->type == CHANNEL_VALUE_REF_VALUE) { + *(double *) dest->ref.value->value.a.data = *((int *) src) != 0 ? 1. : 0.; + } else { + double * data = dest->ref.slice.ref->value.a.data; + *(data + dest->ref.slice.dimension->startIdxs[0]) = *((int *) src) != 0 ? 1. : 0.; + } + + return RETURN_OK; +} + +static McxStatus TypeConversionConvertDoubleToArrayInteger(Conversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckArrayReferencingOnlyOneElement(dest, &ChannelTypeInteger)) { + return RETURN_ERROR; + } + + if (dest->type == CHANNEL_VALUE_REF_VALUE) { + *(int *) dest->ref.value->value.a.data = (int) floor(*((double *) src) + 0.5); + } else { + int * data = dest->ref.slice.ref->value.a.data; + *(data + dest->ref.slice.dimension->startIdxs[0]) = (int) floor(*((double *) src) + 0.5); + } + + return RETURN_OK; +} + +static McxStatus TypeConversionConvertIntegerToArrayInteger(Conversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckArrayReferencingOnlyOneElement(dest, &ChannelTypeInteger)) { + return RETURN_ERROR; + } + + if (dest->type == CHANNEL_VALUE_REF_VALUE) { + *(int *) dest->ref.value->value.a.data = *(int *) src; + } else { + int * data = dest->ref.slice.ref->value.a.data; + *(data + dest->ref.slice.dimension->startIdxs[0]) = *(int *) src; + } + + return RETURN_OK; +} + +static McxStatus TypeConversionConvertBoolToArrayInteger(Conversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckArrayReferencingOnlyOneElement(dest, &ChannelTypeInteger)) { + return RETURN_ERROR; + } + + if (dest->type == CHANNEL_VALUE_REF_VALUE) { + *(int *) dest->ref.value->value.a.data = *((int *) src) != 0 ? 1 : 0; + } else { + int * data = dest->ref.slice.ref->value.a.data; + *(data + dest->ref.slice.dimension->startIdxs[0]) = *((int *) src) != 0 ? 1 : 0; + } + + return RETURN_OK; +} + +static McxStatus TypeConversionConvertDoubleToArrayBool(Conversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckArrayReferencingOnlyOneElement(dest, &ChannelTypeBool)) { + return RETURN_ERROR; + } + + if (dest->type == CHANNEL_VALUE_REF_VALUE) { + *(int *) dest->ref.value->value.a.data = *((double *) src) > 0 ? 1 : 0; + } else { + int * data = dest->ref.slice.ref->value.a.data; + *(data + dest->ref.slice.dimension->startIdxs[0]) = *((double *) src) > 0 ? 1 : 0; + } + + return RETURN_OK; +} + +static McxStatus TypeConversionConvertIntegerToArrayBool(Conversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckArrayReferencingOnlyOneElement(dest, &ChannelTypeBool)) { + return RETURN_ERROR; + } + + if (dest->type == CHANNEL_VALUE_REF_VALUE) { + *(int *) dest->ref.value->value.a.data = *((int *) src) != 0 ? 1 : 0; + } else { + int * data = dest->ref.slice.ref->value.a.data; + *(data + dest->ref.slice.dimension->startIdxs[0]) = *((int *) src) != 0 ? 1 : 0; + } + + return RETURN_OK; +} + +static McxStatus TypeConversionConvertBoolToArrayBool(Conversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_ERROR == CheckArrayReferencingOnlyOneElement(dest, &ChannelTypeBool)) { + return RETURN_ERROR; + } + + if (dest->type == CHANNEL_VALUE_REF_VALUE) { + *(int *) dest->ref.value->value.a.data = *(int *) src; + } else { + int * data = dest->ref.slice.ref->value.a.data; + *(data + dest->ref.slice.dimension->startIdxs[0]) = *(int *) src; + } + + return RETURN_OK; +} + +static size_t IndexOfElemInSrcArray(size_t dest_idx, mcx_array * src_array, ChannelDimension * src_dim, ChannelValueReference * dest) { + if (src_dim) { + if (dest->type == CHANNEL_VALUE_REF_VALUE) { + return ChannelDimensionGetIndex(src_dim, dest_idx, src_array->dims); + } else { + size_t idx = ChannelDimensionGetSliceIndex(dest->ref.slice.dimension, dest_idx, dest->ref.slice.ref->type->ty.a.dims); + return ChannelDimensionGetIndex(src_dim, idx, src_array->dims); + } + } else { + if (dest->type == CHANNEL_VALUE_REF_VALUE) { + return dest_idx; + } else { + return ChannelDimensionGetSliceIndex(dest->ref.slice.dimension, dest_idx, dest->ref.slice.ref->type->ty.a.dims); + } + } +} + +typedef struct Array2ArrayCtx { + mcx_array * src_array; + ChannelDimension * src_dim; + ChannelValueReference * dest; +} Array2ArrayCtx; + +static McxStatus IntegerArrayToDouble(void * element, size_t idx, ChannelType * type, void * ctx) { + Array2ArrayCtx * context = (Array2ArrayCtx *) ctx; + size_t i = IndexOfElemInSrcArray(idx, context->src_array, context->src_dim, context->dest); + void * src_elem = mcx_array_get_elem_reference(context->src_array, i); + + if (!src_elem) { + return RETURN_ERROR; + } + + *(double *) element = (double) *((int *) src_elem); + + return RETURN_OK; +} + +static McxStatus BoolArrayToDouble(void * element, size_t idx, ChannelType * type, void * ctx) { + Array2ArrayCtx * context = (Array2ArrayCtx *) ctx; + size_t i = IndexOfElemInSrcArray(idx, context->src_array, context->src_dim, context->dest); + void * src_elem = mcx_array_get_elem_reference(context->src_array, i); + + if (!src_elem) { + return RETURN_ERROR; + } + + *(double *) element = *((int *) src_elem) != 0 ? 1. : 0.; + + return RETURN_OK; +} + +static McxStatus BoolArrayToInteger(void * element, size_t idx, ChannelType * type, void * ctx) { + Array2ArrayCtx * context = (Array2ArrayCtx *) ctx; + size_t i = IndexOfElemInSrcArray(idx, context->src_array, context->src_dim, context->dest); + void * src_elem = mcx_array_get_elem_reference(context->src_array, i); + + if (!src_elem) { + return RETURN_ERROR; + } + + *(int *) element = *((int *) src_elem) != 0 ? 1 : 0; + + return RETURN_OK; +} + +static McxStatus DoubleArrayToInteger(void * element, size_t idx, ChannelType * type, void * ctx) { + Array2ArrayCtx * context = (Array2ArrayCtx *) ctx; + size_t i = IndexOfElemInSrcArray(idx, context->src_array, context->src_dim, context->dest); + void * src_elem = mcx_array_get_elem_reference(context->src_array, i); + + if (!src_elem) { + return RETURN_ERROR; + } + + *(int *) element = (int) floor(*((double *) src_elem) + 0.5); + + return RETURN_OK; +} + +static McxStatus DoubleArrayToBool(void * element, size_t idx, ChannelType * type, void * ctx) { + Array2ArrayCtx * context = (Array2ArrayCtx *) ctx; + size_t i = IndexOfElemInSrcArray(idx, context->src_array, context->src_dim, context->dest); + void * src_elem = mcx_array_get_elem_reference(context->src_array, i); + + if (!src_elem) { + return RETURN_ERROR; + } + + *(int *) element = *((double *) src_elem) > 0 ? 1 : 0; + + return RETURN_OK; +} + +static McxStatus IntegerArrayToBool(void * element, size_t idx, ChannelType * type, void * ctx) { + Array2ArrayCtx * context = (Array2ArrayCtx *) ctx; + size_t i = IndexOfElemInSrcArray(idx, context->src_array, context->src_dim, context->dest); + void * src_elem = mcx_array_get_elem_reference(context->src_array, i); + + if (!src_elem) { + return RETURN_ERROR; + } + + *(int *) element = *((int *) src_elem) != 0 ? 1 : 0; + + return RETURN_OK; +} + +static McxStatus TypeConversionConvertArrayIntegerToArrayDouble(TypeConversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_OK != ArraysValidForConversion(dest, &ChannelTypeDouble, (mcx_array *) src, conversion->sourceSlice)) { + return RETURN_ERROR; + } + + Array2ArrayCtx ctx = {src, conversion->sourceSlice, dest}; + return ChannelValueReferenceElemMap(dest, IntegerArrayToDouble, &ctx); +} + +static McxStatus TypeConversionConvertArrayBoolToArrayDouble(TypeConversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_OK != ArraysValidForConversion(dest, &ChannelTypeDouble, (mcx_array *) src, conversion->sourceSlice)) { + return RETURN_ERROR; + } + + Array2ArrayCtx ctx = {src, conversion->sourceSlice, dest}; + return ChannelValueReferenceElemMap(dest, BoolArrayToDouble, &ctx); +} + +static McxStatus TypeConversionConvertArrayDoubleToArrayInteger(TypeConversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_OK != ArraysValidForConversion(dest, &ChannelTypeInteger, (mcx_array *) src, conversion->sourceSlice)) { + return RETURN_ERROR; + } + + Array2ArrayCtx ctx = {src, conversion->sourceSlice, dest}; + return ChannelValueReferenceElemMap(dest, DoubleArrayToInteger, &ctx); +} + +static McxStatus TypeConversionConvertArrayBoolToArrayInteger(TypeConversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_OK != ArraysValidForConversion(dest, &ChannelTypeInteger, (mcx_array *) src, conversion->sourceSlice)) { + return RETURN_ERROR; + } + + Array2ArrayCtx ctx = {src, conversion->sourceSlice, dest}; + return ChannelValueReferenceElemMap(dest, BoolArrayToInteger, &ctx); +} + +static McxStatus TypeConversionConvertArrayDoubleToArrayBool(TypeConversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_OK != ArraysValidForConversion(dest, &ChannelTypeBool, (mcx_array *) src, conversion->sourceSlice)) { + return RETURN_ERROR; + } + + Array2ArrayCtx ctx = {src, conversion->sourceSlice, dest}; + return ChannelValueReferenceElemMap(dest, DoubleArrayToBool, &ctx); +} + +static McxStatus TypeConversionConvertArrayIntegerToArrayBool(TypeConversion * conversion, ChannelValueReference * dest, void * src) { + if (RETURN_OK != ArraysValidForConversion(dest, &ChannelTypeBool, (mcx_array *) src, conversion->sourceSlice)) { + return RETURN_ERROR; + } + + Array2ArrayCtx ctx = {src, conversion->sourceSlice, dest}; + return ChannelValueReferenceElemMap(dest, IntegerArrayToBool, &ctx); +} + +static McxStatus TypeConversionConvertId(TypeConversion * conversion, ChannelValueReference * dest, void * src) { + return ChannelValueReferenceSetFromPointer(dest, src, conversion->sourceSlice, NULL); +} + +static int DimensionsMatch(ChannelType * fromType, ChannelDimension * fromDimension, ChannelType * toType, ChannelDimension * toDimension) { + if (fromDimension) { + if (!toDimension) { + return ChannelDimensionConformsTo(fromDimension, toType->ty.a.dims, toType->ty.a.numDims); + } else { + return ChannelDimensionConformsToDimension(toDimension, fromDimension); + } + } else { + if (!toDimension) { + size_t i = 0; + if (fromType->ty.a.numDims != toType->ty.a.numDims) { + return 0; + } + + for (i = 0; i < fromType->ty.a.numDims; i++) { + if (fromType->ty.a.dims[i] != toType->ty.a.dims[i]) { + return 0; + } + } + + return 1; + } else { + return ChannelDimensionConformsTo(toDimension, fromType->ty.a.dims, fromType->ty.a.numDims); + } + } +} + +static McxStatus TypeConversionSetup(TypeConversion * conversion, + ChannelType * fromType, + ChannelDimension * fromDimension, + ChannelType * toType, + ChannelDimension * toDimension) { + conversion->sourceSlice = fromDimension; + + if (ChannelTypeConformable(fromType, fromDimension, toType, toDimension)) { + conversion->Convert = TypeConversionConvertId; + /* scalar <-> scalar */ + } else if (ChannelTypeEq(fromType, &ChannelTypeInteger) && ChannelTypeEq(toType, &ChannelTypeDouble)) { + conversion->Convert = TypeConversionConvertIntDouble; + } else if (ChannelTypeEq(fromType, &ChannelTypeDouble) && ChannelTypeEq(toType, &ChannelTypeInteger)) { + conversion->Convert = TypeConversionConvertDoubleInt; + } else if (ChannelTypeEq(fromType, &ChannelTypeBool) && ChannelTypeEq(toType, &ChannelTypeDouble)) { + conversion->Convert = TypeConversionConvertBoolDouble; + } else if (ChannelTypeEq(fromType, &ChannelTypeDouble) && ChannelTypeEq(toType, &ChannelTypeBool)) { + conversion->Convert = TypeConversionConvertDoubleBool; + } else if (ChannelTypeEq(fromType, &ChannelTypeBool) && ChannelTypeEq(toType, &ChannelTypeInteger)) { + conversion->Convert = TypeConversionConvertBoolInteger; + } else if (ChannelTypeEq(fromType, &ChannelTypeInteger) && ChannelTypeEq(toType, &ChannelTypeBool)) { + conversion->Convert = TypeConversionConvertIntegerBool; + /* scalar <-> array */ + } else if (ChannelTypeIsArray(fromType) && ChannelTypeEq(fromType->ty.a.inner, &ChannelTypeDouble) && ChannelTypeEq(toType, &ChannelTypeDouble)) { + conversion->Convert = TypeConversionConvertArrayDoubleToDouble; + } else if (ChannelTypeIsArray(fromType) && ChannelTypeEq(fromType->ty.a.inner, &ChannelTypeInteger) && ChannelTypeEq(toType, &ChannelTypeDouble)) { + conversion->Convert = TypeConversionConvertArrayIntegerToDouble; + } else if (ChannelTypeIsArray(fromType) && ChannelTypeEq(fromType->ty.a.inner, &ChannelTypeBool) && ChannelTypeEq(toType, &ChannelTypeDouble)) { + conversion->Convert = TypeConversionConvertArrayBoolToDouble; + } else if (ChannelTypeIsArray(fromType) && ChannelTypeEq(fromType->ty.a.inner, &ChannelTypeDouble) && ChannelTypeEq(toType, &ChannelTypeBool)) { + conversion->Convert = TypeConversionConvertArrayDoubleToBool; + } else if (ChannelTypeIsArray(fromType) && ChannelTypeEq(fromType->ty.a.inner, &ChannelTypeInteger) && ChannelTypeEq(toType, &ChannelTypeBool)) { + conversion->Convert = TypeConversionConvertArrayIntegerToBool; + } else if (ChannelTypeIsArray(fromType) && ChannelTypeEq(fromType->ty.a.inner, &ChannelTypeBool) && ChannelTypeEq(toType, &ChannelTypeBool)) { + conversion->Convert = TypeConversionConvertArrayBoolToBool; + } else if (ChannelTypeIsArray(fromType) && ChannelTypeEq(fromType->ty.a.inner, &ChannelTypeDouble) && ChannelTypeEq(toType, &ChannelTypeInteger)) { + conversion->Convert = TypeConversionConvertArrayDoubleToInteger; + } else if (ChannelTypeIsArray(fromType) && ChannelTypeEq(fromType->ty.a.inner, &ChannelTypeInteger) && ChannelTypeEq(toType, &ChannelTypeInteger)) { + conversion->Convert = TypeConversionConvertArrayIntegerToInteger; + } else if (ChannelTypeIsArray(fromType) && ChannelTypeEq(fromType->ty.a.inner, &ChannelTypeBool) && ChannelTypeEq(toType, &ChannelTypeInteger)) { + conversion->Convert = TypeConversionConvertArrayBoolToInteger; + } else if (ChannelTypeEq(fromType, &ChannelTypeDouble) && ChannelTypeIsArray(toType) && ChannelTypeEq(toType->ty.a.inner, &ChannelTypeDouble)) { + conversion->Convert = TypeConversionConvertDoubleToArrayDouble; + } else if (ChannelTypeEq(fromType, &ChannelTypeInteger) && ChannelTypeIsArray(toType) && ChannelTypeEq(toType->ty.a.inner, &ChannelTypeDouble)) { + conversion->Convert = TypeConversionConvertIntegerToArrayDouble; + } else if (ChannelTypeEq(fromType, &ChannelTypeBool) && ChannelTypeIsArray(toType) && ChannelTypeEq(toType->ty.a.inner, &ChannelTypeDouble)) { + conversion->Convert = TypeConversionConvertBoolToArrayDouble; + } else if (ChannelTypeEq(fromType, &ChannelTypeDouble) && ChannelTypeIsArray(toType) && ChannelTypeEq(toType->ty.a.inner, &ChannelTypeInteger)) { + conversion->Convert = TypeConversionConvertDoubleToArrayInteger; + } else if (ChannelTypeEq(fromType, &ChannelTypeInteger) && ChannelTypeIsArray(toType) && ChannelTypeEq(toType->ty.a.inner, &ChannelTypeInteger)) { + conversion->Convert = TypeConversionConvertIntegerToArrayInteger; + } else if (ChannelTypeEq(fromType, &ChannelTypeBool) && ChannelTypeIsArray(toType) && ChannelTypeEq(toType->ty.a.inner, &ChannelTypeInteger)) { + conversion->Convert = TypeConversionConvertBoolToArrayInteger; + } else if (ChannelTypeEq(fromType, &ChannelTypeDouble) && ChannelTypeIsArray(toType) && ChannelTypeEq(toType->ty.a.inner, &ChannelTypeBool)) { + conversion->Convert = TypeConversionConvertDoubleToArrayBool; + } else if (ChannelTypeEq(fromType, &ChannelTypeInteger) && ChannelTypeIsArray(toType) && ChannelTypeEq(toType->ty.a.inner, &ChannelTypeBool)) { + conversion->Convert = TypeConversionConvertIntegerToArrayBool; + } else if (ChannelTypeEq(fromType, &ChannelTypeBool) && ChannelTypeIsArray(toType) && ChannelTypeEq(toType->ty.a.inner, &ChannelTypeBool)) { + conversion->Convert = TypeConversionConvertBoolToArrayBool; + /* array <-> array */ + } else if (ChannelTypeIsArray(fromType) && ChannelTypeIsArray(toType)) { + if (!DimensionsMatch(fromType, fromDimension, toType, toDimension)) { + mcx_log(LOG_ERROR, "Setup type conversion: Array dimensions do not match"); + return RETURN_ERROR; + } + + if (ChannelTypeEq(ChannelTypeBaseType(fromType), &ChannelTypeInteger) && ChannelTypeEq(ChannelTypeBaseType(toType), &ChannelTypeDouble)) { + conversion->Convert = TypeConversionConvertArrayIntegerToArrayDouble; + } else if (ChannelTypeEq(ChannelTypeBaseType(fromType), &ChannelTypeBool) && ChannelTypeEq(ChannelTypeBaseType(toType), &ChannelTypeDouble)) { + conversion->Convert = TypeConversionConvertArrayBoolToArrayDouble; + } else if (ChannelTypeEq(ChannelTypeBaseType(fromType), &ChannelTypeDouble) && ChannelTypeEq(ChannelTypeBaseType(toType), &ChannelTypeInteger)) { + conversion->Convert = TypeConversionConvertArrayDoubleToArrayInteger; + } else if (ChannelTypeEq(ChannelTypeBaseType(fromType), &ChannelTypeBool) && ChannelTypeEq(ChannelTypeBaseType(toType), &ChannelTypeInteger)) { + conversion->Convert = TypeConversionConvertArrayBoolToArrayInteger; + } else if (ChannelTypeEq(ChannelTypeBaseType(fromType), &ChannelTypeDouble) && ChannelTypeEq(ChannelTypeBaseType(toType), &ChannelTypeBool)) { + conversion->Convert = TypeConversionConvertArrayDoubleToArrayBool; + } else if (ChannelTypeEq(ChannelTypeBaseType(fromType), &ChannelTypeInteger) && ChannelTypeEq(ChannelTypeBaseType(toType), &ChannelTypeBool)) { + conversion->Convert = TypeConversionConvertArrayIntegerToArrayBool; + } else { + mcx_log(LOG_ERROR, "Setup type conversion: Illegal conversion between array types selected"); + return RETURN_ERROR; + } + } else { + mcx_log(LOG_ERROR, "Setup type conversion: Illegal conversion selected"); + return RETURN_ERROR; + } + + return RETURN_OK; +} + +static void TypeConversionDestructor(TypeConversion * conversion) { + +} + +static TypeConversion * TypeConversionCreate(TypeConversion * conversion) { + conversion->Setup = TypeConversionSetup; + conversion->Convert = NULL; + + return conversion; } -OBJECT_CLASS(TypeConversion, Conversion); +OBJECT_CLASS(TypeConversion, Object); #ifdef __cplusplus } /* closing brace for extern "C" */ diff --git a/src/core/Conversion.h b/src/core/Conversion.h index 1f542f2..1c2d3ed 100644 --- a/src/core/Conversion.h +++ b/src/core/Conversion.h @@ -12,11 +12,16 @@ #define MCX_CORE_CONVERSION_H #include "units/Units.h" +#include "core/channels/ChannelDimension.h" + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ + +typedef struct ChannelValueReference ChannelValueReference; + typedef struct Conversion Conversion; typedef McxStatus (* fConversion)(Conversion * conversion, ChannelValue * value); @@ -47,14 +52,14 @@ typedef struct RangeConversion { fRangeConversionSetup Setup; fRangeConversionIsEmpty IsEmpty; - ChannelType type; + ChannelType * type; ChannelValue * min; ChannelValue * max; } RangeConversion; -McxStatus ConvertRange(ChannelValue * min, ChannelValue * max, ChannelValue * value); +McxStatus ConvertRange(ChannelValue * min, ChannelValue * max, ChannelValue * value, ChannelDimension * slice); // ---------------------------------------------------------------------- @@ -65,6 +70,7 @@ typedef void(*fUnitConversionVector)(UnitConversion * conversion, double * value typedef McxStatus (* fUnitConversionSetup)(UnitConversion * conversion, const char * fromUnit, const char * toUnit); typedef int (* fUnitConversionIsEmpty)(UnitConversion * conversion); +typedef McxStatus (*fUnitConversionConvertValueRef)(UnitConversion * conversion, ChannelValueReference * ref); extern const struct ObjectClass _UnitConversion; @@ -73,6 +79,7 @@ struct UnitConversion { fUnitConversionSetup Setup; fUnitConversionIsEmpty IsEmpty; + fUnitConversionConvertValueRef ConvertValueReference; si_def source; si_def target; @@ -80,7 +87,7 @@ struct UnitConversion { fUnitConversionVector convertVector; }; -McxStatus ConvertUnit(const char * fromUnit, const char * toUnit, ChannelValue * value); +McxStatus ConvertUnit(const char * fromUnit, const char * toUnit, ChannelValue * value, ChannelDimension * slice); // ---------------------------------------------------------------------- @@ -99,34 +106,42 @@ typedef struct LinearConversion { fLinearConversionSetup Setup; fLinearConversionIsEmpty IsEmpty; - ChannelType type; + ChannelType * type; ChannelValue * factor; ChannelValue * offset; } LinearConversion; -McxStatus ConvertLinear(ChannelValue * factor, ChannelValue * offset, ChannelValue * value); +McxStatus ConvertLinear(ChannelValue * factor, ChannelValue * offset, ChannelValue * value, ChannelDimension * slice); // ---------------------------------------------------------------------- // Type Conversion - typedef struct TypeConversion TypeConversion; -typedef McxStatus (* fTypeConversionSetup)(TypeConversion * conversion, ChannelType fromType, ChannelType toType); -typedef int (* fTypeConversionIsEmpty)(TypeConversion * conversion); +typedef McxStatus (*fTypeConversionSetup)(TypeConversion * conversion, + const ChannelType * fromType, + ChannelDimension * fromDimension, + const ChannelType * toType, + ChannelDimension * toDimension); + +// TODO: Ideally the `src` argument would also be ChannelValueReference, but that requires quite of lot of changes +// in the API of databus definition (i.e. DatabusSetIn(Out)Reference) +typedef McxStatus (*fTypeConversionConvert)(TypeConversion * conversion, ChannelValueReference * dest, void * src); extern const struct ObjectClass _TypeConversion; struct TypeConversion { - Conversion _; + Object _; fTypeConversionSetup Setup; - fTypeConversionIsEmpty IsEmpty; + fTypeConversionConvert Convert; + + ChannelDimension * sourceSlice; }; -McxStatus ConvertType(ChannelType fromType, ChannelType toType, ChannelValue * value); +McxStatus ConvertType(ChannelValue * dest, ChannelDimension * destSlice, ChannelValue * src, ChannelDimension * srcSlice); #ifdef __cplusplus diff --git a/src/core/Databus.c b/src/core/Databus.c index 74efc5b..4385837 100644 --- a/src/core/Databus.c +++ b/src/core/Databus.c @@ -38,368 +38,239 @@ extern "C" { static void DatabusInfoDataDestructor(DatabusInfoData * data) { object_destroy(data->infos); - data->origInfos->DestroyObjects(data->origInfos); - object_destroy(data->origInfos); } static DatabusInfoData * DatabusInfoDataCreate(DatabusInfoData * data) { data->infos = (Vector *) object_create(Vector); data->infos->Setup(data->infos, sizeof(ChannelInfo), ChannelInfoInit, ChannelInfoSetFrom, ChannelInfoDestroy); - data->origInfos = (ObjectContainer *) object_create(ObjectContainer); return data; } OBJECT_CLASS(DatabusInfoData, Object); - -char * CreateIndexedName(const char * name, unsigned i) { - size_t len = 0; - char * buffer = NULL; - - len = strlen(name) + (mcx_digits10(i) + 1) + 2 + 1; - - buffer = (char *) mcx_calloc(len, sizeof(char)); - if (!buffer) { - return NULL; - } - - snprintf(buffer, len, "%s[%d]", name, i); - - return buffer; -} - - - -static Vector * DatabusReadPortInput(PortInput * input) { +static ChannelInfo * DatabusReadPortInput(PortInput * input) { McxStatus retVal = RETURN_OK; - Vector * list = NULL; - VectorChannelInfo * vector = NULL; - list = (Vector *) object_create(Vector); - if (!list) { - retVal = RETURN_ERROR; - goto cleanup_0; + ChannelInfo * info = (ChannelInfo *)mcx_calloc(1, sizeof(ChannelInfo)); + if (!info) { + return NULL; } - list->Setup(list, sizeof(ChannelInfo), ChannelInfoInit, ChannelInfoSetFrom, ChannelInfoDestroy); - - vector = (VectorChannelInfo *)object_create(VectorChannelInfo); - if (!vector) { - retVal = RETURN_ERROR; + retVal = ChannelInfoInit(info); + if (RETURN_ERROR == retVal) { goto cleanup_0; } if (input->type == PORT_VECTOR) { - /* vector of channels: Copy info and add "[i]" to name, nameInTool and id */ - ChannelValue ** mins = NULL; - ChannelValue ** maxs = NULL; - ChannelValue ** scales = NULL; - ChannelValue ** offsets = NULL; - ChannelValue ** defaults = NULL; - ChannelValue ** initials = NULL; - int * writeResults = NULL; - - int startIndex = 0; - int endIndex = 0; - - int i = 0; - VectorPortInput * vectorPortInput = input->port.vectorPort; InputElement * vectorPortElement = (InputElement *) vectorPortInput; - ChannelType expectedType = vectorPortInput->type; - size_t expectedLen = 0; + int startIdx = 0; + int endIdx = 0; - if (!vector) { - retVal = RETURN_ERROR; - goto cleanup_1; - } - - startIndex = vectorPortInput->startIndex; - if (startIndex < 0) { + startIdx = vectorPortInput->startIndex; + if (startIdx < 0) { input_element_error(vectorPortElement, "start index must not be smaller than 0"); retVal = RETURN_ERROR; - goto cleanup_1; + goto vector_cleanup_0; } - endIndex = vectorPortInput->endIndex; - if (endIndex < startIndex) { + endIdx = vectorPortInput->endIndex; + if (endIdx < startIdx) { input_element_error(vectorPortElement, "end index must not be smaller than start index"); retVal = RETURN_ERROR; - goto cleanup_1; - } - - expectedLen = endIndex - startIndex + 1; - - retVal = vector->Setup(vector, vectorPortInput->name, vectorPortInput->nameInModel, FALSE, (size_t) startIndex, (size_t) endIndex); - if (RETURN_ERROR == retVal) { - goto cleanup_1; + goto vector_cleanup_0; } - mins = ArrayToChannelValueArray(vectorPortInput->min, expectedLen, expectedType); - if (vectorPortInput->min && !mins) { + info->dimension = MakeChannelDimension(); + if (!info->dimension) { retVal = RETURN_ERROR; - goto cleanup_1; + goto vector_cleanup_0; } - maxs = ArrayToChannelValueArray(vectorPortInput->max, expectedLen, expectedType); - if (vectorPortInput->max && !maxs) { + if (RETURN_OK != ChannelDimensionSetup(info->dimension, 1)) { + mcx_log(LOG_ERROR, "Could not setup ChannelDimension"); retVal = RETURN_ERROR; - goto cleanup_1; + goto vector_cleanup_0; } - scales = ArrayToChannelValueArray(vectorPortInput->scale, expectedLen, expectedType); - if (vectorPortInput->scale && !scales) { + if (RETURN_OK != ChannelDimensionSetDimension(info->dimension, 0, (size_t) startIdx, (size_t) endIdx)) { + mcx_log(LOG_ERROR, "Could not SetDimension"); retVal = RETURN_ERROR; - goto cleanup_1; + goto vector_cleanup_0; } - offsets = ArrayToChannelValueArray(vectorPortInput->offset, expectedLen, expectedType); - if (vectorPortInput->offset && !offsets) { - retVal = RETURN_ERROR; - goto cleanup_1; - } - - defaults = ArrayToChannelValueArray(vectorPortInput->default_, expectedLen, expectedType); - if (vectorPortInput->default_ && !defaults) { - retVal = RETURN_ERROR; - goto cleanup_1; - } + size_t dims[1] = { endIdx - startIdx + 1 }; - initials = ArrayToChannelValueArray(vectorPortInput->initial, expectedLen, expectedType); - if (vectorPortInput->initial && !initials) { + if (RETURN_OK != ChannelInfoSetup(info, + vectorPortInput->name, + vectorPortInput->nameInModel, + vectorPortInput->description, + vectorPortInput->unit, + ChannelTypeArray(vectorPortInput->type, 1, dims), + vectorPortInput->id)) { + mcx_log(LOG_ERROR, "Could not Init ChannelInfo"); retVal = RETURN_ERROR; - goto cleanup_1; + goto vector_cleanup_0; } - writeResults = vectorPortInput->writeResults; - - for (i = startIndex; i <= endIndex; i++) { - char * name = NULL; - char * nameInTool = NULL; - char * id = NULL; - - ChannelInfo copy = { 0 }; - - if (!(name = CreateIndexedName(vectorPortInput->name, i))) { + if (vectorPortInput->min) { + info->min = ChannelValueNewArray(1, dims, vectorPortInput->type, vectorPortInput->min); + if (!info->min) { retVal = RETURN_ERROR; - goto cleanup_2; - } - if (vectorPortInput->nameInModel) { // optional - if (!(nameInTool = CreateIndexedName(vectorPortInput->nameInModel, i))) { - retVal = RETURN_ERROR; - goto cleanup_2; - } - } - if (vectorPortInput->id) { // optional - if (!(id = CreateIndexedName(vectorPortInput->id, i))) { - retVal = RETURN_ERROR; - goto cleanup_2; - } - } - - retVal = ChannelInfoInit(©); - if (RETURN_ERROR == retVal) { - goto cleanup_2; - } - - ChannelInfoSetName(©, name); - ChannelInfoSetNameInTool(©, nameInTool); - ChannelInfoSetID(©, id); - - ChannelInfoSetDescription(©, vectorPortInput->description); - ChannelInfoSetType(©, vectorPortInput->type); - - if (!ChannelInfoIsBinary(©)) { - ChannelInfoSetUnit(©, vectorPortInput->unit); - } else { - ChannelInfoSetUnit(©, "-"); - } - - ChannelInfoSetVector(©, (VectorChannelInfo *) object_strong_reference(vector)); - - if (mins) { - copy.min = mins[i - startIndex]; - } - if (maxs) { - copy.max = maxs[i - startIndex]; - } - - if (scales) { - copy.scale = scales[i - startIndex]; - } - if (offsets) { - copy.offset = offsets[i - startIndex]; + goto vector_cleanup_0; } + } - if (defaults) { - copy.defaultValue = defaults[i - startIndex]; - } - if (initials) { - copy.initialValue = initials[i - startIndex]; - } - if (writeResults) { - copy.writeResult = writeResults[i - startIndex]; + if (vectorPortInput->max) { + info->max = ChannelValueNewArray(1, dims, vectorPortInput->type, vectorPortInput->max); + if (!info->max) { + retVal = RETURN_ERROR; + goto vector_cleanup_0; } + } - list->PushBack(list, ©); - - retVal = vector->AddElement(vector, list->At(list, list->Size(list) - 1), i); - if (RETURN_ERROR == retVal) { - goto cleanup_2; + if (vectorPortInput->scale) { + info->scale = ChannelValueNewArray(1, dims, vectorPortInput->type, vectorPortInput->scale); + if (!info->scale) { + retVal = RETURN_ERROR; + goto vector_cleanup_0; } + } - cleanup_2: - if (name) { - mcx_free(name); - } - if (nameInTool) { - mcx_free(nameInTool); - } - if (id) { - mcx_free(id); - } - if (RETURN_ERROR == retVal) { - goto cleanup_1; + if (vectorPortInput->offset) { + info->offset = ChannelValueNewArray(1, dims, vectorPortInput->type, vectorPortInput->offset); + if (!info->offset) { + retVal = RETURN_ERROR; + goto vector_cleanup_0; } } - cleanup_1: - - if (mins) { - mcx_free(mins); - } - if (maxs) { - mcx_free(maxs); + if (vectorPortInput->initial) { + info->initialValue = ChannelValueNewArray(1, dims, vectorPortInput->type, vectorPortInput->initial); + if (!info->initialValue) { + retVal = RETURN_ERROR; + goto vector_cleanup_0; + } } - if (scales) { - mcx_free(scales); - } - if (offsets) { - mcx_free(offsets); + if (vectorPortInput->default_) { + info->defaultValue = ChannelValueNewArray(1, dims, vectorPortInput->type, vectorPortInput->default_); + if (!info->defaultValue) { + retVal = RETURN_ERROR; + goto vector_cleanup_0; + } } - if (defaults) { - mcx_free(defaults); - } - if (initials) { - mcx_free(initials); - } - if (writeResults) { - // writeResults was taken from vectorPortInput + if (vectorPortInput->writeResults.defined) { + info->writeResult = vectorPortInput->writeResults.value; } - if (RETURN_ERROR == retVal) { + vector_cleanup_0: + if (retVal == RETURN_ERROR) { goto cleanup_0; } } else { ScalarPortInput * scalarPortInput = input->port.scalarPort; - ChannelInfo info = { 0 }; - retVal = ChannelInfoInit(&info); + retVal = ChannelInfoInit(info); if (RETURN_ERROR == retVal) { goto cleanup_else_1; } - ChannelInfoSetName(&info, scalarPortInput->name); - ChannelInfoSetNameInTool(&info, scalarPortInput->nameInModel); - ChannelInfoSetDescription(&info, scalarPortInput->description); - ChannelInfoSetID(&info, scalarPortInput->id); - ChannelInfoSetType(&info, scalarPortInput->type); + ChannelInfoSetName(info, scalarPortInput->name); + ChannelInfoSetNameInTool(info, scalarPortInput->nameInModel); + ChannelInfoSetDescription(info, scalarPortInput->description); + ChannelInfoSetID(info, scalarPortInput->id); + ChannelInfoSetType(info, scalarPortInput->type); - if (!ChannelInfoIsBinary(&info)) { - ChannelInfoSetUnit(&info, scalarPortInput->unit); + if (!ChannelInfoIsBinary(info)) { + ChannelInfoSetUnit(info, scalarPortInput->unit); } else { - ChannelInfoSetUnit(&info, "-"); + ChannelInfoSetUnit(info, "-"); } - ChannelType expectedType = info.type; - + ChannelType * expectedType = info->type; ChannelValue value; - ChannelValueInit(&value, expectedType); + ChannelValueInit(&value, ChannelTypeClone(expectedType)); if (scalarPortInput->min.defined) { - ChannelValueSetFromReference(&value, &scalarPortInput->min.value); - info.min = ChannelValueClone(&value); - if (!info.min) { + if (RETURN_OK != ChannelValueSetFromReference(&value, &scalarPortInput->min.value)) { + goto cleanup_else_1; + } + info->min = ChannelValueClone(&value); + if (!info->min) { goto cleanup_else_1; } } if (scalarPortInput->max.defined) { - ChannelValueSetFromReference(&value, &scalarPortInput->max.value); - info.max = ChannelValueClone(&value); - if (!info.max) { + if (RETURN_OK != ChannelValueSetFromReference(&value, &scalarPortInput->max.value)) { + goto cleanup_else_1; + } + info->max = ChannelValueClone(&value); + if (!info->max) { goto cleanup_else_1; } } if (scalarPortInput->scale.defined) { - ChannelValueSetFromReference(&value, &scalarPortInput->scale.value); - info.scale = ChannelValueClone(&value); - if (!info.scale) { + if (RETURN_OK != ChannelValueSetFromReference(&value, &scalarPortInput->scale.value)) { + goto cleanup_else_1; + } + info->scale = ChannelValueClone(&value); + if (!info->scale) { goto cleanup_else_1; } } if (scalarPortInput->offset.defined) { - ChannelValueSetFromReference(&value, &scalarPortInput->offset.value); - info.offset = ChannelValueClone(&value); - if (!info.offset) { + if (RETURN_OK != ChannelValueSetFromReference(&value, &scalarPortInput->offset.value)) { + goto cleanup_else_1; + } + info->offset = ChannelValueClone(&value); + if (!info->offset) { goto cleanup_else_1; } } if (scalarPortInput->default_.defined) { - ChannelValueSetFromReference(&value, &scalarPortInput->default_.value); - info.defaultValue = ChannelValueClone(&value); - if (!info.defaultValue) { + if (RETURN_OK != ChannelValueSetFromReference(&value, &scalarPortInput->default_.value)) { + goto cleanup_else_1; + } + info->defaultValue = ChannelValueClone(&value); + if (!info->defaultValue) { goto cleanup_else_1; } } if (scalarPortInput->initial.defined) { - ChannelValueSetFromReference(&value, &scalarPortInput->initial.value); - info.initialValue = ChannelValueClone(&value); - if (!info.initialValue) { + if (RETURN_OK != ChannelValueSetFromReference(&value, &scalarPortInput->initial.value)) { + goto cleanup_else_1; + } + info->initialValue = ChannelValueClone(&value); + if (!info->initialValue) { goto cleanup_else_1; } } if (scalarPortInput->writeResults.defined) { - info.writeResult = scalarPortInput->writeResults.value; - } - - retVal = vector->Setup(vector, ChannelInfoGetName(&info), info.nameInTool, TRUE, -1, -1); - if (RETURN_ERROR == retVal) { - goto cleanup_else_1; - } - ChannelInfoSetVector(&info, (VectorChannelInfo *) object_strong_reference(vector)); - - list->PushBack(list, &info); - - retVal = vector->AddElement(vector, list->At(list, list->Size(list) - 1), 0); - if (RETURN_ERROR == retVal) { - goto cleanup_else_1; + info->writeResult = scalarPortInput->writeResults.value; } cleanup_else_1: - ChannelInfoDestroy(&info); ChannelValueDestructor(&value); goto cleanup_0; } cleanup_0: if (RETURN_ERROR == retVal) { - object_destroy(list); + object_destroy(info); } - object_destroy(vector); - - return list; + return info; } static int ChannelInfoSameNamePred(void * elem, const char * name) { @@ -432,10 +303,12 @@ McxStatus DatabusInfoRead(DatabusInfo * dbInfo, Vector * dbInfos = dbInfo->data->infos; size_t requiredSize = 0; - ObjectContainer * allChannels = dbInfo->data->origInfos; - if (NULL == allChannels) { - mcx_log(LOG_ERROR, "Ports: Read port infos: Container of vector ports missing"); - return RETURN_ERROR; + StringContainer * portNames = StringContainerCreate(numChildren); + + if (!portNames) { + mcx_log(LOG_ERROR, "Ports: Port name container allocation failed"); + retVal = RETURN_ERROR; + goto cleanup; } for (i = 0; i < numChildren; i++) { @@ -445,77 +318,58 @@ McxStatus DatabusInfoRead(DatabusInfo * dbInfo, } else { requiredSize += portInput->port.vectorPort->endIndex - portInput->port.vectorPort->startIndex + 1; } - } - - if (requiredSize > 0) { - dbInfos->Reserve(dbInfos, requiredSize); - } - - for (i = 0; i < numChildren; i++) { - PortInput * portInput = (PortInput *) input->ports->At(input->ports, i); - Vector * infos = NULL; - infos = DatabusReadPortInput(portInput); - if (!infos) { + ChannelInfo * info = DatabusReadPortInput(portInput); + if (!info) { mcx_log(LOG_ERROR, "Ports: Read port infos: Could not read info of port %zu", i); - return RETURN_ERROR; + retVal = RETURN_ERROR; + goto cleanup; } - for (j = 0; j < infos->Size(infos); j++) { - ChannelInfo * info = (ChannelInfo *) infos->At(infos, j); - const char * name = ChannelInfoGetName(info); - int n = ChannelInfosGetNameIdx(dbInfos, name); + info->mode = mode; - if (n >= 0) { // key already exists - mcx_log(LOG_ERROR, "Ports: Duplicate port %s", name); - object_destroy(infos); - return RETURN_ERROR; - } + const char * name = ChannelInfoGetName(info); + if (info->dimension) { + mcx_log(LOG_DEBUG, " Port: \"%s[%zu:%zu]\"", name, info->dimension->startIdxs[0], info->dimension->endIdxs[0]); + } else { mcx_log(LOG_DEBUG, " Port: \"%s\"", name); - - info->mode = mode; } - for (j = 0; j < infos->Size(infos); ++j) { - ChannelInfo * info = (ChannelInfo *) infos->At(infos, j); - VectorChannelInfo * vInfo = info->vector; - size_t startIdx = vInfo->GetStartIndex(vInfo); - size_t idx = startIdx == -1 ? 0 : (startIdx + j); - ChannelInfo * infoCpy = NULL; - - retVal = dbInfos->PushBack(dbInfos, info); - if (RETURN_OK != retVal) { - mcx_log(LOG_ERROR, "Ports: Read port infos: Could not append info of port %zu", i); - object_destroy(infos); - return RETURN_ERROR; + { + // check for duplicates + int n = StringContainerGetIndex(portNames, name); + if (n >= 0) { // key already exists + mcx_log(LOG_ERROR, "Ports: Duplicate port %s", name); + retVal = RETURN_ERROR; + goto cleanup; } + } - infoCpy = dbInfos->At(dbInfos, dbInfos->Size(dbInfos) - 1); - retVal = infoCpy->vector->AddElement(infoCpy->vector, infoCpy, idx); + if (SpecificRead) { + retVal = SpecificRead(comp, info, portInput, i); if (RETURN_ERROR == retVal) { - mcx_log(LOG_ERROR, "Ports: Read port infos: Could not add vector info of port %zu", i); - object_destroy(infos); - return RETURN_ERROR; - } - - if (infos->Size(infos) == 1 && SpecificRead) { - retVal = SpecificRead(comp, infoCpy, portInput, i); - if (RETURN_ERROR == retVal) { - mcx_log(LOG_ERROR, "Ports: Read port infos: Could not read element specific data of port %zu", i); - return RETURN_ERROR; - } + mcx_log(LOG_ERROR, "Ports: Read port infos: Could not read element specific data of port %zu", i); + goto cleanup; } } - { - size_t dbInfosSize = dbInfos->Size(dbInfos); - ChannelInfo * chInfo = (ChannelInfo *)dbInfos->At(dbInfos, dbInfosSize - 1); - allChannels->PushBack(allChannels, (Object *) object_strong_reference(chInfo->vector)); + if (RETURN_OK != dbInfos->PushBack(dbInfos, info)) { + mcx_log(LOG_ERROR, "Ports: Read port infos: Could not append info of port %zu", i); + retVal = RETURN_ERROR; + goto cleanup; } - object_destroy(infos); + retVal = StringContainerAddString(portNames, name); + if (RETURN_ERROR == retVal) { + mcx_log(LOG_ERROR, "Ports: Storing port name failed"); + goto cleanup; + } } - return RETURN_OK; + +cleanup: + StringContainerDestroy(portNames); + + return retVal; } static int IsWriteResults(void * elem, void * ignore) { @@ -802,7 +656,7 @@ McxStatus DatabusTriggerInConnections(Databus * db, TimeInterval * consumerTime) for (i = 0; i < numIn; i++) { Channel * channel = (Channel *) db->data->in[i]; - if (channel->IsValid(channel)) { + if (channel->IsConnected(channel) || channel->info.defaultValue) { retVal = channel->Update(channel, consumerTime); if (RETURN_OK != retVal) { ChannelInfo * info = &channel->info; @@ -935,14 +789,22 @@ size_t DatabusInfoGetChannelNum(DatabusInfo * info) { return info->data->infos->Size(info->data->infos); } -// Only returns SIZE_T_ERROR if info was NULL -size_t DatabusInfoGetVectorChannelNum(DatabusInfo * info) { - if (!info) { - mcx_log(LOG_ERROR, "Ports: Get vector port number: Invalid structure"); - return SIZE_T_ERROR; +size_t DatabusInfoGetChannelElemNum(DatabusInfo * info) { + size_t numInfos = info->data->infos->Size(info->data->infos); + size_t i = 0; + size_t numElems = 0; + + for (i = 0; i < numInfos; i++) { + ChannelInfo * chInfo = (ChannelInfo*)info->data->infos->At(info->data->infos, i); + if (chInfo->dimension) { + // array + numElems += ChannelDimensionNumElements(chInfo->dimension); + } else { + numElems += 1; + } } - return info->data->origInfos->Size(info->data->origInfos); + return numElems; } ChannelInfo * DatabusInfoGetChannel(DatabusInfo * info, size_t i) { @@ -959,20 +821,6 @@ ChannelInfo * DatabusInfoGetChannel(DatabusInfo * info, size_t i) { return (ChannelInfo *) info->data->infos->At(info->data->infos, i); } -static VectorChannelInfo * DatabusInfoGetVectorChannelInfo(DatabusInfo * info, size_t i) { - if (!info) { - mcx_log(LOG_ERROR, "Ports: Get vector port info: Invalid structure"); - return NULL; - } - - if (i >= info->data->origInfos->Size(info->data->origInfos)) { - mcx_log(LOG_ERROR, "Ports: Get vector port info: Unknown port %d", i); - return NULL; - } - - return (VectorChannelInfo *) info->data->origInfos->At(info->data->origInfos, i); -} - int DatabusInChannelsDefined(Databus * db) { return db->data->in != NULL; } @@ -1085,26 +933,6 @@ size_t DatabusGetInChannelsNum(Databus * db) { return DatabusInfoGetChannelNum(DatabusGetInInfo(db)); } -// Only returns SIZE_T_ERROR if db was NULL -size_t DatabusGetOutVectorChannelsNum(Databus * db) { - if (!db) { - mcx_log(LOG_ERROR, "Ports: Get outport number: Invalid structure"); - return SIZE_T_ERROR; - } - - return DatabusInfoGetVectorChannelNum(DatabusGetOutInfo(db)); -} - -// Only returns SIZE_T_ERROR if db was NULL -size_t DatabusGetInVectorChannelsNum(Databus * db) { - if (!db) { - mcx_log(LOG_ERROR, "Ports: Get inport number: Invalid structure"); - return SIZE_T_ERROR; - } - - return DatabusInfoGetVectorChannelNum(DatabusGetInInfo(db)); -} - // Only returns SIZE_T_ERROR if db was NULL size_t DatabusGetLocalChannelsNum(Databus * db) { if (!db) { @@ -1125,8 +953,16 @@ size_t DatabusGetRTFactorChannelsNum(Databus * db) { return DatabusInfoGetChannelNum(DatabusGetRTFactorInfo(db)); } +size_t DatabusGetOutChannelsElemNum(Databus * db) { + return DatabusInfoGetChannelElemNum(DatabusGetOutInfo(db)); +} + +size_t DatabusGetInChannelsElemNum(Databus * db) { + return DatabusInfoGetChannelElemNum(DatabusGetInInfo(db)); +} + -McxStatus DatabusSetOutReference(Databus * db, size_t channel, const void * reference, ChannelType type) { +McxStatus DatabusSetOutReference(Databus * db, size_t channel, const void * reference, ChannelType * type) { ChannelOut * out = NULL; if (!db) { @@ -1144,10 +980,10 @@ McxStatus DatabusSetOutReference(Databus * db, size_t channel, const void * refe return RETURN_ERROR; } - if (CHANNEL_UNKNOWN != type) { + if (ChannelTypeIsValid(type)) { ChannelInfo * info = &((Channel *)out)->info; - if (info->type != type) { - if (ChannelInfoIsBinary(info) && (type == CHANNEL_BINARY || type == CHANNEL_BINARY_REFERENCE)) { + if (!ChannelTypeEq(info->type, type)) { + if (ChannelInfoIsBinary(info) && ChannelTypeIsBinary(type)) { // ok } else { mcx_log(LOG_ERROR, "Ports: Set out reference: Port %s has mismatching type %s, given: %s", @@ -1160,7 +996,7 @@ McxStatus DatabusSetOutReference(Databus * db, size_t channel, const void * refe return out->SetReference(out, reference, type); } -McxStatus DatabusSetOutReferenceFunction(Databus * db, size_t channel, const void * reference, ChannelType type) { +McxStatus DatabusSetOutReferenceFunction(Databus * db, size_t channel, const void * reference, ChannelType * type) { ChannelOut * out = NULL; ChannelInfo * info = NULL; @@ -1180,7 +1016,7 @@ McxStatus DatabusSetOutReferenceFunction(Databus * db, size_t channel, const voi } info = &((Channel *)out)->info; - if (info->type != type) { + if (!ChannelTypeEq(info->type, type)) { mcx_log(LOG_ERROR, "Ports: Set out reference function: Port %s has mismatching type %s, given: %s", ChannelInfoGetName(info), ChannelTypeToString(info->type), ChannelTypeToString(type)); return RETURN_ERROR; @@ -1193,123 +1029,7 @@ McxStatus DatabusSetOutReferenceFunction(Databus * db, size_t channel, const voi return out->SetReferenceFunction(out, (const proc *) reference, type); } -McxStatus DatabusSetOutRefVectorChannel(Databus * db, size_t channel, - size_t startIdx, size_t endIdx, ChannelValue * value) -{ - VectorChannelInfo * vInfo = NULL; - size_t i = 0; - size_t ii = 0; - ChannelType type = ChannelValueType(value); - - if (startIdx > endIdx) { - mcx_log(LOG_ERROR, "Ports: Set out reference vector: Start index %d bigger than end index %d", startIdx, endIdx); - return RETURN_ERROR; - } - if (CHANNEL_UNKNOWN == type) { - mcx_log(LOG_ERROR, "Ports: Set out reference vector: Type of vector needs to be specified"); - return RETURN_ERROR; - } - - if (channel > DatabusGetOutVectorChannelsNum(db)) { - mcx_log(LOG_ERROR, "Ports: Set out reference vector: Vector port %d does not exist (numer of vector ports=%d)", channel, DatabusGetOutVectorChannelsNum(db)); - return RETURN_ERROR; - } - - vInfo = DatabusGetOutVectorChannelInfo(db, channel); - for (i = startIdx; i <= endIdx; i++) { - McxStatus retVal = RETURN_OK; - const void * ref = NULL; - ChannelOut * chOut = NULL; - ChannelInfo * chInfo = vInfo->GetElement(vInfo, i); - if (!chInfo) { - mcx_log(LOG_ERROR, "Ports: Set out reference vector: Vector Port does not exist"); - return RETURN_ERROR; - } - chOut = (ChannelOut *) chInfo->channel; - if (!chOut) { - mcx_log(LOG_ERROR, "Ports: Set out reference vector: Vector Port not initialized"); - return RETURN_ERROR; - } - ii = i - startIdx; - ref = (const void *) ( &((ChannelValue*)value + ii)->value ); - - retVal = chOut->SetReference(chOut, ref, type); - if (RETURN_ERROR == retVal) { - mcx_log(LOG_ERROR, "Ports: Set out reference vector: Reference could not be set"); - return RETURN_ERROR; - } - } - - return RETURN_OK; -} - -McxStatus DatabusSetOutRefVector(Databus * db, size_t channel, - size_t startIdx, size_t endIdx, const void * reference, ChannelType type) -{ - VectorChannelInfo * vInfo = NULL; - size_t i = 0; - size_t ii = 0; - - if (startIdx > endIdx) { - mcx_log(LOG_ERROR, "Ports: Set out reference vector: Start index %d bigger than end index %d", startIdx, endIdx); - return RETURN_ERROR; - } - if (CHANNEL_UNKNOWN == type) { - mcx_log(LOG_ERROR, "Ports: Set out reference vector: Type of vector needs to be specified"); - return RETURN_ERROR; - } - - if (channel > DatabusGetOutVectorChannelsNum(db)) { - mcx_log(LOG_ERROR, "Ports: Set out reference vector: Vector port %d does not exist (numer of vector ports=%d)", channel, DatabusGetOutVectorChannelsNum(db)); - return RETURN_ERROR; - } - - vInfo = DatabusGetOutVectorChannelInfo(db, channel); - for (i = startIdx; i <= endIdx; i++) { - McxStatus retVal = RETURN_OK; - const void * ref = NULL; - ChannelOut * chOut = NULL; - ChannelInfo * chInfo = vInfo->GetElement(vInfo, i); - if (!chInfo) { - mcx_log(LOG_ERROR, "Ports: Set out reference vector: Vector Port does not exist"); - return RETURN_ERROR; - } - chOut = (ChannelOut *) chInfo->channel; - if (!chOut) { - mcx_log(LOG_ERROR, "Ports: Set out reference vector: Vector Port not initialized"); - return RETURN_ERROR; - } - ii = i - startIdx; - switch (type) { - case CHANNEL_DOUBLE: - ref = (const void *) (((double *) reference) + ii); - break; - case CHANNEL_INTEGER: - ref = (const void *) (((int *) reference) + ii); - break; - case CHANNEL_BOOL: - ref = (const void *) (((int *) reference) + ii); - break; - case CHANNEL_BINARY: - case CHANNEL_BINARY_REFERENCE: - ref = (const void *) (((binary_string *) reference) + ii); - break; - default: - mcx_log(LOG_ERROR, "Ports: Set out reference vector: Type of vector not allowed"); - return RETURN_ERROR; - } - - retVal = chOut->SetReference(chOut, ref, type); - if (RETURN_ERROR == retVal) { - mcx_log(LOG_ERROR, "Ports: Set out reference vector: Reference could not be set"); - return RETURN_ERROR; - } - } - - return RETURN_OK; -} - -McxStatus DatabusSetInReference(Databus * db, size_t channel, void * reference, ChannelType type) { +McxStatus DatabusSetInReference(Databus * db, size_t channel, void * reference, ChannelType * type) { ChannelIn * in = NULL; ChannelInfo * info = NULL; @@ -1328,10 +1048,11 @@ McxStatus DatabusSetInReference(Databus * db, size_t channel, void * reference, return RETURN_ERROR; } - if (CHANNEL_UNKNOWN != type) { + if (ChannelTypeIsValid(type)) { info = &((Channel *)in)->info; - if (info->type != type) { - if (ChannelInfoIsBinary(info) && (type == CHANNEL_BINARY || type == CHANNEL_BINARY_REFERENCE)) { + if (!ChannelTypeEq(info->type, type)) { + // TODO: Remove ChannelInfoIsBinary, use ChannelTypeIsBinary instead? + if (ChannelInfoIsBinary(info) && ChannelTypeIsBinary(type)) { // ok } else { mcx_log(LOG_ERROR, "Ports: Set in-reference: Port %s has mismatching type %s, given: %s", @@ -1344,109 +1065,6 @@ McxStatus DatabusSetInReference(Databus * db, size_t channel, void * reference, return in->SetReference(in, reference, type); } -McxStatus DatabusSetInRefVector(Databus * db, size_t channel, size_t startIdx, size_t endIdx, void * reference, ChannelType type) -{ - VectorChannelInfo * vInfo = NULL; - size_t i = 0; - size_t ii = 0; - if (startIdx > endIdx) { - mcx_log(LOG_ERROR, "Ports: Set in reference vector: Start index %d bigger than end index %d", startIdx, endIdx); - return RETURN_ERROR; - } - if (CHANNEL_UNKNOWN == type) { - mcx_log(LOG_ERROR, "Ports: Set in reference vector: Type of vector needs to be specified"); - return RETURN_ERROR; - } - if (channel > DatabusGetInVectorChannelsNum(db)) { - mcx_log(LOG_ERROR, "Ports: Set in reference vector: Vector port %d does not exist (numer of vector ports=%d)", channel, DatabusGetOutVectorChannelsNum(db)); - return RETURN_ERROR; - } - - vInfo = DatabusGetInVectorChannelInfo(db, channel); - for (i = startIdx; i <= endIdx; i++) { - McxStatus retVal = RETURN_OK; - void * ref = NULL; - ChannelIn * chIn = NULL; - ChannelInfo * chInfo = vInfo->GetElement(vInfo, i); - if (!chInfo) { - mcx_log(LOG_ERROR, "Ports: Set in reference vector: Vector Port does not exist"); - return RETURN_ERROR; - } - chIn = (ChannelIn *) chInfo->channel; - if (!chIn) { - mcx_log(LOG_ERROR, "Ports: Set in reference vector: Vector Port not initialized"); - return RETURN_ERROR; - } - ii = i - startIdx; - if (CHANNEL_DOUBLE == type) { - ref = (void *) (((double *) reference) + ii); - } else if (CHANNEL_INTEGER == type) { - ref = (void *) (((int *) reference) + ii); - } else if (CHANNEL_BOOL == type) { - ref = (void *) (((int *) reference) + ii); - } else { - mcx_log(LOG_ERROR, "Ports: Set in reference vector: Type of vector not allowed"); - return RETURN_ERROR; - } - retVal = chIn->SetReference(chIn, ref, type); - if (RETURN_ERROR == retVal) { - mcx_log(LOG_ERROR, "Ports: Set in reference vector: Reference could not be set"); - return RETURN_ERROR; - } - } - - return RETURN_OK; -} - -McxStatus DatabusSetInRefVectorChannel(Databus * db, size_t channel, - size_t startIdx, size_t endIdx, ChannelValue * value) -{ - VectorChannelInfo * vInfo = NULL; - size_t i = 0; - size_t ii = 0; - ChannelType type = ChannelValueType(value); - - if (startIdx > endIdx) { - mcx_log(LOG_ERROR, "Ports: Set in reference vector: Start index %d bigger than end index %d", startIdx, endIdx); - return RETURN_ERROR; - } - if (CHANNEL_UNKNOWN == type) { - mcx_log(LOG_ERROR, "Ports: Set in reference vector: Type of vector needs to be specified"); - return RETURN_ERROR; - } - if (channel > DatabusGetInVectorChannelsNum(db)) { - mcx_log(LOG_ERROR, "Ports: Set in reference vector: Vector port %d does not exist (numer of vector ports=%d)", channel, DatabusGetInVectorChannelsNum(db)); - return RETURN_ERROR; - } - - vInfo = DatabusGetInVectorChannelInfo(db, channel); - for (i = startIdx; i <= endIdx; i++) { - McxStatus retVal = RETURN_OK; - void * ref = NULL; - ChannelIn * chIn = NULL; - ChannelInfo * chInfo = vInfo->GetElement(vInfo, i); - if (!chInfo) { - mcx_log(LOG_ERROR, "Ports: Set in reference vector: Vector Port does not exist"); - return RETURN_ERROR; - } - chIn = (ChannelIn *) chInfo->channel; - if (!chIn) { - mcx_log(LOG_ERROR, "Ports: Set in reference vector: Vector Port not initialized"); - return RETURN_ERROR; - } - ii = i - startIdx; - ref = (void *) ( &((ChannelValue*)value + ii)->value ); - - retVal = chIn->SetReference(chIn, ref, type); - if (RETURN_ERROR == retVal) { - mcx_log(LOG_ERROR, "Ports: Set in reference vector: Reference could not be set"); - return RETURN_ERROR; - } - } - - return RETURN_OK; -} - static char * DatabusGetUniqueChannelName(Databus * db, const char * name) { #define SUFFIX_LEN 5 @@ -1483,7 +1101,7 @@ static McxStatus DatabusAddLocalChannelInternal(Databus * db, const char * id, const char * unit, const void * reference, - ChannelType type) { + ChannelType * type) { ChannelInfo chInfo = { 0 }; ChannelLocal * local = NULL; Channel * channel = NULL; @@ -1501,7 +1119,7 @@ static McxStatus DatabusAddLocalChannelInternal(Databus * db, } uniqueName = DatabusGetUniqueChannelName(db, name); - retVal = ChannelInfoSetup(&chInfo, uniqueName, NULL, unit, type, id); + retVal = ChannelInfoSetup(&chInfo, uniqueName, uniqueName, NULL, unit, type, id); if (RETURN_OK != retVal) { mcx_log(LOG_ERROR, "Ports: Set local-reference: Setting up ChannelInfo for %s failed", ChannelInfoGetName(&chInfo)); goto cleanup; @@ -1555,7 +1173,7 @@ McxStatus DatabusAddLocalChannel(Databus * db, const char * id, const char * unit, const void * reference, - ChannelType type) { + ChannelType * type) { DatabusData * dbData = db->data; DatabusInfoData * infoData = dbData->localInfo->data; @@ -1584,7 +1202,7 @@ McxStatus DatabusAddRTFactorChannel(Databus * db, const char * id, const char * unit, const void * reference, - ChannelType type) { + ChannelType * type) { DatabusData * dbData = db->data; DatabusInfoData * infoData = dbData->rtfactorInfo->data; @@ -1676,116 +1294,6 @@ ChannelInfo * DatabusGetLocalChannelInfo(Databus * db, size_t channel) { return (ChannelInfo *) data->infos->At(data->infos, channel); } -ChannelInfo * DatabusGetRTFactorChannelInfo(Databus * db, size_t channel) { - DatabusInfoData * data = db->data->rtfactorInfo->data; - - if (channel >= data->infos->Size(data->infos)) { - mcx_log(LOG_ERROR, "Ports: Get rtfactor-info: Unknown port %d", channel); - return NULL; - } - - return (ChannelInfo *) data->infos->At(data->infos, channel); -} - -VectorChannelInfo * DatabusGetInVectorChannelInfo(Databus * db, size_t channel) { - DatabusInfo * inInfo = NULL; - if (!db) { - mcx_log(LOG_ERROR, "Ports: Get in-info: Invalid structure"); - return NULL; - } - - inInfo = DatabusGetInInfo(db); - - return DatabusInfoGetVectorChannelInfo(inInfo, channel); -} - -VectorChannelInfo * DatabusGetOutVectorChannelInfo(Databus * db, size_t channel) { - DatabusInfo * outInfo = NULL; - if (!db) { - mcx_log(LOG_ERROR, "Ports: Get out-info: Invalid structure"); - return NULL; - } - - outInfo = DatabusGetOutInfo(db); - - return DatabusInfoGetVectorChannelInfo(outInfo, channel); -} - -int DatabusChannelInIsValid(Databus * db, size_t channel) { - Channel * in = NULL; - - if (!db) { - return FALSE; - } - - in = (Channel *) DatabusGetInChannel(db, channel); - if (!in) { - return FALSE; - } - - return in->IsValid(in); -} - -int DatabusChannelInIsConnected(struct Databus * db, size_t channel) { - Channel * in = NULL; - - if (!db) { - return FALSE; - } - - in = (Channel *) DatabusGetInChannel(db, channel); - if (!in) { - return FALSE; - } - - return in->IsConnected(in); -} - -int DatabusChannelOutIsValid(Databus * db, size_t channel) { - Channel * out = NULL; - - if (!db) { - return FALSE; - } - - out = (Channel *) DatabusGetOutChannel(db, channel); - if (!out) { - return FALSE; - } - - return out->IsValid(out); -} - -int DatabusChannelLocalIsValid(Databus * db, size_t channel) { - Channel * local = NULL; - - if (!db) { - return FALSE; - } - - local = (Channel *) DatabusGetLocalChannel(db, channel); - if (!local) { - return FALSE; - } - - return local->IsValid(local); -} - -int DatabusChannelRTFactorIsValid(Databus * db, size_t channel) { - Channel * rtfactor = NULL; - - if (!db) { - return FALSE; - } - - rtfactor = (Channel *) DatabusGetRTFactorChannel(db, channel); - if (!rtfactor) { - return FALSE; - } - - return rtfactor->IsValid(rtfactor); -} - McxStatus DatabusCollectModeSwitchData(Databus * db) { Vector * infos = db->data->outInfo->data->infos; size_t size = infos->Size(infos); diff --git a/src/core/Databus.h b/src/core/Databus.h index 70cda7e..7e0df44 100644 --- a/src/core/Databus.h +++ b/src/core/Databus.h @@ -18,7 +18,6 @@ #include "core/channels/Channel.h" #include "core/connections/Connection.h" #include "core/Component.h" -#include "core/channels/VectorChannelInfo.h" #include "reader/model/ports/PortsInput.h" #ifdef __cplusplus @@ -70,6 +69,9 @@ size_t DatabusGetInChannelsNum(struct Databus * db); */ size_t DatabusGetLocalChannelsNum(struct Databus * db); +size_t DatabusGetInChannelsElemNum(Databus * db); +size_t DatabusGetOutChannelsElemNum(Databus * db); + /** * \return The number of rtfactor channels of \a db or -1 if \a db is not initialized * correctly. @@ -86,13 +88,13 @@ size_t DatabusGetRTFactorChannelsNum(struct Databus * db); McxStatus DatabusSetOutReference(struct Databus * db, size_t channel, const void * reference, - ChannelType type); + ChannelType * type); McxStatus DatabusSetOutReferenceFunction(struct Databus * db, size_t channel, const void * reference, - ChannelType type); + ChannelType * type); /** * Connects a variable of type \a type at \a reference to the in channel \a channel @@ -101,7 +103,7 @@ McxStatus DatabusSetOutReferenceFunction(struct Databus * db, * * \return \c RETURN_OK on success, or \c RETURN_ERROR otherwise. */ -McxStatus DatabusSetInReference(struct Databus * db, size_t channel, void * reference, ChannelType type); +McxStatus DatabusSetInReference(struct Databus * db, size_t channel, void * reference, ChannelType * type); /** * Adds a local channel of type \a type at \a reference to the databus \a db. @@ -111,7 +113,7 @@ McxStatus DatabusAddLocalChannel(Databus * db, const char * id, const char * unit, const void * reference, - ChannelType type); + ChannelType * type); /** * Adds a rtfactor channel of type \a type at \a reference to the databus \a db. @@ -121,27 +123,7 @@ McxStatus DatabusAddRTFactorChannel(Databus * db, const char * id, const char * unit, const void * reference, - ChannelType type); - -/* vector channel functions */ - -VectorChannelInfo * DatabusGetInVectorChannelInfo(Databus * db, size_t channel); -VectorChannelInfo * DatabusGetOutVectorChannelInfo(Databus * db, size_t channel); - -size_t DatabusGetInVectorChannelsNum(Databus * db); - -size_t DatabusGetOutVectorChannelsNum(Databus * db); - -McxStatus DatabusSetOutRefVector(Databus * db, size_t channel, - size_t startIdx, size_t endIdx, const void * reference, ChannelType type); - -McxStatus DatabusSetOutRefVectorChannel(Databus * db, size_t channel, - size_t startIdx, size_t endIdx, ChannelValue * value); - -McxStatus DatabusSetInRefVector(Databus * db, size_t channel, - size_t startIdx, size_t endIdx, void * reference, ChannelType type); -McxStatus DatabusSetInRefVectorChannel(Databus * db, size_t channel, - size_t startIdx, size_t endIdx, ChannelValue * value); + ChannelType * type); /** @@ -168,37 +150,6 @@ struct ChannelInfo * DatabusGetInChannelInfo (struct Databus * db, size_t channe */ struct ChannelInfo * DatabusGetOutChannelInfo(struct Databus * db, size_t channel); -/** - * \return \c TRUE if the in channel \a channel in \a db is connected or - * provides a default value, and \c FALSE if it is not connected or \a db or \a - * channel are invalid. - */ -int DatabusChannelInIsValid(struct Databus * db, size_t channel); - -/** - * \return \c TRUE if the in channel \a channel in \a db is connected or - * and \c FALSE otherwise - */ -int DatabusChannelInIsConnected(struct Databus * db, size_t channel); - -/** - * \return \c TRUE if the out channel \a channel in \a db is connected, and \c - * FALSE if it is not connected or \a db or \a channel are invalid. - */ -int DatabusChannelOutIsValid(struct Databus * db, size_t channel); - -/** - * \return \c TRUE if the local channel \a channel in \a db has a reference, and \c - * FALSE if it has no reference or \a db or \a channel are invalid. - */ -int DatabusChannelLocalIsValid(struct Databus * db, size_t channel); - -/** - * \return \c TRUE if the rtfactor channel \a channel in \a db has a reference, and \c - * FALSE if it has no reference or \a db or \a channel are invalid. - */ -int DatabusChannelRTFactorIsValid(struct Databus * db, size_t channel); - /** private interface for Component **/ @@ -341,8 +292,6 @@ typedef struct Databus { size_t modeSwitchDataSize; } Databus; -char * CreateIndexedName(const char * name, unsigned i); - #ifdef __cplusplus } /* closing brace for extern "C" */ #endif /* __cplusplus */ diff --git a/src/core/Databus_impl.h b/src/core/Databus_impl.h index 8527c27..8799e8c 100644 --- a/src/core/Databus_impl.h +++ b/src/core/Databus_impl.h @@ -30,7 +30,6 @@ typedef struct DatabusInfoData { Object _; // base class Vector * infos; - ObjectContainer * origInfos; } DatabusInfoData; diff --git a/src/core/Dependency.h b/src/core/Dependency.h index 861cca9..4a04a03 100644 --- a/src/core/Dependency.h +++ b/src/core/Dependency.h @@ -24,7 +24,7 @@ typedef enum DependencyDef { DEP_FIXED = 3, } Dependency; -struct Dependencies; +typedef struct Dependencies Dependencies; struct Dependencies * DependenciesCreate(size_t numIn, size_t numOut); diff --git a/src/core/Model.c b/src/core/Model.c index 3c7246b..12321cb 100644 --- a/src/core/Model.c +++ b/src/core/Model.c @@ -24,6 +24,7 @@ #include "core/Databus.h" #include "core/channels/Channel.h" +#include "core/channels/ChannelValueReference.h" #include "core/channels/ChannelInfo.h" #include "core/connections/Connection.h" #include "core/connections/FilteredConnection.h" @@ -136,15 +137,15 @@ static void UpdateBinaryChannelTypes(Vector * connInfos, Task * task) { mcx_log(LOG_DEBUG, "Fast binary channel requirements fulfilled for connection %s", buffer); mcx_free(buffer); - trgInfo->type = CHANNEL_BINARY_REFERENCE; - srcInfo->type = CHANNEL_BINARY_REFERENCE; + trgInfo->type = &ChannelTypeBinaryReference; + srcInfo->type = &ChannelTypeBinaryReference; } else { char * buffer = ConnectionInfoConnectionString(info); mcx_log(LOG_DEBUG, "Using binary channels for connection %s", buffer); mcx_free(buffer); - trgInfo->type = CHANNEL_BINARY; - srcInfo->type = CHANNEL_BINARY; + trgInfo->type = &ChannelTypeBinary; + srcInfo->type = &ChannelTypeBinary; } } } @@ -167,7 +168,7 @@ static McxStatus ModelPreprocessConstConnections(Model * model) { mcx_log(LOG_ERROR, "Not enough memory to filter out constant connections"); return RETURN_ERROR; } - filteredConns->Setup(filteredConns, sizeof(ConnectionInfo), ConnectionInfoInit, NULL, NULL); + filteredConns->Setup(filteredConns, sizeof(ConnectionInfo), ConnectionInfoInit, ConnectionInfoSetFrom, DestroyConnectionInfo); for (i = 0; i < conns->Size(conns); i++) { ConnectionInfo * info = (ConnectionInfo *) conns->At(conns, i); @@ -177,10 +178,12 @@ static McxStatus ModelPreprocessConstConnections(Model * model) { CompConstant * srcCompConst = NULL; Databus * srcDb = srcComp->GetDatabus(srcComp); ChannelInfo * srcChannelInfo = DatabusGetOutChannelInfo(srcDb, info->sourceChannel); + ChannelDimension * srcDim = NULL; Component * trgComp = info->targetComponent; Databus * trgDb = trgComp->GetDatabus(trgComp); ChannelInfo * trgChannelInfo = DatabusGetInChannelInfo(trgDb, info->targetChannel); + ChannelDimension * trgDim = NULL; // if not a const conn, add to the filtered conns if (0 != strcmp(compConstantTypeString, srcComp->GetType(srcComp))) { @@ -188,6 +191,11 @@ static McxStatus ModelPreprocessConstConnections(Model * model) { continue; } + if (!ChannelDimensionEq(trgChannelInfo->dimension, info->targetDimension)) { + filteredConns->PushBack(filteredConns, info); + continue; + } + // else kick the connection and update the channel default value srcCompConst = (CompConstant *) srcComp; src = (ChannelValue *) mcx_calloc(1, sizeof(ChannelValue)); @@ -197,40 +205,67 @@ static McxStatus ModelPreprocessConstConnections(Model * model) { goto cleanup_1; } - ChannelValueInit(src, srcChannelInfo->type); + ChannelValueInit(src, ChannelTypeClone(srcChannelInfo->type)); retVal = ChannelValueSet(src, srcCompConst->GetValue(srcCompConst, info->sourceChannel)); if (retVal == RETURN_ERROR) { goto cleanup_1; } + if (!trgChannelInfo->defaultValue) { + trgChannelInfo->defaultValue = (ChannelValue *) mcx_calloc(1, sizeof(ChannelValue)); + ChannelValueInit(trgChannelInfo->defaultValue, ChannelTypeClone(trgChannelInfo->type)); + } + + // prepare slice dimensions + srcDim = CloneChannelDimension(info->sourceDimension); + if (info->sourceDimension && !srcDim) { + mcx_log(LOG_ERROR, "ModelPreprocessConstConnections: Source dimension slice allocation failed"); + goto cleanup_1; + } + + retVal = ChannelDimensionAlignIndicesWithZero(srcDim, srcChannelInfo->dimension); + if (RETURN_ERROR == retVal) { + mcx_log(LOG_ERROR, "ModelPreprocessConstConnections: Source dimension normalization failed"); + goto cleanup_1; + } + + trgDim = CloneChannelDimension(info->targetDimension); + if (info->targetDimension && !trgDim) { + mcx_log(LOG_ERROR, "ModelPreprocessConstConnections: Target dimension slice allocation failed"); + goto cleanup_1; + } + + retVal = ChannelDimensionAlignIndicesWithZero(trgDim, trgChannelInfo->dimension); + if (RETURN_ERROR == retVal) { + mcx_log(LOG_ERROR, "ModelPreprocessConstConnections: Target dimension normalization failed"); + goto cleanup_1; + } + // out channel range and linear conversions - retVal = ConvertRange(srcChannelInfo->min, srcChannelInfo->max, src); + retVal = ConvertRange(srcChannelInfo->min, srcChannelInfo->max, src, srcDim); if (retVal == RETURN_ERROR) { goto cleanup_1; } - retVal = ConvertLinear(srcChannelInfo->scale, srcChannelInfo->offset, src); + retVal = ConvertLinear(srcChannelInfo->scale, srcChannelInfo->offset, src, srcDim); if (retVal == RETURN_ERROR) { goto cleanup_1; } // type conversion - retVal = ConvertType(srcChannelInfo->type, trgChannelInfo->type, src); + retVal = ConvertType(trgChannelInfo->defaultValue, trgDim, src, srcDim); if (retVal == RETURN_ERROR) { goto cleanup_1; } // unit conversion - retVal = ConvertUnit(srcChannelInfo->unitString, trgChannelInfo->unitString, src); + retVal = ConvertUnit(srcChannelInfo->unitString, trgChannelInfo->unitString, trgChannelInfo->defaultValue, trgDim); if (retVal == RETURN_ERROR) { goto cleanup_1; } - // set the default value - if (trgChannelInfo->defaultValue) { - ChannelValueDestructor(trgChannelInfo->defaultValue); - } - trgChannelInfo->defaultValue = src; + object_destroy(srcDim); + object_destroy(trgDim); continue; @@ -240,6 +275,9 @@ static McxStatus ModelPreprocessConstConnections(Model * model) { mcx_free(src); } + object_destroy(srcDim); + object_destroy(trgDim); + retVal = RETURN_ERROR; goto cleanup; } @@ -507,6 +545,35 @@ static McxStatus ModelSignalConnectionsDone(ObjectContainer * comps) { return RETURN_OK; } +int ComponentsHaveVectorChannels(ObjectContainer * components) { + size_t numComps = components->Size(components); + size_t i = 0; + + for (i = 0; i < numComps; i++) { + Component * comp = (Component *) components->At(components, i); + Databus * db = comp->GetDatabus(comp); + size_t numIns = DatabusGetInChannelsNum(db); + size_t numOuts = DatabusGetOutChannelsNum(db); + size_t j = 0; + + for (j = 0; j < numIns; j++) { + ChannelInfo * info = DatabusGetInChannelInfo(db, j); + if (ChannelTypeIsArray(info->type)) { + return TRUE; + } + } + + for (j = 0; j < numOuts; j++) { + ChannelInfo* info = DatabusGetOutChannelInfo(db, j); + if (ChannelTypeIsArray(info->type)) { + return TRUE; + } + } + } + + return FALSE; +} + static McxStatus ModelConnectionsDone(Model * model) { OrderedNodes * orderedNodes = NULL; McxStatus retVal = RETURN_OK; @@ -531,6 +598,13 @@ static McxStatus ModelConnectionsDone(Model * model) { // determine initialization evaluation order if (model->config->cosimInitEnabled) { + // separate triggering of array elements is not supported at the moment + // therefore some models with array ports might not work + if (ComponentsHaveVectorChannels(model->components)) { + mcx_log(LOG_ERROR, "Co-Simulation initialization: Components with vector ports are not supported"); + return RETURN_ERROR; + } + retVal = ModelCreateInitSubModel(model); if (RETURN_ERROR == retVal) { mcx_log(LOG_ERROR, "Model: InitSubModel could not be created"); @@ -677,7 +751,6 @@ static void ModelDestructor(void * self) { object_destroy(model->initialSubModelGenerator); object_destroy(model->subModel); object_destroy(model->initialSubModel); - } static McxStatus ModelReadComponents(void * self, ComponentsInput * input) { @@ -882,12 +955,11 @@ static McxStatus ModelDoComponentConsistencyChecks(Component * comp, void * para for (i = 0; i < numInChannels; i++) { Channel * channel = (Channel *)DatabusGetInChannel(db, i); + ChannelIn * in = (ChannelIn *) channel; ChannelInfo * info = &channel->info; - if ((info->mode == CHANNEL_MANDATORY) - && !channel->IsValid(channel)) { - mcx_log(LOG_ERROR, "Model: %zu. inport (%s) of element %s not connected" - , i+1, ChannelInfoGetName(info), comp->GetName(comp)); + if (info->mode == CHANNEL_MANDATORY && !channel->ProvidesValue(channel)) { + mcx_log(LOG_ERROR, "Model: %zu. inport (%s) of element %s not connected", i+1, ChannelInfoGetName(info), comp->GetName(comp)); return RETURN_ERROR; } } @@ -896,10 +968,8 @@ static McxStatus ModelDoComponentConsistencyChecks(Component * comp, void * para Channel * channel = (Channel *)DatabusGetOutChannel(db, i); ChannelInfo * info = &channel->info; - if ((info->mode == CHANNEL_MANDATORY) - && !channel->IsValid(channel)) { - mcx_log(LOG_ERROR, "Model: %zu. outport (%s) of element %s not connected" - , i+1, ChannelInfoGetName(info), comp->GetName(comp)); + if (info->mode == CHANNEL_MANDATORY && !channel->ProvidesValue(channel)) { + mcx_log(LOG_ERROR, "Model: %zu. outport (%s) of element %s not connected", i+1, ChannelInfoGetName(info), comp->GetName(comp)); return RETURN_ERROR; } } @@ -1293,6 +1363,35 @@ static McxStatus CompUpdateInitOutputs(CompAndGroup * compGroup, void * param) { return retVal; } +static McxStatus CompUpdateInAndOutputs(CompAndGroup * compGroup, void * param) { + Component * comp = compGroup->comp; + double startTime = comp->GetTime(comp); + TimeInterval time = { startTime, startTime }; + McxStatus retVal = RETURN_OK; + + retVal = DatabusTriggerInConnections(comp->GetDatabus(comp), &time); + if (RETURN_ERROR == retVal) { + mcx_log(LOG_ERROR, "Model: Updating inports after initialization loop failed"); + return RETURN_ERROR; + } + + if (comp->UpdateInChannels) { + retVal = comp->UpdateInChannels(comp); + if (RETURN_ERROR == retVal) { + mcx_log(LOG_ERROR, "Model: Updating inports failed"); + return RETURN_ERROR; + } + } + + retVal = ComponentUpdateOutChannels(comp, &time); + if (RETURN_ERROR == retVal) { + mcx_log(LOG_ERROR, "Model: Updating outports after initialization loop failed"); + return RETURN_ERROR; + } + + return retVal; +} + static McxStatus CompUpdateOutputs(CompAndGroup * compGroup, void * param) { Component * comp = compGroup->comp; const Task * task = (const Task *) param; @@ -1347,6 +1446,17 @@ static McxStatus ModelInitialize(Model * model) { return retVal; } + if (model->config->patchWrongInitBehavior) { + // Additional step for faulty elements which return zeroes as out channel values during initialization. + // This makes sure that after the element exits initialization (CompExitInit), + // the output channels contain good values before they get forwarded to the filters (ModelConnectionsExitInitMode) + retVal = subModel->LoopEvaluationList(subModel, CompUpdateInAndOutputs, (void*)model->task); + if (RETURN_ERROR == retVal) { + mcx_log(LOG_ERROR, "Model: Updating element channels failed"); + return RETURN_ERROR; + } + } + retVal = ModelConnectionsExitInitMode(model->components, model->task->params->time); if (RETURN_ERROR == retVal) { mcx_log(LOG_ERROR, "Model: Exiting initialization mode failed"); @@ -1646,7 +1756,7 @@ static Model * ModelCreate(Model * model) { // set to default values model->components = (ObjectContainer *) object_create(ObjectContainer); model->connections = (Vector *) object_create(Vector); - model->connections->Setup(model->connections, sizeof(ConnectionInfo), ConnectionInfoInit, NULL, NULL); + model->connections->Setup(model->connections, sizeof(ConnectionInfo), ConnectionInfoInit, ConnectionInfoSetFrom, DestroyConnectionInfo); model->factory = NULL; model->config = NULL; diff --git a/src/core/Model.h b/src/core/Model.h index 5465ad7..a0fefb5 100644 --- a/src/core/Model.h +++ b/src/core/Model.h @@ -97,6 +97,8 @@ McxStatus ModelConnectionsEnterInitMode(ObjectContainer * comps); McxStatus ModelConnectionsExitInitMode(ObjectContainer * comps, double time); McxStatus ModelDoConnectionsInitialization(ObjectContainer * comps, int onlyIfDecoupled); +int ComponentsHaveVectorChannels(ObjectContainer * components); + #ifdef __cplusplus } /* closing brace for extern "C" */ #endif /* __cplusplus */ diff --git a/src/core/SubModel.c b/src/core/SubModel.c index 87feb06..f55ee35 100644 --- a/src/core/SubModel.c +++ b/src/core/SubModel.c @@ -689,76 +689,101 @@ static struct Dependencies * SubModelGeneratorCreateDependencyMatrix(SubModelGen // initial inputs are always exact if (info->initialValue) { //check if connection exists (cosim init values are not deoupling connections, they only have lower priority than connection values) - ConnectionInfo * info = GetInConnectionInfo(targetComp, targetInChannelID); - if (NULL != info) { - if (ConnectionInfoIsDecoupled(info)) {//decoupled connection + Vector * infos = GetInConnectionInfos(targetComp, targetInChannelID); + size_t numInfos = infos->Size(infos); + size_t i = 0; + + if (numInfos == 0) { // no connections + dependency = DEP_INDEPENDENT; + } else { + int allDecoupled = TRUE; + for (i = 0; i < numInfos; i++) { + ConnectionInfo * info = *(ConnectionInfo**) infos->At(infos, i); + if (!ConnectionInfoIsDecoupled(info)) { + object_destroy(infos); + allDecoupled = FALSE; + break; + } + } + + if (allDecoupled) { // decoupled connections dependency = DEP_INDEPENDENT; } - } else {//no connection - dependency = DEP_INDEPENDENT; } + object_destroy(infos); } } if (DEP_INDEPENDENT != dependency) { - ConnectionInfo * info = GetInConnectionInfo(targetComp, targetInChannelID); - Connection * conn = GetInConnection(targetComp, targetInChannelID); - - if (info - && (info->decoupleType & (DECOUPLE_NEVER | DECOUPLE_IFNEEDED)) - && (!ConnectionInfoIsDecoupled(info)) - && conn - && conn->IsActiveDependency(conn)) - { - Component * sourceComp = info->sourceComponent; - size_t sourceOutGroup, sourceNode; - Databus * db = targetComp->GetDatabus(targetComp); - DatabusInfo * dbInfo = DatabusGetOutInfo(db); - size_t numOutChannels = DatabusInfoGetChannelNum(dbInfo); - - if (INITIAL_DEPENDENCIES == depType) { - sourceOutGroup = sourceComp->GetInitialOutGroup(sourceComp, info->sourceChannel); - } else { - sourceOutGroup = sourceComp->GetOutGroup(sourceComp, info->sourceChannel); - } + Vector * infos = GetInConnectionInfos(targetComp, targetInChannelID); + ObjectContainer * conns = GetInConnections(targetComp, targetInChannelID); + size_t i = 0; + + for (i = 0; i < infos->Size(infos); i++) { + ConnectionInfo * info = *(ConnectionInfo**) infos->At(infos, i); + Connection * conn = conns->At(conns, i); + + if (info && (info->decoupleType & (DECOUPLE_NEVER | DECOUPLE_IFNEEDED)) && (!ConnectionInfoIsDecoupled(info)) && + conn && conn->IsActiveDependency(conn)) + { + Component * sourceComp = info->sourceComponent; + size_t sourceOutGroup, sourceNode; + Databus * db = targetComp->GetDatabus(targetComp); + DatabusInfo * dbInfo = DatabusGetOutInfo(db); + size_t numOutChannels = DatabusInfoGetChannelNum(dbInfo); + + if (INITIAL_DEPENDENCIES == depType) { + sourceOutGroup = sourceComp->GetInitialOutGroup(sourceComp, info->sourceChannel); + } else { + sourceOutGroup = sourceComp->GetOutGroup(sourceComp, info->sourceChannel); + } - sourceNode = SubModelGeneratorGetNodeID(subModelGenerator, sourceComp, sourceOutGroup); + sourceNode = SubModelGeneratorGetNodeID(subModelGenerator, sourceComp, sourceOutGroup); - if (SIZE_T_ERROR == sourceNode) { - // source is not part of this submodel - // -> no dependency -> do nothing - continue; - } + if (SIZE_T_ERROR == sourceNode) { + // source is not part of this submodel + // -> no dependency -> do nothing + continue; + } - if (INITIAL_DEPENDENCIES == depType) { - // check if the target output has an exact initial value - ChannelInfo * info = NULL; - // check if target outputs even exits - if (0 < numOutChannels) { - info = DatabusGetOutChannelInfo(db, targetGroup); - // initial outputs are exact only if specified - if (info->initialValueIsExact && info->initialValue) { - continue; + if (INITIAL_DEPENDENCIES == depType) { + // check if the target output has an exact initial value + ChannelInfo * info = NULL; + // check if target outputs even exits + if (0 < numOutChannels) { + info = DatabusGetOutChannelInfo(db, targetGroup); + // initial outputs are exact only if specified + if (info->initialValueIsExact && info->initialValue) { + continue; + } } } - } - retVal = SetDependency(A, sourceNode, targetNode, DEP_DEPENDENT); - if (RETURN_ERROR == retVal) { - mcx_log(LOG_ERROR, "SetDependency failed in SubModelGeneratorCreateDependencyMatrix"); - mcx_free(A); - return NULL; - } + retVal = SetDependency(A, sourceNode, targetNode, DEP_DEPENDENT); + if (RETURN_ERROR == retVal) { + mcx_log(LOG_ERROR, "SetDependency failed in SubModelGeneratorCreateDependencyMatrix"); + mcx_free(A); + object_destroy(infos); + return NULL; + } - if (0 == numOutChannels && (INITIAL_DEPENDENCIES == depType) ) { - mcx_log(LOG_DEBUG, "(%s,%d) -> (%s,-)", - sourceComp->GetName(sourceComp), sourceOutGroup, - targetComp->GetName(targetComp) ); - } else { - mcx_log(LOG_DEBUG, "(%s,%d) -> (%s,%d)", - sourceComp->GetName(sourceComp), sourceOutGroup, - targetComp->GetName(targetComp), targetGroup); + if (0 == numOutChannels && (INITIAL_DEPENDENCIES == depType)) { + mcx_log(LOG_DEBUG, + "(%s,%zu) -> (%s,-)", + sourceComp->GetName(sourceComp), + sourceOutGroup, + targetComp->GetName(targetComp)); + } else { + mcx_log(LOG_DEBUG, + "(%s,%zu) -> (%s,%zu)", + sourceComp->GetName(sourceComp), + sourceOutGroup, + targetComp->GetName(targetComp), + targetGroup); + } } } + + object_destroy(infos); } } if (targetCompDependency) { diff --git a/src/core/Task.c b/src/core/Task.c index 4882a82..fa63a9f 100644 --- a/src/core/Task.c +++ b/src/core/Task.c @@ -135,6 +135,7 @@ static McxStatus TaskInitialize(Task * task, Model * model) { task->storage->StoreModelOut(task->storage, model->subModel, stepParams->time, STORE_SYNCHRONIZATION); task->storage->StoreModelLocal(task->storage, model->subModel, stepParams->time, STORE_SYNCHRONIZATION); + task->storage->StoreModelRTFactor(task->storage, model->subModel, stepParams->time, STORE_SYNCHRONIZATION); task->stepType->Configure(task->stepType, stepParams, subModel); @@ -233,12 +234,14 @@ static McxStatus TaskRead(Task * task, TaskInput * taskInput) { task->params->timeStepSize = taskInput->deltaTime.defined ? taskInput->deltaTime.value : 0.01; mcx_log(LOG_INFO, " Synchronization time step: %g s", task->params->timeStepSize); - task->params->sumTime = taskInput->sumTime.defined ? taskInput->sumTime.value : FALSE; + task->params->sumTime = taskInput->sumTime.defined ? taskInput->sumTime.value : TRUE; if (task->config && task->config->sumTimeDefined) { task->params->sumTime = task->config->sumTime; } if (task->params->sumTime) { mcx_log(LOG_DEBUG, " Using summation for time calculation"); + } else { + mcx_log(LOG_DEBUG, " Using multiplication for time calculation"); } task->stepTypeType = taskInput->stepType; diff --git a/src/core/channels/Channel.c b/src/core/channels/Channel.c index 6d656d8..73f945a 100644 --- a/src/core/channels/Channel.c +++ b/src/core/channels/Channel.c @@ -10,17 +10,89 @@ #include "CentralParts.h" #include "core/Config.h" +#include "core/channels/ChannelValue.h" #include "core/connections/Connection.h" #include "core/Conversion.h" #include "core/channels/ChannelInfo.h" +#include "core/channels/ChannelValueReference.h" #include "core/channels/Channel.h" +#include "core/channels/ChannelValue.h" #include "core/channels/Channel_impl.h" +#include +#include + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ + +static McxStatus ReportConnStringError(ChannelInfo * chInfo, const char * prefixFmt, ConnectionInfo * connInfo, const char * fmt, ...) { + // create format string + const char * portPrefix = "Port %s: "; + char * connString = NULL; + char * formatString = NULL; + char * prefix = NULL; + + if (connInfo) { + char * connString = ConnectionInfoConnectionString(connInfo); + + prefix = (char *) mcx_calloc(strlen(portPrefix) - 2 /* format specifier %s */ + + strlen(prefixFmt) - (connInfo == NULL ? 0 : 2 /* format specifier %s */) + + (connInfo == NULL ? 0 : connString ? strlen(connString) : strlen("(null)")) + + strlen(ChannelInfoGetLogName(chInfo)) + 1 /* \0 at the end of the string */, + sizeof(char)); + if (!prefix) { + goto cleanup; + } + + sprintf(prefix, portPrefix, ChannelInfoGetLogName(chInfo)); + sprintf(prefix + strlen(prefix), prefixFmt, connString); + } else { + prefix = (char *) mcx_calloc(strlen(portPrefix) - 2 /* format specifier %s */ + strlen(prefixFmt) + + strlen(ChannelInfoGetLogName(chInfo)) + 1 /* \0 at the end of the string */, + sizeof(char)); + if (!prefix) { + goto cleanup; + } + + sprintf(prefix, portPrefix, ChannelInfoGetLogName(chInfo)); + } + + formatString = (char *) mcx_calloc(strlen(fmt) + strlen(prefix) + 1, sizeof(char)); + if (!formatString) { + goto cleanup; + } + + strcat(formatString, prefix); + strcat(formatString, fmt); + + // log error message + va_list args; + va_start(args, fmt); + + mcx_vlog(LOG_ERROR, formatString, args); + + va_end(args); + +cleanup: + // free connection string + if (connString) { + mcx_free(connString); + } + + if (prefix) { + mcx_free(prefix); + } + + if (formatString) { + mcx_free(formatString); + } + + return RETURN_ERROR; +} + // ---------------------------------------------------------------------- // Channel @@ -58,7 +130,7 @@ static Channel * ChannelCreate(Channel * channel) { channel->isDefinedDuringInit = FALSE; channel->internalValue = NULL; - ChannelValueInit(&channel->value, CHANNEL_UNKNOWN); + ChannelValueInit(&channel->value, &ChannelTypeUnknown); channel->Setup = ChannelSetup; channel->IsDefinedDuringInit = ChannelIsDefinedDuringInit; @@ -67,9 +139,10 @@ static Channel * ChannelCreate(Channel * channel) { // virtual functions channel->GetValueReference = NULL; channel->Update = NULL; - channel->IsValid = NULL; + channel->ProvidesValue = NULL; channel->IsConnected = NULL; + channel->IsFullyConnected = NULL; return channel; } @@ -81,13 +154,34 @@ static Channel * ChannelCreate(Channel * channel) { // object that is stored in target component that stores // the channel connection +static void DestroyChannelValueReferencePtr(ChannelValueReference ** ptr) { + DestroyChannelValueReference(*ptr); +} static ChannelInData * ChannelInDataCreate(ChannelInData * data) { - data->connection = NULL; + data->connections = (ObjectContainer *) object_create(ObjectContainer); + if (!data->connections) { + return NULL; + } + + data->valueReferences = (Vector *) object_create(Vector); + if (!data->valueReferences) { + return NULL; + } + + data->valueReferences->Setup(data->valueReferences, sizeof(ChannelValueReference *), NULL, NULL, DestroyChannelValueReferencePtr); + data->reference = NULL; + data->type = ChannelTypeClone(&ChannelTypeUnknown); - data->unitConversion = NULL; - data->typeConversion = NULL; + data->typeConversions = (ObjectContainer *) object_create(ObjectContainer); + if (!data->typeConversions) { + return NULL; + } + data->unitConversions = (ObjectContainer *) object_create(ObjectContainer); + if (!data->unitConversions) { + return NULL; + } data->linearConversion = NULL; data->rangeConversion = NULL; @@ -97,25 +191,37 @@ static ChannelInData * ChannelInDataCreate(ChannelInData * data) { } static void ChannelInDataDestructor(ChannelInData * data) { - if (data->unitConversion) { - object_destroy(data->unitConversion); - } - if (data->typeConversion) { - object_destroy(data->typeConversion); - } + // clean up conversion objects + data->typeConversions->DestroyObjects(data->typeConversions); + object_destroy(data->typeConversions); + + data->unitConversions->DestroyObjects(data->unitConversions); + object_destroy(data->unitConversions); + if (data->linearConversion) { object_destroy(data->linearConversion); } if (data->rangeConversion) { object_destroy(data->rangeConversion); } + if (data->type) { + ChannelTypeDestructor(data->type); + } + + if (data->connections) { + object_destroy(data->connections); + } + + if (data->valueReferences) { + object_destroy(data->valueReferences); + } } OBJECT_CLASS(ChannelInData, Object); -static McxStatus ChannelInSetReference(ChannelIn * in, void * reference, ChannelType type) { +static McxStatus ChannelInSetReference(ChannelIn * in, void * reference, ChannelType * type) { Channel * ch = (Channel *) in; ChannelInfo * info = &ch->info; @@ -128,13 +234,13 @@ static McxStatus ChannelInSetReference(ChannelIn * in, void * reference, Channel return RETURN_ERROR; } - if (CHANNEL_UNKNOWN != type) { + if (ChannelTypeIsValid(type)) { if (!info) { mcx_log(LOG_ERROR, "Port %s: Set inport reference: Port not set up", ChannelInfoGetLogName(info)); return RETURN_ERROR; } - if (info->type != type) { - if (ChannelInfoIsBinary(info) && (type == CHANNEL_BINARY || type == CHANNEL_BINARY_REFERENCE)) { + if (!ChannelTypeEq(info->type, type)) { + if (ChannelInfoIsBinary(info) && ChannelTypeIsBinary(type)) { // ok } else { mcx_log(LOG_ERROR, "Port %s: Set inport reference: Mismatching types", ChannelInfoGetLogName(info)); @@ -144,13 +250,14 @@ static McxStatus ChannelInSetReference(ChannelIn * in, void * reference, Channel } in->data->reference = reference; + in->data->type = ChannelTypeClone(type); return RETURN_OK; } static const void * ChannelInGetValueReference(Channel * channel) { ChannelIn * in = (ChannelIn *) channel; - if (!channel->IsValid(channel)) { + if (!channel->ProvidesValue(channel)) { const static int maxCountError = 10; static int i = 0; if (i < maxCountError) { @@ -164,75 +271,66 @@ static const void * ChannelInGetValueReference(Channel * channel) { return NULL; } - return ChannelValueReference(&channel->value); + return ChannelValueDataPointer(&channel->value); } static McxStatus ChannelInUpdate(Channel * channel, TimeInterval * time) { ChannelIn * in = (ChannelIn *) channel; ChannelInfo * info = &channel->info; - Connection * conn = in->data->connection; McxStatus retVal = RETURN_OK; /* if no connection is present we have nothing to update*/ - if (conn) { - ConnectionInfo * connInfo = NULL; - ChannelValue * val = &channel->value; - - connInfo = &conn->info; - - ChannelValueDestructor(val); - ChannelValueInit(val, ConnectionInfoGetType(connInfo)); + size_t i = 0; + size_t numConns = in->data->connections->Size(in->data->connections); + for (i = 0; i < numConns; i++) { + Connection * conn = (Connection *) in->data->connections->At(in->data->connections, i); + ConnectionInfo * connInfo = &conn->info; + TypeConversion * typeConv = (TypeConversion *) in->data->typeConversions->At(in->data->typeConversions, i); + UnitConversion * unitConv = (UnitConversion *) in->data->unitConversions->At(in->data->unitConversions, i); + ChannelValueReference * valueRef = *(ChannelValueReference **) in->data->valueReferences->At(in->data->valueReferences, i); /* Update the connection for the current time */ - conn->UpdateToOutput(conn, time); - ChannelValueSetFromReference(val, conn->GetValueReference(conn)); - - //type - if (in->data->typeConversion) { - Conversion * conversion = (Conversion *) in->data->typeConversion; - retVal = conversion->convert(conversion, val); - if (RETURN_OK != retVal) { - mcx_log(LOG_ERROR, "Port %s: Update inport: Could not execute type conversion", ChannelInfoGetLogName(info)); - return RETURN_ERROR; - } + if (RETURN_OK != conn->UpdateToOutput(conn, time)) { + return ReportConnStringError(info, "Update inport for connection %s: ", connInfo, "UpdateToOutput failed"); } + // TODO: ideally make conn->GetValueReference return ChannelValueReference + if (RETURN_OK != ChannelValueReferenceSetFromPointer(valueRef, conn->GetValueReference(conn), conn->GetValueDimension(conn), typeConv)) { + return ReportConnStringError(info, "Update inport for connection %s: ", connInfo, "ChannelValueReferenceSetFromPointer failed"); + } - if (info->type == CHANNEL_DOUBLE) { - ChannelValue * val = &channel->value; - // unit - if (in->data->unitConversion) { - Conversion * conversion = (Conversion *) in->data->unitConversion; - retVal = conversion->convert(conversion, val); - if (RETURN_OK != retVal) { - mcx_log(LOG_ERROR, "Port %s: Update inport: Could not execute unit conversion", ChannelInfoGetLogName(info)); - return RETURN_ERROR; - } + // unit conversion + if (unitConv) { + retVal = unitConv->ConvertValueReference(unitConv, valueRef); + if (RETURN_OK != retVal) { + return ReportConnStringError(info, "Update inport for connection %s: ", connInfo, "Unit conversion failed"); } } + } - if (info->type == CHANNEL_DOUBLE || info->type == CHANNEL_INTEGER) { - ChannelValue * val = &channel->value; - // linear - if (in->data->linearConversion) { - Conversion * conversion = (Conversion *) in->data->linearConversion; - retVal = conversion->convert(conversion, val); - if (RETURN_OK != retVal) { - mcx_log(LOG_ERROR, "Port %s: Update inport: Could not execute linear conversion", ChannelInfoGetLogName(info)); - return RETURN_ERROR; - } + // Conversions + if (numConns > 0) { + ChannelValue * val = &channel->value; + + // linear + if (in->data->linearConversion) { + Conversion * conversion = (Conversion *) in->data->linearConversion; + retVal = conversion->convert(conversion, val); + if (RETURN_OK != retVal) { + mcx_log(LOG_ERROR, "Port %s: Update inport: Could not execute linear conversion", ChannelInfoGetLogName(info)); + return RETURN_ERROR; } + } - // range - if (in->data->rangeConversion) { - Conversion * conversion = (Conversion *) in->data->rangeConversion; - retVal = conversion->convert(conversion, val); - if (RETURN_OK != retVal) { - mcx_log(LOG_ERROR, "Port %s: Update inport: Could not execute range conversion", ChannelInfoGetLogName(info)); - return RETURN_ERROR; - } + // range + if (in->data->rangeConversion) { + Conversion * conversion = (Conversion *) in->data->rangeConversion; + retVal = conversion->convert(conversion, val); + if (RETURN_OK != retVal) { + mcx_log(LOG_ERROR, "Port %s: Update inport: Could not execute range conversion", ChannelInfoGetLogName(info)); + return RETURN_ERROR; } } } @@ -242,73 +340,39 @@ static McxStatus ChannelInUpdate(Channel * channel, TimeInterval * time) { return RETURN_OK; } - - switch (info->type) { - case CHANNEL_DOUBLE: -#ifdef MCX_DEBUG - if (time->startTime < MCX_DEBUG_LOG_TIME) { - MCX_DEBUG_LOG("[%f] CH IN (%s) (%f, %f)", time->startTime, ChannelInfoGetLogName(info), time->startTime, * (double *) channel->GetValueReference(channel)); - } -#endif // MCX_DEBUG - * (double *) in->data->reference = * (double *) channel->GetValueReference(channel); - break; - case CHANNEL_INTEGER: - * (int *) in->data->reference = * (int *) channel->GetValueReference(channel); - break; - case CHANNEL_BOOL: - * (int *) in->data->reference = * (int *) channel->GetValueReference(channel); - break; - case CHANNEL_STRING: - { - const void * reference = channel->GetValueReference(channel); - - if (NULL != reference && NULL != * (const char * *) reference ) { - if (* (char * *) in->data->reference) { - mcx_free(* (char * *) in->data->reference); - } - * (char * *) in->data->reference = (char *) mcx_calloc(strlen(* (const char **) reference) + 1, sizeof(char)); - if (* (char * *) in->data->reference) { - strncpy(* (char * *) in->data->reference, * (const char **)reference, strlen(* (const char **)reference) + 1); - } - } - break; + if (time->startTime < MCX_DEBUG_LOG_TIME && ChannelTypeEq(info->type, &ChannelTypeDouble)) { + MCX_DEBUG_LOG("[%f] CH IN (%s) (%f, %f)", time->startTime, ChannelInfoGetLogName(info), time->startTime, * (double *) channel->GetValueReference(channel)); } - case CHANNEL_BINARY: - { - const void * reference = channel->GetValueReference(channel); - if (NULL != reference && NULL != ((const binary_string *) reference)->data) { - if (((binary_string *) in->data->reference)->data) { - mcx_free(((binary_string *) in->data->reference)->data); - } - ((binary_string *) in->data->reference)->len = ((const binary_string *) reference)->len; - ((binary_string *) in->data->reference)->data = (char *) mcx_malloc(((binary_string *) in->data->reference)->len); - if (((binary_string *) in->data->reference)->data) { - memcpy(((binary_string *) in->data->reference)->data, ((const binary_string *) reference)->data, ((binary_string *) in->data->reference)->len); - } - } - break; + if (RETURN_OK != ChannelValueDataSetFromReference(in->data->reference, in->data->type, channel->GetValueReference(channel))) { + return RETURN_ERROR; } - case CHANNEL_BINARY_REFERENCE: - { - const void * reference = channel->GetValueReference(channel); - if (NULL != reference && NULL != ((binary_string *) reference)->data) { - ((binary_string *) in->data->reference)->len = ((binary_string *) reference)->len; - ((binary_string *) in->data->reference)->data = ((binary_string *) reference)->data; - } - break; + return RETURN_OK; +} + +static int ChannelInIsFullyConnected(Channel * channel) { + ChannelIn * in = (ChannelIn *) channel; + size_t i = 0; + size_t connectedElems = 0; + size_t channelNumElems = channel->info.dimension ? ChannelDimensionNumElements(channel->info.dimension) : 1; + + for (i = 0; i < in->data->connections->Size(in->data->connections); i++) { + Connection * conn = (Connection *) in->data->connections->At(in->data->connections, i); + ConnectionInfo * info = &conn->info; + + connectedElems += info->targetDimension ? ChannelDimensionNumElements(info->targetDimension) : 1; } - default: - break; + + if (connectedElems == channelNumElems) { + return TRUE; } - return RETURN_OK; + return FALSE; } -static int ChannelInIsValid(Channel * channel) { - - if (channel->IsConnected(channel)) { +static int ChannelInProvidesValue(Channel * channel) { + if (ChannelInIsFullyConnected(channel)) { return TRUE; } else { ChannelInfo * info = &channel->info; @@ -328,11 +392,11 @@ static int ChannelInIsDiscrete(ChannelIn * in) { } static int ChannelInIsConnected(Channel * channel) { - if (channel->info.type != CHANNEL_UNKNOWN && channel->info.connected) { + if (ChannelTypeIsValid(channel->info.type) && channel->info.connected) { return TRUE; } else { ChannelIn * in = (ChannelIn *) channel; - if (NULL != in->data->connection) { + if (in->data->connections->Size(in->data->connections) > 0) { return TRUE; } } @@ -340,64 +404,114 @@ static int ChannelInIsConnected(Channel * channel) { return FALSE; } -static ConnectionInfo * ChannelInGetConnectionInfo(ChannelIn * in) { - if (in->data->connection) { - return &in->data->connection->info; - } else { +static Vector * ChannelInGetConnectionInfos(ChannelIn * in) { + Vector * infos = (Vector*) object_create(Vector); + size_t numConns = in->data->connections->Size(in->data->connections); + size_t i = 0; + + if (!infos) { return NULL; } -} -static Connection * ChannelInGetConnection(ChannelIn * in) { - if (in->data->connection) { - return in->data->connection; - } else { - return NULL; + infos->Setup(infos, sizeof(ConnectionInfo*), NULL, NULL, NULL); + + for (i = 0; i < numConns; i++) { + Connection * conn = in->data->connections->At(in->data->connections, i); + ConnectionInfo * connInfo = &conn->info; + if (RETURN_ERROR == infos->PushBack(infos, &connInfo)) { + object_destroy(infos); + return NULL; + } } + + return infos; +} + +static ObjectContainer * ChannelInGetConnections(ChannelIn * in) { + return in->data->connections; } -static McxStatus ChannelInSetConnection(ChannelIn * in, Connection * connection, const char * unit, ChannelType type -) { +static McxStatus ChannelInRegisterConnection(ChannelIn * in, Connection * connection, const char * unit, ChannelType * type) { + ConnectionInfo * connInfo = &connection->info; Channel * channel = (Channel *) in; - ChannelInfo * inInfo = NULL; + ChannelInfo * inInfo = &channel->info; + ChannelValueReference * valRef = NULL; - McxStatus retVal; + McxStatus retVal = RETURN_OK; - in->data->connection = connection; - channel->internalValue = connection->GetValueReference(connection); + retVal = in->data->connections->PushBack(in->data->connections, (Object *) connection); + if (RETURN_OK != retVal) { + return ReportConnStringError(inInfo, "Register inport connection %s: ", connInfo, "Could not register connection"); + } - // setup unit conversion - inInfo = &channel->info; + ChannelDimension * dimension = connInfo->targetDimension; + if (dimension && !ChannelDimensionEq(dimension, inInfo->dimension)) { + ChannelDimension * slice = CloneChannelDimension(dimension); - if (inInfo->type == CHANNEL_DOUBLE) { - in->data->unitConversion = (UnitConversion *) object_create(UnitConversion); - retVal = in->data->unitConversion->Setup(in->data->unitConversion, - unit, - inInfo->unitString); - if (RETURN_ERROR == retVal) { - mcx_log(LOG_ERROR, "Port %s: Set inport connection: Could not setup unit conversion", ChannelInfoGetLogName(inInfo)); + retVal = ChannelDimensionAlignIndicesWithZero(slice, inInfo->dimension); + if (retVal == RETURN_ERROR) { + ReportConnStringError(inInfo, "Register inport connection %s: ", connInfo, "Normalizing array slice dimension failed"); return RETURN_ERROR; } - if (in->data->unitConversion->IsEmpty(in->data->unitConversion)) { - object_destroy(in->data->unitConversion); + valRef = MakeChannelValueReference(&channel->value, slice); + } else { + valRef = MakeChannelValueReference(&channel->value, NULL); + } + + retVal = in->data->valueReferences->PushBack(in->data->valueReferences, &valRef); + if (RETURN_ERROR == retVal) { + ReportConnStringError(inInfo, "Register inport connection %s: ", connInfo, "Storing value reference failed"); + return RETURN_ERROR; + } + + if (ChannelTypeEq(ChannelTypeBaseType(inInfo->type), &ChannelTypeDouble)) { + UnitConversion * conversion = (UnitConversion *) object_create(UnitConversion); + + retVal = conversion->Setup(conversion, unit, inInfo->unitString); + if (RETURN_ERROR == retVal) { + return ReportConnStringError(inInfo, "Register inport connection %s: ", connInfo, "Could not set up unit conversion"); + } + + if (conversion->IsEmpty(conversion)) { + object_destroy(conversion); + } + + retVal = in->data->unitConversions->PushBack(in->data->unitConversions, (Object *) conversion); + if (RETURN_ERROR == retVal) { + return ReportConnStringError(inInfo, "Register inport connection %s: ", connInfo, "Could not add unit conversion"); + } + } else { + retVal = in->data->unitConversions->PushBack(in->data->unitConversions, NULL); + if (RETURN_ERROR == retVal) { + return ReportConnStringError(inInfo, "Register inport connection %s: ", connInfo, "Could not add empty unit conversion"); } } // setup type conversion - if (inInfo->type != type) { - in->data->typeConversion = (TypeConversion *) object_create(TypeConversion); - retVal = in->data->typeConversion->Setup(in->data->typeConversion, - type, - inInfo->type); + if (!ChannelTypeConformable(inInfo->type, inInfo->dimension, connection->GetValueType(connection), connection->GetValueDimension(connection))) { + TypeConversion * typeConv = (TypeConversion *) object_create(TypeConversion); + retVal = typeConv->Setup(typeConv, + connection->GetValueType(connection), + connection->GetValueDimension(connection), + inInfo->type, + connInfo->targetDimension); if (RETURN_ERROR == retVal) { - mcx_log(LOG_ERROR, "Port %s: Set connection: Could not setup type conversion", ChannelInfoGetLogName(inInfo)); - return RETURN_ERROR; + return ReportConnStringError(inInfo, "Register inport connection %s: ", connInfo, "Could not set up type conversion"); } - } - return RETURN_OK; + retVal = in->data->typeConversions->PushBack(in->data->typeConversions, (Object *) typeConv); + if (RETURN_ERROR == retVal) { + return ReportConnStringError(inInfo, "Register inport connection %s: ", connInfo, "Could not add type conversion"); + } + } else { + retVal = in->data->typeConversions->PushBack(in->data->typeConversions, NULL); + if (RETURN_ERROR == retVal) { + return ReportConnStringError(inInfo, "Register inport connection %s: ", connInfo, "Could not add empty type conversion"); + } + } + return retVal; } static McxStatus ChannelInSetup(ChannelIn * in, ChannelInfo * info) { @@ -407,35 +521,35 @@ static McxStatus ChannelInSetup(ChannelIn * in, ChannelInfo * info) { retVal = channel->Setup(channel, info); // call base-class function // types - if (info->type == CHANNEL_UNKNOWN) { + if (!ChannelTypeIsValid(info->type)) { mcx_log(LOG_ERROR, "Port %s: Setup inport: Unknown type", ChannelInfoGetLogName(info)); return RETURN_ERROR; } - ChannelValueInit(&channel->value, info->type); + ChannelValueInit(&channel->value, ChannelTypeClone(info->type)); // default value if (info->defaultValue) { ChannelValueSet(&channel->value, info->defaultValue); // apply range and linear conversions immediately - retVal = ConvertRange(info->min, info->max, &channel->value); + retVal = ConvertRange(info->min, info->max, &channel->value, NULL); if (retVal == RETURN_ERROR) { return RETURN_ERROR; } - retVal = ConvertLinear(info->scale, info->offset, &channel->value); + retVal = ConvertLinear(info->scale, info->offset, &channel->value, NULL); if (retVal == RETURN_ERROR) { return RETURN_ERROR; } channel->SetDefinedDuringInit(channel); - channel->internalValue = ChannelValueReference(&channel->value); } // unit conversion is setup when a connection is set // min/max conversions are only used for double types - if (info->type == CHANNEL_DOUBLE || info->type == CHANNEL_INTEGER) + if (ChannelTypeEq(ChannelTypeBaseType(info->type), &ChannelTypeDouble) || + ChannelTypeEq(ChannelTypeBaseType(info->type), &ChannelTypeInteger)) { ChannelValue * min = info->min; ChannelValue * max = info->max; @@ -485,17 +599,18 @@ static ChannelIn * ChannelInCreate(ChannelIn * in) { // virtual functions channel->GetValueReference = ChannelInGetValueReference; - channel->IsValid = ChannelInIsValid; + channel->ProvidesValue = ChannelInProvidesValue; channel->Update = ChannelInUpdate; channel->IsConnected = ChannelInIsConnected; + channel->IsFullyConnected = ChannelInIsFullyConnected; in->Setup = ChannelInSetup; in->SetReference = ChannelInSetReference; - in->GetConnectionInfo = ChannelInGetConnectionInfo; + in->GetConnectionInfos = ChannelInGetConnectionInfos; - in->GetConnection = ChannelInGetConnection; - in->SetConnection = ChannelInSetConnection; + in->GetConnections = ChannelInGetConnections; + in->RegisterConnection = ChannelInRegisterConnection; in->IsDiscrete = ChannelInIsDiscrete; in->SetDiscrete = ChannelInSetDiscrete; @@ -509,6 +624,7 @@ static ChannelIn * ChannelInCreate(ChannelIn * in) { static ChannelOutData * ChannelOutDataCreate(ChannelOutData * data) { data->valueFunction = NULL; + ChannelValueInit(&data->valueFunctionRes, ChannelTypeClone(&ChannelTypeUnknown)); data->rangeConversion = NULL; data->linearConversion = NULL; @@ -540,6 +656,8 @@ static void ChannelOutDataDestructor(ChannelOutData * data) { object_destroy(conns->elements[i]); } object_destroy(data->connections); + + ChannelValueDestructor(&data->valueFunctionRes); } OBJECT_CLASS(ChannelOutData, Object); @@ -560,20 +678,21 @@ static McxStatus ChannelOutSetup(ChannelOut * out, ChannelInfo * info, Config * retVal = channel->Setup(channel, info); // call base-class function // default value - if (info->type == CHANNEL_UNKNOWN) { + if (!ChannelTypeIsValid(info->type)) { mcx_log(LOG_ERROR, "Port %s: Setup outport: Unknown type", ChannelInfoGetLogName(info)); return RETURN_ERROR; } - ChannelValueInit(&channel->value, info->type); + ChannelValueInit(&channel->value, ChannelTypeClone(info->type)); // default value if (info->defaultValue) { - channel->internalValue = ChannelValueReference(channel->info.defaultValue); + channel->internalValue = ChannelValueDataPointer(channel->info.defaultValue); } // min/max conversions are only used for double types - if (info->type == CHANNEL_DOUBLE || info->type == CHANNEL_INTEGER) + if (ChannelTypeEq(ChannelTypeBaseType(info->type), &ChannelTypeDouble) + || ChannelTypeEq(ChannelTypeBaseType(info->type), &ChannelTypeInteger)) { out->data->rangeConversion = (RangeConversion *) object_create(RangeConversion); retVal = out->data->rangeConversion->Setup(out->data->rangeConversion, min, max); @@ -613,6 +732,11 @@ static McxStatus ChannelOutSetup(ChannelOut * out, ChannelInfo * info, Config * static McxStatus ChannelOutRegisterConnection(ChannelOut * out, Connection * connection) { ObjectList * conns = out->data->connections; + ChannelDimension * outDim = ((Channel*)out)->info.dimension; + ChannelDimension * connDim = connection->info.sourceDimension; + + // TODO: do we have to check that channelout and connection match + // in type/dimension? return conns->PushBack(conns, (Object *) connection); } @@ -622,12 +746,12 @@ static const void * ChannelOutGetValueReference(Channel * channel) { ChannelInfo * info = &channel->info; // check if out is initialized - if (!channel->IsValid(channel)) { + if (!channel->ProvidesValue(channel)) { mcx_log(LOG_ERROR, "Port %s: Get value reference: No Value Reference", ChannelInfoGetLogName(info)); return NULL; } - return ChannelValueReference(&channel->value); + return ChannelValueDataPointer(&channel->value); } static const proc * ChannelOutGetFunction(ChannelOut * out) { @@ -638,7 +762,7 @@ static ObjectList * ChannelOutGetConnections(ChannelOut * out) { return out->data->connections; } -static int ChannelOutIsValid(Channel * channel) { +static int ChannelOutProvidesValue(Channel * channel) { return (NULL != channel->internalValue); } @@ -657,7 +781,44 @@ static int ChannelOutIsConnected(Channel * channel) { return FALSE; } -static McxStatus ChannelOutSetReference(ChannelOut * out, const void * reference, ChannelType type) { +static int ChannelOutIsFullyConnected(Channel * channel) { + ChannelOut * out = (ChannelOut *) channel; + + if (ChannelTypeIsArray(&channel->info.type)) { + ObjectList* conns = out->data->connections; + size_t i = 0; + size_t num_elems = ChannelDimensionNumElements(channel->info.dimension); + + int * connected = (int *) mcx_calloc(num_elems, sizeof(int)); + if (!connected) { + mcx_log(LOG_ERROR, "ChannelOutIsFullyConnected: Not enough memory"); + return -1; + } + + for (i = 0; i < conns->Size(conns); i++) { + Connection * conn = (Connection *) out->data->connections->At(out->data->connections, i); + ConnectionInfo * info = &conn->info; + size_t j = 0; + + for (j = 0; j < ChannelDimensionNumElements(info->sourceDimension); i++) { + size_t idx = ChannelDimensionGetIndex(info->sourceDimension, j, channel->info.type->ty.a.dims); + connected[idx] = 1; + } + } + + for (i = 0; i < num_elems; i++) { + if (!connected[i]) { + return FALSE; + } + } + + return TRUE; + } else { + return ChannelOutIsConnected(channel); + } +} + +static McxStatus ChannelOutSetReference(ChannelOut * out, const void * reference, ChannelType * type) { Channel * channel = (Channel *) out; ChannelInfo * info = NULL; @@ -671,13 +832,13 @@ static McxStatus ChannelOutSetReference(ChannelOut * out, const void * reference return RETURN_ERROR; } if (channel->internalValue - && !(info->defaultValue && channel->internalValue == ChannelValueReference(info->defaultValue))) { + && !(info->defaultValue && channel->internalValue == ChannelValueDataPointer(info->defaultValue))) { mcx_log(LOG_ERROR, "Port %s: Set outport reference: Reference already set", ChannelInfoGetLogName(info)); return RETURN_ERROR; } - if (CHANNEL_UNKNOWN != type) { - if (info->type != type) { - if (ChannelInfoIsBinary(info) && (type == CHANNEL_BINARY || type == CHANNEL_BINARY_REFERENCE)) { + if (ChannelTypeIsValid(type)) { + if (!ChannelTypeEq(info->type, type)) { + if (ChannelInfoIsBinary(info) && ChannelTypeIsBinary(type)) { // ok } else { mcx_log(LOG_ERROR, "Port %s: Set outport reference: Mismatching types", ChannelInfoGetLogName(info)); @@ -691,7 +852,7 @@ static McxStatus ChannelOutSetReference(ChannelOut * out, const void * reference return RETURN_OK; } -static McxStatus ChannelOutSetReferenceFunction(ChannelOut * out, const proc * reference, ChannelType type) { +static McxStatus ChannelOutSetReferenceFunction(ChannelOut * out, const proc * reference, ChannelType * type) { Channel * channel = (Channel *) out; ChannelInfo * info = NULL; if (!out) { @@ -700,8 +861,8 @@ static McxStatus ChannelOutSetReferenceFunction(ChannelOut * out, const proc * r } info = &channel->info; - if (CHANNEL_UNKNOWN != type) { - if (info->type != type) { + if (ChannelTypeIsValid(type)) { + if (!ChannelTypeEq(info->type, type)) { mcx_log(LOG_ERROR, "Port %s: Set outport function: Mismatching types", ChannelInfoGetLogName(info)); return RETURN_ERROR; } @@ -715,8 +876,11 @@ static McxStatus ChannelOutSetReferenceFunction(ChannelOut * out, const proc * r // Save channel procedure out->data->valueFunction = (const proc *) reference; + // Initialize (and allocate necessary memory) + ChannelValueInit(&out->data->valueFunctionRes, ChannelTypeClone(type)); + // Setup value reference to point to internal value - channel->internalValue = ChannelValueReference(&channel->value); + channel->internalValue = ChannelValueDataPointer(&channel->value); return RETURN_OK; } @@ -753,17 +917,26 @@ static McxStatus ChannelOutUpdate(Channel * channel, TimeInterval * time) { if (out->GetFunction(out)) { // function value proc * p = (proc *) out->GetFunction(out); - double val = p->fn(time, p->env); + if (p->fn(time, p->env, &out->data->valueFunctionRes) != 0) { + mcx_log(LOG_ERROR, "Port %s: Update outport: Function failed", ChannelInfoGetLogName(info)); + return RETURN_ERROR; + } #ifdef MCX_DEBUG if (time->startTime < MCX_DEBUG_LOG_TIME) { - MCX_DEBUG_LOG("[%f] CH OUT (%s) (%f, %f)", time->startTime, ChannelInfoGetLogName(info), time->startTime, val); + MCX_DEBUG_LOG("[%f] CH OUT (%s) (%f, %f)", + time->startTime, + ChannelInfoGetLogName(info), + time->startTime, + out->data->valueFunctionRes.value.d); } #endif // MCX_DEBUG - ChannelValueSetFromReference(&channel->value, &val); + if (RETURN_OK != ChannelValueSetFromReference(&channel->value, ChannelValueDataPointer(&out->data->valueFunctionRes))) { + return RETURN_ERROR; + } } else { #ifdef MCX_DEBUG if (time->startTime < MCX_DEBUG_LOG_TIME) { - if (CHANNEL_DOUBLE == info->type) { + if (ChannelTypeEq(&ChannelTypeDouble, info->type)) { MCX_DEBUG_LOG("[%f] CH OUT (%s) (%f, %f)", time->startTime, ChannelInfoGetLogName(info), @@ -774,11 +947,15 @@ static McxStatus ChannelOutUpdate(Channel * channel, TimeInterval * time) { } } #endif // MCX_DEBUG - ChannelValueSetFromReference(&channel->value, channel->internalValue); + if (RETURN_OK != ChannelValueSetFromReference(&channel->value, channel->internalValue)) { + mcx_log(LOG_ERROR, "Port %s: Update outport: Setting value failed", ChannelInfoGetLogName(info)); + return RETURN_ERROR; + } } // Apply conversion - if (info->type == CHANNEL_DOUBLE || info->type == CHANNEL_INTEGER) { + if (ChannelTypeEq(ChannelTypeBaseType(info->type), &ChannelTypeDouble) || + ChannelTypeEq(ChannelTypeBaseType(info->type), &ChannelTypeInteger)) { ChannelValue * val = &channel->value; // range @@ -814,7 +991,7 @@ static McxStatus ChannelOutUpdate(Channel * channel, TimeInterval * time) { } - if (CHANNEL_DOUBLE == info->type) { + if (ChannelTypeEq(&ChannelTypeDouble, info->type)) { const double * val = NULL; { @@ -866,9 +1043,10 @@ static ChannelOut * ChannelOutCreate(ChannelOut * out) { // virtual functions channel->GetValueReference = ChannelOutGetValueReference; - channel->IsValid = ChannelOutIsValid; + channel->ProvidesValue = ChannelOutProvidesValue; channel->Update = ChannelOutUpdate; channel->IsConnected = ChannelOutIsConnected; + channel->IsFullyConnected = ChannelOutIsFullyConnected; out->Setup = ChannelOutSetup; out->RegisterConnection = ChannelOutRegisterConnection; @@ -908,14 +1086,14 @@ static McxStatus ChannelLocalUpdate(Channel * channel, TimeInterval * time) { return RETURN_OK; } -static int ChannelLocalIsValid(Channel * channel) { +static int ChannelLocalProvidesValue(Channel * channel) { return (channel->internalValue != NULL); } // TODO: Unify with ChannelOutsetReference (similar code) static McxStatus ChannelLocalSetReference(ChannelLocal * local, const void * reference, - ChannelType type) { + ChannelType * type) { Channel * channel = (Channel *) local; ChannelInfo * info = NULL; @@ -925,12 +1103,12 @@ static McxStatus ChannelLocalSetReference(ChannelLocal * local, return RETURN_ERROR; } if (channel->internalValue - && !(info->defaultValue && channel->internalValue == ChannelValueReference(info->defaultValue))) { + && !(info->defaultValue && channel->internalValue == ChannelValueDataPointer(info->defaultValue))) { mcx_log(LOG_ERROR, "Port %s: Set local value reference: Reference already set", ChannelInfoGetLogName(info)); return RETURN_ERROR; } - if (CHANNEL_UNKNOWN != type) { - if (info->type != type) { + if (ChannelTypeIsValid(type)) { + if (!ChannelTypeEq(info->type, type)) { mcx_log(LOG_ERROR, "Port %s: Set local value reference: Mismatching types", ChannelInfoGetLogName(info)); return RETURN_ERROR; } @@ -956,9 +1134,9 @@ static ChannelLocal * ChannelLocalCreate(ChannelLocal * local) { // virtual functions channel->GetValueReference = ChannelLocalGetValueReference; channel->Update = ChannelLocalUpdate; - channel->IsValid = ChannelLocalIsValid; + channel->ProvidesValue = ChannelLocalProvidesValue; - channel->IsConnected = ChannelLocalIsValid; + channel->IsConnected = ChannelLocalProvidesValue; local->Setup = ChannelLocalSetup; local->SetReference = ChannelLocalSetReference; diff --git a/src/core/channels/Channel.h b/src/core/channels/Channel.h index 9847dc1..52d45d3 100644 --- a/src/core/channels/Channel.h +++ b/src/core/channels/Channel.h @@ -37,6 +37,8 @@ typedef int (* fChannelIsValid)(Channel * channel); typedef int (* fChannelIsConnected)(Channel * channel); +typedef int (* fChannelIsFullyConnected)(Channel * channel); + typedef int (* fChannelIsDefinedDuringInit)(Channel * channel); typedef void (* fChannelSetDefinedDuringInit)(Channel * channel); @@ -82,13 +84,18 @@ struct Channel { * * Returns true if a channel provides a value (connected or default value) */ - fChannelIsValid IsValid; + fChannelIsValid ProvidesValue; /** - * Returns true if a channel is connected + * Returns true if a channel is connected (i.e., atleast one element of the channel) */ fChannelIsConnected IsConnected; + /** + * Returns true if all elements of the channel are connected + */ + fChannelIsFullyConnected IsFullyConnected; + /** * Getter for the flag data->isDefinedDuringInit */ @@ -113,16 +120,13 @@ typedef McxStatus (* fChannelInSetup)(ChannelIn * in, struct ChannelInfo * info) typedef McxStatus (* fChannelInSetReference) (ChannelIn * in, void * reference, - ChannelType type); + ChannelType * type); -typedef struct ConnectionInfo * (* fChannelInGetConnectionInfo)(ChannelIn * in); +typedef struct Vector * (* fChannelInGetConnectionInfos)(ChannelIn * in); -typedef struct Connection * (* fChannelInGetConnection)(ChannelIn * in); +typedef struct ObjectContainer * (* fChannelInGetConnections)(ChannelIn * in); -typedef McxStatus (* fChannelInSetConnection)(ChannelIn * in, - struct Connection * connection, - const char * unit, - ChannelType type); +typedef McxStatus (*fChannelInRegisterConnection)(ChannelIn * in, struct Connection * connection, const char * unit, ChannelType * type); typedef int (*fChannelInIsDiscrete)(ChannelIn * in); typedef void (*fChannelInSetDiscrete)(ChannelIn * in); @@ -150,15 +154,15 @@ struct ChannelIn { /** * Returns the ConnectionInfo of the incoming connection. */ - fChannelInGetConnectionInfo GetConnectionInfo; + fChannelInGetConnectionInfos GetConnectionInfos; - fChannelInGetConnection GetConnection; + fChannelInGetConnections GetConnections; /** * Set the connection from which the channel retrieves the values in the * specified unit. */ - fChannelInSetConnection SetConnection; + fChannelInRegisterConnection RegisterConnection; /** * Returns true if a channel value is discrete @@ -184,10 +188,10 @@ typedef McxStatus (* fChannelOutSetup)(ChannelOut * out, typedef McxStatus (* fChannelOutSetReference) (ChannelOut * out, const void * reference, - ChannelType type); + ChannelType * type); typedef McxStatus (* fChannelOutSetReferenceFunction) (ChannelOut * out, const proc * reference, - ChannelType type); + ChannelType * type); typedef McxStatus (* fChannelOutRegisterConnection)(struct ChannelOut * out, struct Connection * connection); @@ -252,7 +256,7 @@ typedef McxStatus (* fChannelLocalSetup)(ChannelLocal * local, struct ChannelInf typedef McxStatus (* fChannelLocalSetReference) (ChannelLocal * local, const void * reference, - ChannelType type); + ChannelType * type); extern const struct ObjectClass _ChannelLocal; diff --git a/src/core/channels/ChannelDimension.c b/src/core/channels/ChannelDimension.c new file mode 100644 index 0000000..03ef23e --- /dev/null +++ b/src/core/channels/ChannelDimension.c @@ -0,0 +1,331 @@ +/******************************************************************************** + * Copyright (c) 2021 AVL List GmbH and others + * + * This program and the accompanying materials are made available under the + * terms of the Apache Software License 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "core/channels/ChannelDimension.h" + +#include "util/stdlib.h" + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + + +McxStatus ChannelDimensionSetup(ChannelDimension * dimension, size_t num) { + dimension->num = num; + + dimension->startIdxs = (size_t *) mcx_calloc(dimension->num, sizeof(size_t)); + if (!dimension->startIdxs) { + goto error_cleanup; + } + dimension->endIdxs = (size_t *) mcx_calloc(dimension->num, sizeof(size_t)); + if (!dimension->endIdxs) { + goto error_cleanup; + } + + return RETURN_OK; + +error_cleanup: + if (dimension->startIdxs) { mcx_free(dimension->startIdxs); } + if (dimension->endIdxs) { mcx_free(dimension->endIdxs); } + + return RETURN_ERROR; +} + +McxStatus ChannelDimensionSetDimension(ChannelDimension * dimension, size_t dim, size_t start, size_t end) { + if (dim > dimension->num) { + return RETURN_ERROR; + } + + dimension->startIdxs[dim] = start; + dimension->endIdxs[dim] = end; + + return RETURN_OK; +} + +size_t ChannelDimensionNumElements(const ChannelDimension * dimension) { + size_t i = 0; + size_t n = 1; + + if (dimension->num == 0) { + return 0; + } + + for (i = 0; i < dimension->num; i++) { + n *= (dimension->endIdxs[i] - dimension->startIdxs[i] + 1); + } + + return n; +} + +ChannelDimension * CloneChannelDimension(const ChannelDimension * dimension) { + ChannelDimension * clone = NULL; + McxStatus retVal = RETURN_OK; + size_t i = 0; + + if (!dimension) { + return NULL; + } + + clone = MakeChannelDimension(); + if (!clone) { + mcx_log(LOG_ERROR, "CloneChannelDimension: Not enough memory"); + return NULL; + } + + retVal = ChannelDimensionSetup(clone, dimension->num); + if (RETURN_ERROR == retVal) { + mcx_log(LOG_ERROR, "CloneChannelDimension: Channel dimension setup failed"); + goto cleanup; + } + + for (i = 0; i < dimension->num; i++) { + retVal = ChannelDimensionSetDimension(clone, i, dimension->startIdxs[i], dimension->endIdxs[i]); + if (RETURN_ERROR == retVal) { + mcx_log(LOG_ERROR, "CloneChannelDimension: Channel dimension %zu set failed", i); + goto cleanup; + } + } + + return clone; + +cleanup: + object_destroy(clone); + return NULL; +} + +int ChannelDimensionEq(const ChannelDimension * first, const ChannelDimension * second) { + size_t i = 0; + + if (!first && !second) { + return TRUE; + } else if (!first || !second) { + return FALSE; + } + + if (first->num != second->num) { + return FALSE; + } + + for (i = 0; i < first->num; i++) { + if (first->startIdxs[i] != second->startIdxs[i] || first->endIdxs[i] != second->endIdxs[i]) { + return FALSE; + } + } + + return TRUE; +} + +int ChannelDimensionConformsToDimension(const ChannelDimension * first, const ChannelDimension * second) { + size_t i = 0; + + if (!first && !second) { + return TRUE; + } else if (!first || !second) { + return FALSE; + } + + if (first->num != second->num) { + return FALSE; + } + + for (i = 0; i < first->num; i++) { + if (first->endIdxs[i] - first->startIdxs[i] != second->endIdxs[i] - second->startIdxs[i]) { + return FALSE; + } + } + + return TRUE; +} + +int ChannelDimensionConformsTo(const ChannelDimension * dimension, const size_t * dims, size_t numDims) { + size_t i = 0; + if (dimension->num != numDims) { + return 0; + } + + for (i = 0; i < numDims; i++) { + if (dims[i] != (dimension->endIdxs[i] - dimension->startIdxs[i] + 1)) { + return 0; + } + } + + return 1; +} + +int ChannelDimensionIncludedIn(const ChannelDimension * first, const ChannelDimension * second) { + size_t i = 0; + + if (!first && !second) { + return TRUE; + } else if (!first || !second) { + return FALSE; + } + + if (first->num != second->num) { + return FALSE; // only same number of dimensions is comparable + } + + for (i = 0; i < first->num; i++) { + if (first->startIdxs[i] < second->startIdxs[i] || first->startIdxs[i] > second->endIdxs[i]) { + return FALSE; + } + + if (first->endIdxs[i] > second->endIdxs[i] || first->endIdxs[i] < second->startIdxs[i]) { + return FALSE; + } + } + + return TRUE; +} + +size_t ChannelDimensionGetIndex(const ChannelDimension * dimension, size_t elem_idx, const size_t * sizes) { + switch (dimension->num) { + case 1: + { + size_t idx = dimension->startIdxs[0] + elem_idx; + if (idx > dimension->endIdxs[0]) { + mcx_log(LOG_ERROR, "ChannelDimensionGetIndex: Index out of range"); + break; + } + + return idx; + } + case 2: + { + size_t dim_1_slice_size = dimension->endIdxs[1] - dimension->startIdxs[1] + 1; + + size_t slice_idx_0 = elem_idx / dim_1_slice_size + dimension->startIdxs[0]; + size_t slice_idx_1 = elem_idx % dim_1_slice_size + dimension->startIdxs[1]; + + if (slice_idx_0 > dimension->endIdxs[0] || slice_idx_1 > dimension->endIdxs[1]) { + mcx_log(LOG_ERROR, "ChannelDimensionGetIndex: Index out of range"); + break; + } + + return slice_idx_0 * sizes[1] + slice_idx_1; + } + default: + mcx_log(LOG_ERROR, "ChannelDimensionGetIndex: Number of dimensions not supported (%zu)", dimension->num); + break; + } + + return (size_t) -1; +} + +size_t ChannelDimensionGetSliceIndex(const ChannelDimension * dimension, size_t slice_idx, const size_t * dims) { + switch (dimension->num) { + case 1: + { + size_t idx = slice_idx - dimension->startIdxs[0]; + if (idx > dimension->endIdxs[0]) { + mcx_log(LOG_ERROR, "ChannelDimensionGetSliceIndex: Index out of range"); + break; + } + + return idx; + } + case 2: + { + size_t idx_0 = slice_idx / dims[1]; + size_t idx_1 = slice_idx - idx_0 * dims[1]; + + size_t slice_idx_0 = idx_0 - dimension->startIdxs[0]; + size_t slice_idx_1 = idx_1 - dimension->startIdxs[1]; + + size_t dim_1_slice_size = dimension->endIdxs[1] - dimension->startIdxs[1] + 1; + + return slice_idx_0 * dim_1_slice_size * slice_idx_1; + } + default: + mcx_log(LOG_ERROR, "ChannelDimensionGetSliceIndex: Number of dimensions not supported (%zu)", dimension->num); + break; + } + + return (size_t) -1; +} + +char * ChannelDimensionString(const ChannelDimension * dimension) { + char * str = NULL; + size_t length = 0; + size_t i = 0; + int n = 0; + + if (!dimension) { + return NULL; + } + + for (i = 0; i < dimension->num; i++) { + length += 1; // '(' + length += mcx_digits10(dimension->startIdxs[i]); // a + length += 2; // ', ' + length += mcx_digits10(dimension->endIdxs[i]); // b + length += 1; // ')' + } + + length += dimension->num - 1; // spaces between dimensions + length += 2; // '[' at the beginning and ']' at the end + length += 1; // '\0' + + str = (char *) mcx_calloc(sizeof(char), length); + if (!str) { + mcx_log(LOG_ERROR, "ChannelDimensionString: Not enough memory"); + return NULL; + } + + n += sprintf(str, "["); + for (i = 0; i < dimension->num; i++) { + if (i > 0) { + n += sprintf(str + n, " "); + } + n += sprintf(str + n, "(%zu, %zu)", dimension->startIdxs[i], dimension->endIdxs[i]); + } + sprintf(str + n, "]"); + + return str; +} + +McxStatus ChannelDimensionAlignIndicesWithZero(ChannelDimension * target, const ChannelDimension * base) { + size_t i = 0; + if (!target && !base) { + return RETURN_OK; + } else if (!target || !base) { + return RETURN_ERROR; + } + + if (target->num != base->num) { + return RETURN_ERROR; + } + + for (i = 0; i < target->num; i++) { + target->endIdxs[i] -= base->startIdxs[i]; + target->startIdxs[i] -= base->startIdxs[i]; + } + + return RETURN_OK; +} + +ChannelDimension * MakeChannelDimension() { + ChannelDimension * dimension = (ChannelDimension *) mcx_calloc(1, sizeof(ChannelDimension)); + + return dimension; +} + +void DestroyChannelDimension(ChannelDimension * dimension) { + if (dimension->startIdxs) { mcx_free(dimension->startIdxs); } + if (dimension->endIdxs) { mcx_free(dimension->endIdxs); } +} + + + +#ifdef __cplusplus +} /* closing brace for extern "C" */ +#endif /* __cplusplus */ \ No newline at end of file diff --git a/src/core/channels/ChannelDimension.h b/src/core/channels/ChannelDimension.h new file mode 100644 index 0000000..a98d6ef --- /dev/null +++ b/src/core/channels/ChannelDimension.h @@ -0,0 +1,56 @@ +/******************************************************************************** + * Copyright (c) 2021 AVL List GmbH and others + * + * This program and the accompanying materials are made available under the + * terms of the Apache Software License 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#ifndef MCX_CORE_CHANNELS_CHANNEL_DIMENSION_H +#define MCX_CORE_CHANNELS_CHANNEL_DIMENSION_H + +#include "CentralParts.h" + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + + +typedef struct ChannelDimension { + size_t num; + + size_t * startIdxs; + size_t * endIdxs; +} ChannelDimension; + + +ChannelDimension * MakeChannelDimension(); +ChannelDimension * CloneChannelDimension(const ChannelDimension * dimension); +void DestroyChannelDimension(ChannelDimension * dimension); + +McxStatus ChannelDimensionSetup(ChannelDimension * dimension, size_t num); +McxStatus ChannelDimensionSetDimension(ChannelDimension * dimension, size_t dim, size_t start, size_t end); + +int ChannelDimensionEq(const ChannelDimension * first, const ChannelDimension * second); +int ChannelDimensionConformsToDimension(const ChannelDimension * first, const ChannelDimension * second); +int ChannelDimensionConformsTo(const ChannelDimension * dimension, const size_t * dims, size_t numDims); +int ChannelDimensionIncludedIn(const ChannelDimension * first, const ChannelDimension * second); + +size_t ChannelDimensionNumElements(const ChannelDimension* dimension); +char * ChannelDimensionString(const ChannelDimension * dimension); +size_t ChannelDimensionGetIndex(const ChannelDimension * dimension, size_t elem_idx, const size_t * sizes); +size_t ChannelDimensionGetSliceIndex(const ChannelDimension * dimension, size_t slice_idx, const size_t * dims); + +McxStatus ChannelDimensionAlignIndicesWithZero(ChannelDimension * target, const ChannelDimension * base); + + + +#ifdef __cplusplus +} /* closing brace for extern "C" */ +#endif /* __cplusplus */ + +#endif // MCX_CORE_CHANNELS_CHANNEL_DIMENSION_H \ No newline at end of file diff --git a/src/core/channels/ChannelInfo.c b/src/core/channels/ChannelInfo.c index 41e4baf..ff93130 100644 --- a/src/core/channels/ChannelInfo.c +++ b/src/core/channels/ChannelInfo.c @@ -12,6 +12,8 @@ #include "core/channels/ChannelInfo.h" +#include "core/channels/ChannelValue.h" +#include "objects/Object.h" #include "util/string.h" #ifdef __cplusplus @@ -36,7 +38,7 @@ const char * ChannelInfoGetName(const ChannelInfo * info) { } int ChannelInfoIsBinary(const ChannelInfo * info) { - return info->type == CHANNEL_BINARY || info->type == CHANNEL_BINARY_REFERENCE; + return ChannelTypeIsBinary(info->type); } static McxStatus ChannelInfoSetString(char ** dst, const char * src) { @@ -74,13 +76,13 @@ McxStatus ChannelInfoSetUnit(ChannelInfo * info, const char * name) { return ChannelInfoSetString(&info->unitString, name); } -McxStatus ChannelInfoSetType(ChannelInfo * info, ChannelType type) { - if (info->type != CHANNEL_UNKNOWN) { +McxStatus ChannelInfoSetType(ChannelInfo * info, ChannelType * type) { + if (ChannelTypeIsValid(info->type)) { mcx_log(LOG_ERROR, "Port %s: Type already set", ChannelInfoGetLogName(info)); return RETURN_ERROR; } - info->type = type; + info->type = ChannelTypeClone(type); if (ChannelInfoIsBinary(info)) { // the default for binary is off @@ -90,26 +92,21 @@ McxStatus ChannelInfoSetType(ChannelInfo * info, ChannelType type) { return RETURN_OK; } -McxStatus ChannelInfoSetVector(ChannelInfo * info, VectorChannelInfo * vector) { - if (info->vector) { - object_destroy(info->vector); - } - - info->vector = vector; - - return RETURN_OK; -} - McxStatus ChannelInfoSetup(ChannelInfo * info, const char * name, + const char * nameInModel, const char * descr, const char * unit, - ChannelType type, + ChannelType * type, const char * id) { if (name && RETURN_OK != ChannelInfoSetName(info, name)) { mcx_log(LOG_DEBUG, "Port %s: Could not set name", name); return RETURN_ERROR; } + if (nameInModel && RETURN_OK != ChannelInfoSetNameInTool(info, nameInModel)) { + mcx_log(LOG_DEBUG, "Port %s: Could not set name in tool", name); + return RETURN_ERROR; + } if (descr && RETURN_OK != ChannelInfoSetDescription(info, descr)) { mcx_log(LOG_DEBUG, "Port %s: Could not set description", name); return RETURN_ERROR; @@ -171,7 +168,7 @@ McxStatus ChannelInfoSetFrom(ChannelInfo * info, const ChannelInfo * other) { return RETURN_ERROR; } - info->type = other->type; + info->type = ChannelTypeClone(other->type); info->mode = other->mode; info->writeResult = other->writeResult; info->connected = other->connected; @@ -234,12 +231,13 @@ McxStatus ChannelInfoSetFrom(ChannelInfo * info, const ChannelInfo * other) { } } - if (info->vector) { - object_destroy(info->vector); - } - - if (other->vector) { - info->vector = (VectorChannelInfo *) object_strong_reference(other->vector); + object_destroy(info->dimension); + if (other->dimension) { + info->dimension = CloneChannelDimension(other->dimension); + if (!info->dimension) { + mcx_log(LOG_ERROR, "ChannelInfoSetFrom: Failed to set dimension"); + return RETURN_ERROR; + } } return RETURN_OK; @@ -266,19 +264,23 @@ void ChannelInfoDestroy(ChannelInfo * info) { FreeChannelValue(&info->defaultValue); FreeChannelValue(&info->initialValue); - if (info->vector) { - object_destroy(info->vector); + if (info->type) { + ChannelTypeDestructor(info->type); + } + + if (info->dimension) { + object_destroy(info->dimension); } info->channel = NULL; info->initialValueIsExact = FALSE; - info->type = CHANNEL_UNKNOWN; + info->type = ChannelTypeClone(&ChannelTypeUnknown); info->connected = FALSE; info->writeResult = TRUE; } McxStatus ChannelInfoInit(ChannelInfo * info) { - info->vector = NULL; + info->dimension = NULL; info->name = NULL; info->nameInTool = NULL; @@ -295,7 +297,7 @@ McxStatus ChannelInfoInit(ChannelInfo * info) { info->scale = NULL; info->offset = NULL; - info->type = CHANNEL_UNKNOWN; + info->type = ChannelTypeClone(&ChannelTypeUnknown); info->defaultValue = NULL; info->initialValue = NULL; diff --git a/src/core/channels/ChannelInfo.h b/src/core/channels/ChannelInfo.h index 9f8c0a0..adad36d 100644 --- a/src/core/channels/ChannelInfo.h +++ b/src/core/channels/ChannelInfo.h @@ -12,7 +12,8 @@ #define MCX_CORE_CHANNELS_CHANNELINFO_H #include "core/channels/ChannelValue.h" -#include "core/channels/VectorChannelInfo.h" +#include "CentralParts.h" +#include "core/channels/ChannelDimension.h" #include "common/status.h" @@ -24,13 +25,11 @@ extern "C" { typedef struct ChannelInfo { - /* vector must be NULL if this is a scalar. It is the *only* way - * to distinguish between vectors of size 1 and scalar values. - */ - VectorChannelInfo * vector; - struct Channel * channel; + // Channel is a scalar iff dimension == NULL + ChannelDimension * dimension; + char * name; char * nameInTool; char * description; @@ -44,10 +43,11 @@ typedef struct ChannelInfo { ChannelValue * max; ChannelValue * scale; ChannelValue * offset; + ChannelValue * defaultValue; ChannelValue * initialValue; - ChannelType type; + ChannelType * type; int connected; int initialValueIsExact; @@ -66,16 +66,16 @@ McxStatus ChannelInfoSetNameInTool(ChannelInfo * info, const char * name); McxStatus ChannelInfoSetID(ChannelInfo * info, const char * name); McxStatus ChannelInfoSetDescription(ChannelInfo * info, const char * name); McxStatus ChannelInfoSetUnit(ChannelInfo * info, const char * name); -McxStatus ChannelInfoSetType(ChannelInfo * info, ChannelType type); -McxStatus ChannelInfoSetVector(ChannelInfo * info, VectorChannelInfo * vector); +McxStatus ChannelInfoSetType(ChannelInfo * info, ChannelType * type); int ChannelInfoIsBinary(const ChannelInfo * info); McxStatus ChannelInfoSetup(ChannelInfo * info, const char * name, + const char * nameInModel, const char * descr, const char * unit, - ChannelType type, + ChannelType * type, const char * id); McxStatus ChannelInfoSetFrom(ChannelInfo * info, const ChannelInfo * other); diff --git a/src/core/channels/ChannelValue.c b/src/core/channels/ChannelValue.c index 2c4cc8c..1e33f03 100644 --- a/src/core/channels/ChannelValue.c +++ b/src/core/channels/ChannelValue.c @@ -9,35 +9,516 @@ ********************************************************************************/ #include "core/channels/ChannelValue.h" +#include "core/channels/ChannelDimension.h" #include "util/stdlib.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ -void ChannelValueInit(ChannelValue * value, ChannelType type) { +ChannelType ChannelTypeUnknown = { CHANNEL_UNKNOWN, NULL}; +ChannelType ChannelTypeInteger = { CHANNEL_INTEGER, NULL}; +ChannelType ChannelTypeDouble = { CHANNEL_DOUBLE, NULL}; +ChannelType ChannelTypeBool = { CHANNEL_BOOL, NULL}; +ChannelType ChannelTypeString = { CHANNEL_STRING, NULL}; +ChannelType ChannelTypeBinary = { CHANNEL_BINARY, NULL}; +ChannelType ChannelTypeBinaryReference = { CHANNEL_BINARY_REFERENCE, NULL}; + + +char * CreateIndexedName(const char * name, unsigned i) { + size_t len = 0; + char * buffer = NULL; + + len = strlen(name) + (mcx_digits10(i) + 1) + 2 + 1; + + buffer = (char *) mcx_calloc(len, sizeof(char)); + if (!buffer) { + return NULL; + } + + snprintf(buffer, len, "%s[%d]", name, i); + + return buffer; +} + + +ChannelType * ChannelTypeClone(ChannelType * type) { + switch (type->con) { + case CHANNEL_UNKNOWN: + case CHANNEL_INTEGER: + case CHANNEL_DOUBLE: + case CHANNEL_BOOL: + case CHANNEL_STRING: + case CHANNEL_BINARY: + case CHANNEL_BINARY_REFERENCE: + // Scalar types are used statically (&ChannelTypeDouble, etc) + return type; + case CHANNEL_ARRAY: { + ChannelType * clone = (ChannelType *) mcx_calloc(sizeof(ChannelType), 1); + if (!clone) { return NULL; } + + clone->con = type->con; + + clone->ty.a.inner = ChannelTypeClone(type->ty.a.inner); + clone->ty.a.numDims = type->ty.a.numDims; + clone->ty.a.dims = mcx_copy(type->ty.a.dims, sizeof(size_t) * type->ty.a.numDims); + if (!clone->ty.a.dims) { + mcx_free(clone); + return NULL; + } + + return clone; + } + } + + return NULL; +} + +void ChannelTypeDestructor(ChannelType * type) { + if (&ChannelTypeUnknown == type) { } + else if (&ChannelTypeInteger == type) { } + else if (&ChannelTypeDouble == type) { } + else if (&ChannelTypeBool == type) { } + else if (&ChannelTypeString == type) { } + else if (&ChannelTypeBinary == type) { } + else if (&ChannelTypeBinaryReference == type) { } + else if (type->con == CHANNEL_ARRAY) { + // other ChannelTypes are static + ChannelTypeDestructor(type->ty.a.inner); + mcx_free(type->ty.a.dims); + mcx_free(type); + } else { + mcx_free(type); + } +} + +ChannelType * ChannelTypeArray(ChannelType * inner, size_t numDims, size_t * dims) { + ChannelType * array = NULL; + + if (!inner) { + return &ChannelTypeUnknown; + } + + array = (ChannelType *) mcx_malloc(sizeof(ChannelType)); + if (!array) { + return &ChannelTypeUnknown; + } + + array->con = CHANNEL_ARRAY; + array->ty.a.inner = inner; + array->ty.a.numDims = numDims; + array->ty.a.dims = (size_t *) mcx_calloc(sizeof(size_t), numDims); + if (!array->ty.a.dims) { + return &ChannelTypeUnknown; + } + + memcpy(array->ty.a.dims, dims, sizeof(size_t)*numDims); + + return array; +} + +ChannelType * ChannelTypeArrayLongDims(ChannelType * inner, size_t numDims, unsigned long * dims) { + ChannelType * array = NULL; + size_t i = 0; + + if (!inner) { + return &ChannelTypeUnknown; + } + + array = (ChannelType *) mcx_malloc(sizeof(ChannelType)); + if (!array) { + return &ChannelTypeUnknown; + } + + array->con = CHANNEL_ARRAY; + array->ty.a.inner = inner; + array->ty.a.numDims = numDims; + array->ty.a.dims = (size_t *) mcx_calloc(sizeof(size_t), numDims); + if (!array->ty.a.dims) { + return &ChannelTypeUnknown; + } + + for (i = 0; i < numDims; i++) { + array->ty.a.dims[i] = dims[i]; + } + + return array; +} + +ChannelType * ChannelTypeArrayInner(ChannelType * array) { + if (!ChannelTypeIsArray(array)) { + return &ChannelTypeUnknown; + } + + return array->ty.a.inner; +} + +int ChannelTypeIsValid(const ChannelType * a) { + return a->con != CHANNEL_UNKNOWN; +} + +int ChannelTypeIsScalar(const ChannelType * a) { + return a->con != CHANNEL_ARRAY; +} + +int ChannelTypeIsArray(const ChannelType * a) { + return a->con == CHANNEL_ARRAY; +} + +int ChannelTypeIsBinary(const ChannelType * a) { + return a->con == CHANNEL_BINARY || a->con == CHANNEL_BINARY_REFERENCE; +} + +ChannelType * ChannelTypeBaseType(const ChannelType * a) { + if (ChannelTypeIsArray(a)) { + return ChannelTypeBaseType(a->ty.a.inner); + } else { + return a; + } +} + +size_t ChannelTypeNumElements(const ChannelType * type) { + if (ChannelTypeIsArray(type)) { + size_t i = 0; + size_t num_elems = 1; + + for (i = 0; i < type->ty.a.numDims; i++) { + num_elems *= type->ty.a.dims[i]; + } + + return num_elems; + } else { + return 1; + } +} + +int ChannelTypeConformable(ChannelType * a, ChannelDimension * sliceA, ChannelType * b, ChannelDimension * sliceB) { + if (a->con == CHANNEL_ARRAY && b->con == CHANNEL_ARRAY) { + if (sliceA && sliceB) { + return a->ty.a.inner == b->ty.a.inner && ChannelDimensionConformsToDimension(sliceA, sliceB); + } else if (sliceA && !sliceB) { + return a->ty.a.inner == b->ty.a.inner && ChannelDimensionConformsTo(sliceA, b->ty.a.dims, b->ty.a.numDims); + } else if (!sliceA && sliceB) { + return a->ty.a.inner == b->ty.a.inner && ChannelDimensionConformsTo(sliceB, a->ty.a.dims, a->ty.a.numDims); + } else { + return ChannelTypeEq(a, b); + } + } else { + if (sliceA || sliceB) { + return 0; + } + + return a->con == b->con; + } +} + +int ChannelTypeEq(const ChannelType * a, const ChannelType * b) { + if (a->con == CHANNEL_ARRAY && b->con == CHANNEL_ARRAY) { + size_t i = 0; + if (a->ty.a.numDims != b->ty.a.numDims) { + return 0; + } + for (i = 0; i < a->ty.a.numDims; i++) { + if (a->ty.a.dims[i] != b->ty.a.dims[i]) { + return 0; + } + } + return a->ty.a.inner == b->ty.a.inner; + return 1; + } else { + return a->con == b->con; + } +} + +McxStatus mcx_array_init(mcx_array * a, size_t numDims, size_t * dims, ChannelType * inner) { + a->numDims = numDims; + a->dims = (size_t *) mcx_calloc(sizeof(size_t), numDims); + if (!a->dims) { + return RETURN_ERROR; + } + memcpy(a->dims, dims, sizeof(size_t) * numDims); + + a->type = inner; + a->data = (void *) mcx_calloc(ChannelValueTypeSize(inner), mcx_array_num_elements(a)); + if (!a->data) { + return RETURN_ERROR; + } + + return RETURN_OK; +} + +void mcx_array_destroy(mcx_array * a) { + if (a->dims) { mcx_free(a->dims); } + if (a->data) { mcx_free(a->data); } + if (a->type) { ChannelTypeDestructor(a->type); } +} + +int mcx_array_all(mcx_array * a, mcx_array_predicate_f_ptr predicate) { + size_t i = 0; + ChannelValueData element = {0}; + + for (i = 0; i < mcx_array_num_elements(a); i++) { + if (RETURN_OK != mcx_array_get_elem(a, i, &element)) { + mcx_log(LOG_WARNING, "mcx_array_all: Getting element %zu failed", i); + return 0; + } + + if (!predicate(&element, a->type)) { + return 0; + } + } + + return 1; +} + +int mcx_array_leq(const mcx_array * left, const mcx_array * right) { + size_t numElems = 0; + size_t i = 0; + + if (!ChannelTypeEq(left->type, right->type)) { + return 0; + } + + numElems = mcx_array_num_elements(left); + + for (i = 0; i < numElems; i++) { + switch (left->type->con) { + case CHANNEL_DOUBLE: + if (((double *)left->data)[i] > ((double*)right->data)[i]) { + return 0; + } + break; + case CHANNEL_INTEGER: + if (((int *) left->data)[i] > ((int *) right->data)[i]) { + return 0; + } + break; + default: + return 0; + } + } + + return 1; +} + +int mcx_array_dims_match(mcx_array * a, mcx_array * b) { + size_t i = 0; + + if (a->numDims != b->numDims) { + return 0; + } + if (a->dims == NULL || b->dims == NULL) { + return 0; + } + + for (i = 0; i < a->numDims; i++) { + if (a->dims[i] != b->dims[i]) { + return 0; + } + } + + return 1; +} + +size_t mcx_array_num_elements(const mcx_array * a) { + size_t i = 0; + size_t n = 1; + + if (a->numDims == 0) { + return 0; + } + + for (i = 0; i < a->numDims; i++) { + n *= a->dims[i]; + } + + return n; +} + +McxStatus mcx_array_map(mcx_array * a, mcx_array_map_f_ptr fn, void * ctx) { + size_t num_elems = mcx_array_num_elements(a); + size_t i = 0; + + for (i = 0; i < num_elems; i++) { + if (fn((char *) a->data + i * ChannelValueTypeSize(a->type), i, a->type, ctx)) { + return RETURN_ERROR; + } + } + + return RETURN_OK; +} + +McxStatus mcx_array_get_elem(const mcx_array * a, size_t idx, ChannelValueData * element) { + size_t num_elems = mcx_array_num_elements(a); + + if (idx >= num_elems) { + mcx_log(LOG_ERROR, "mcx_array_get_elem: Array index out of range (idx: %zu, num_elems: %zu)", idx, num_elems); + return RETURN_ERROR; + } + + switch (a->type->con) { + case CHANNEL_DOUBLE: + ChannelValueDataSetFromReference(element, a->type, ((double*)a->data) + idx); + break; + case CHANNEL_INTEGER: + case CHANNEL_BOOL: + ChannelValueDataSetFromReference(element, a->type, ((int *) a->data) + idx); + break; + case CHANNEL_STRING: + ChannelValueDataSetFromReference(element, a->type, ((char **) a->data) + idx); + break; + case CHANNEL_BINARY: + case CHANNEL_BINARY_REFERENCE: + ChannelValueDataSetFromReference(element, a->type, ((binary_string *) a->data) + idx); + break; + case CHANNEL_ARRAY: + ChannelValueDataSetFromReference(element, a->type, ((mcx_array *) a->data) + idx); + break; + default: + mcx_log(LOG_ERROR, "mcx_array_get_elem: Unknown array type"); + return RETURN_ERROR; + } + + return RETURN_OK; +} + +McxStatus mcx_array_set_elem(mcx_array * a, size_t idx, ChannelValueData * element) { + size_t num_elems = mcx_array_num_elements(a); + + if (idx >= num_elems) { + mcx_log(LOG_ERROR, "mcx_array_set_elem: Array index out of range (idx: %zu, num_elems: %zu)", idx, num_elems); + return RETURN_ERROR; + } + + switch (a->type->con) { + case CHANNEL_DOUBLE: + *((double *) a->data + idx) = element->d; + break; + case CHANNEL_INTEGER: + *((int *) a->data + idx) = element->i; + break; + case CHANNEL_BOOL: + *((int *) a->data + idx) = element->i != 0 ? 1 : 0; + break; + case CHANNEL_STRING: + { + char ** elements = (char **) a->data; + + if (elements[idx]) { + mcx_free(elements[idx]); + } + + elements[idx] = (char *) mcx_calloc(strlen(element->s) + 1, sizeof(char)); + if (!elements[idx]) { + mcx_log(LOG_ERROR, "mcx_array_set_elem: Not enough memory"); + return RETURN_ERROR; + } + + strncpy(elements[idx], element->s, strlen(element->s) + 1); + break; + } + case CHANNEL_BINARY: + { + binary_string * elements = (binary_string *) a->data; + if (elements[idx].data) { + mcx_free(elements[idx].data); + } + + elements[idx].len = element->b.len; + elements[idx].data = (char *) mcx_calloc(elements[idx].len, 1); + if (!elements[idx].data) { + mcx_log(LOG_ERROR, "mcx_array_set_elem: Not enough memory"); + return RETURN_ERROR; + } + memcpy(elements[idx].data, element->b.data, elements[idx].len); + break; + } + case CHANNEL_BINARY_REFERENCE: + ((binary_string *) a->data)[idx].len = element->b.len; + ((binary_string *) a->data)[idx].data = element->b.data; + break; + case CHANNEL_ARRAY: + mcx_log(LOG_ERROR, "mcx_array_set_elem: Nested arrays are not supported"); + return RETURN_ERROR; + default: + mcx_log(LOG_ERROR, "mcx_array_set_elem: Unknown array type"); + return RETURN_ERROR; + } + + return RETURN_OK; +} + +void * mcx_array_get_elem_reference(const mcx_array * a, size_t idx) { + size_t num_elems = mcx_array_num_elements(a); + char * data = (char *) a->data; + + if (idx >= num_elems) { + mcx_log(LOG_ERROR, "mcx_array_get_elem_reference: Array index out of range (idx: %zu, num_elems: %zu)", idx, num_elems); + return NULL; + } + + return data + idx * ChannelValueTypeSize(a->type); +} + +ChannelType * ChannelTypeFromDimension(ChannelType * base_type, ChannelDimension * dimension) { + if (dimension) { + // source data is an array + size_t i = 0; + ChannelType * type = NULL; + size_t * dims = (size_t *) mcx_calloc(sizeof(size_t), dimension->num); + + if (!dims) { + mcx_log(LOG_ERROR, "DetermineSliceType: Not enough memory for dimension calculation"); + return NULL; + } + + for (i = 0; i < dimension->num; i++) { + dims[i] = dimension->endIdxs[i] - dimension->startIdxs[i] + 1; // indices are inclusive + } + + type = ChannelTypeArray(ChannelTypeClone(ChannelTypeBaseType(base_type)), dimension->num, dims); + mcx_free(dims); + return type; + } else { + // source data is a scalar + return ChannelTypeClone(base_type); + } +} + +void ChannelValueInit(ChannelValue * value, ChannelType * type) { value->type = type; ChannelValueDataInit(&value->value, type); } -void ChannelValueDataDestructor(ChannelValueData * data, ChannelType type) { - if (type == CHANNEL_STRING) { +void ChannelValueDataDestructor(ChannelValueData * data, ChannelType * type) { + if (type->con == CHANNEL_STRING) { if (data->s) { mcx_free(data->s); data->s = NULL; } - } else if (type == CHANNEL_BINARY) { + } else if (type->con == CHANNEL_BINARY) { if (data->b.data) { mcx_free(data->b.data); data->b.data = NULL; } - } else if (type == CHANNEL_BINARY_REFERENCE) { + } else if (type->con == CHANNEL_BINARY_REFERENCE) { // do not free references to binary, they are not owned by the ChannelValueData + } else if (type->con == CHANNEL_ARRAY) { + if (data->a.dims) { + + mcx_free(data->a.dims); + data->a.dims = NULL; + } + if (data->a.data) { + mcx_free(data->a.data); + data->a.data = NULL; + } } } void ChannelValueDestructor(ChannelValue * value) { ChannelValueDataDestructor(&value->value, value->type); + ChannelTypeDestructor(value->type); } static int isSpecialChar(unsigned char c) { @@ -45,6 +526,19 @@ static int isSpecialChar(unsigned char c) { return (c < ' ' || c > '~'); } +size_t ChannelValueDataDoubleToBuffer(char * buffer, void * value, size_t i) { + const size_t precision = 13; + return sprintf(buffer, "%*.*E", (unsigned) precision, (unsigned) precision, ((double *) value)[i]); +} + +size_t ChannelValueDataIntegerToBuffer(char * buffer, void * value, size_t i) { + return sprintf(buffer, "%d", ((int *) value)[i]); +} + +size_t ChannelValueDataBoolToBuffer(char * buffer, void * value, size_t i) { + return sprintf(buffer, "%1d", (((int *) value)[i] != 0) ? 1 : 0); +} + char * ChannelValueToString(ChannelValue * value) { size_t i = 0; size_t length = 0; @@ -52,14 +546,14 @@ char * ChannelValueToString(ChannelValue * value) { const uint32_t digits_of_exp = 4; // = (mcx_digits10(DBL_MAX_10_EXP) + 1 /* sign */ char * buffer = NULL; - switch (value->type) { + switch (value->type->con) { case CHANNEL_DOUBLE: length = 1 /* sign */ + 1 /* pre decimal place */ + 1 /* dot */ + precision + digits_of_exp + 1 /* string termination */; buffer = (char *) mcx_malloc(sizeof(char) * length); if (!buffer) { return NULL; } - sprintf(buffer, "%*.*E", (unsigned) precision, (unsigned) precision, value->value.d); + ChannelValueDataDoubleToBuffer(buffer, &value->value.d, 0); break; case CHANNEL_INTEGER: length = 1 /* sign */ + mcx_digits10(abs(value->value.i)) + 1 /* string termination*/; @@ -67,7 +561,7 @@ char * ChannelValueToString(ChannelValue * value) { if (!buffer) { return NULL; } - sprintf(buffer, "%d", value->value.i); + ChannelValueDataIntegerToBuffer(buffer, &value->value.i, 0); break; case CHANNEL_BOOL: length = 2; @@ -75,7 +569,7 @@ char * ChannelValueToString(ChannelValue * value) { if (!buffer) { return NULL; } - sprintf(buffer, "%1d", (value->value.i != 0) ? 1 : 0); + ChannelValueDataBoolToBuffer(buffer, &value->value.i, 0); break; case CHANNEL_STRING: if (!value->value.s) { @@ -107,10 +601,49 @@ char * ChannelValueToString(ChannelValue * value) { size_t i = 0; for (i = 0; i < value->value.b.len; i++) { - sprintf(buffer + (4 * i), "\\x%02x", value->value.b.data[i]); + // specifier: x (hex integer) -----+ + // length: 2 (unsigned char) -----+| + // width: 2 --------------------+ || + // flag: 0-padded -------------+| || + // || || + // string literal "\x" ------+ || || + sprintf(buffer + (4 * i), "\\x%02hhx", value->value.b.data[i]); + } + } + break; + case CHANNEL_ARRAY:{ + size_t (*fmt)(char * buffer, void * value, size_t i); + + if (ChannelTypeEq(ChannelTypeArrayInner(value->type), &ChannelTypeDouble)) { + fmt = ChannelValueDataDoubleToBuffer; + } else if (ChannelTypeEq(ChannelTypeArrayInner(value->type), &ChannelTypeInteger)) { + fmt = ChannelValueDataIntegerToBuffer; + } else if (ChannelTypeEq(ChannelTypeArrayInner(value->type), &ChannelTypeBool)) { + fmt = ChannelValueDataBoolToBuffer; + } else { + return NULL; + } + + length = 1 /* sign */ + 1 /* pre decimal place */ + 1 /* dot */ + precision + digits_of_exp + 1 /* string termination */; + length *= mcx_array_num_elements(&value->value.a); + buffer = (char *) mcx_malloc(sizeof(char) * length); + if (!buffer) { + return NULL; + } + + if (mcx_array_num_elements(&value->value.a) > 0) { + size_t i = 0; + size_t n = 0; + + n += fmt(buffer + n, value->value.a.data, 0); + for (i = 1; i < mcx_array_num_elements(&value->value.a); i++) { + n += sprintf(buffer + n, ","); + n += fmt(buffer + n, value->value.a.data, i); } } + break; + } default: return NULL; } @@ -118,18 +651,18 @@ char * ChannelValueToString(ChannelValue * value) { return buffer; } -McxStatus ChannelValueDataToStringBuffer(const ChannelValueData * value, ChannelType type, char * buffer, size_t len) { +McxStatus ChannelValueDataToStringBuffer(const ChannelValueData * value, ChannelType * type, char * buffer, size_t len) { size_t i = 0; size_t length = 0; const size_t precision = 13; const uint32_t digits_of_exp = 4; // = (mcx_digits10(DBL_MAX_10_EXP) + 1 /* sign */ const char * doubleFmt = "%*.*E"; - switch (type) { + switch (type->con) { case CHANNEL_DOUBLE: length = 1 /* sign */ + 1 /* pre decimal place */ + 1 /* dot */ + precision + digits_of_exp + 1 /* string termination */; if (len < length) { - mcx_log(LOG_ERROR, "Port value to string: buffer too short. Needed: %d, given: %d", length, len); + mcx_log(LOG_ERROR, "Port value to string: buffer too short. Needed: %zu, given: %zu", length, len); return RETURN_ERROR; } sprintf(buffer, doubleFmt, (unsigned)precision, (unsigned)precision, value->d); @@ -138,7 +671,7 @@ McxStatus ChannelValueDataToStringBuffer(const ChannelValueData * value, Channel length = 1 /* sign */ + mcx_digits10(abs(value->i)) + 1 /* string termination*/; if (len < length) { - mcx_log(LOG_ERROR, "Port value to string: buffer too short. Needed: %d, given: %d", length, len); + mcx_log(LOG_ERROR, "Port value to string: buffer too short. Needed: %zu, given: %zu", length, len); return RETURN_ERROR; } sprintf(buffer, "%d", value->i); @@ -146,7 +679,7 @@ McxStatus ChannelValueDataToStringBuffer(const ChannelValueData * value, Channel case CHANNEL_BOOL: length = 2; if (len < length) { - mcx_log(LOG_ERROR, "Port value to string: buffer too short. Needed: %d, given: %d", length, len); + mcx_log(LOG_ERROR, "Port value to string: buffer too short. Needed: %zu, given: %zu", length, len); return RETURN_ERROR; } sprintf(buffer, "%1d", (value->i != 0) ? 1 : 0); @@ -158,7 +691,7 @@ McxStatus ChannelValueDataToStringBuffer(const ChannelValueData * value, Channel } length = strlen(value->s) + 1 /* string termination */; if (len < length) { - mcx_log(LOG_ERROR, "Port value to string: buffer too short. Needed: %d, given: %d", length, len); + mcx_log(LOG_ERROR, "Port value to string: buffer too short. Needed: %zu, given: %zu", length, len); return RETURN_ERROR; } sprintf(buffer, "%s", value->s); @@ -173,7 +706,7 @@ McxStatus ChannelValueDataToStringBuffer(const ChannelValueData * value, Channel case CHANNEL_BINARY_REFERENCE: length = value->b.len * 4 + 1; if (len < length) { - mcx_log(LOG_DEBUG, "Port value to string: buffer too short. Needed: %d, given: %d", length, len); + mcx_log(LOG_DEBUG, "Port value to string: buffer too short. Needed: %zu, given: %zu", length, len); return RETURN_ERROR; } { @@ -184,6 +717,19 @@ McxStatus ChannelValueDataToStringBuffer(const ChannelValueData * value, Channel } } break; + case CHANNEL_ARRAY: { + const char * doubleFmt = "% *.*E"; + + length = 1 /* sign */ + 1 /* pre decimal place */ + 1 /* dot */ + precision + digits_of_exp + 1 /* string termination */; + if (len < length) { + mcx_log(LOG_ERROR, "Port value to string: buffer too short. Needed: %zu, given: %zu", length, len); + return RETURN_ERROR; + } + sprintf(buffer, doubleFmt, (unsigned)precision, (unsigned)precision, *(double *)value->a.data); + + + break; + } default: mcx_log(LOG_DEBUG, "Port value to string: Unknown type"); return RETURN_ERROR; @@ -196,20 +742,20 @@ McxStatus ChannelValueToStringBuffer(const ChannelValue * value, char * buffer, return ChannelValueDataToStringBuffer(&value->value, value->type, buffer, len); } -ChannelType ChannelValueType(ChannelValue * value) { +ChannelType * ChannelValueType(ChannelValue * value) { return value->type; } -void * ChannelValueReference(ChannelValue * value) { - if (value->type == CHANNEL_UNKNOWN) { +void * ChannelValueDataPointer(ChannelValue * value) { + if (value->type->con == CHANNEL_UNKNOWN) { return NULL; } else { return &value->value; } } -void ChannelValueDataInit(ChannelValueData * data, ChannelType type) { - switch (type) { +void ChannelValueDataInit(ChannelValueData * data, ChannelType * type) { + switch (type->con) { case CHANNEL_DOUBLE: data->d = 0.0; break; @@ -227,16 +773,82 @@ void ChannelValueDataInit(ChannelValueData * data, ChannelType type) { data->b.len = 0; data->b.data = NULL; break; + case CHANNEL_ARRAY: { + void * tmp = data->a.dims; + data->a.type = ChannelTypeClone(type->ty.a.inner); + data->a.numDims = type->ty.a.numDims; + data->a.dims = (size_t *) mcx_calloc(sizeof(size_t), type->ty.a.numDims); + if (data->a.dims) { + memcpy(data->a.dims, type->ty.a.dims, type->ty.a.numDims * sizeof(size_t)); + } + data->a.data = mcx_calloc(mcx_array_num_elements(&data->a), ChannelValueTypeSize(data->a.type)); + break; + } case CHANNEL_UNKNOWN: default: break; } } -void ChannelValueDataSetFromReference(ChannelValueData * data, ChannelType type, const void * reference) { - if (!reference) { return; } +McxStatus ChannelValueDataSetFromReferenceIfElemwisePred(ChannelValueData * data, + ChannelType * type, + const void * reference, + fChannelValueDataSetterPredicate predicate) { + if (!reference) { + return RETURN_OK; + } + + if (ChannelTypeIsArray(type)) { + mcx_array * a = (mcx_array *) reference; + size_t i = 0; + ChannelValueData first = {0}; + ChannelValueData second = {0}; + + if (!mcx_array_dims_match(&data->a, a)) { + mcx_log(LOG_ERROR, "ChannelValueDataSetFromReferenceIfElemwisePred: Mismatching array dimensions"); + return RETURN_ERROR; + } + + if (a->data == NULL || data->a.data == NULL) { + mcx_log(LOG_ERROR, "ChannelValueDataSetFromReferenceIfElemwisePred: Array data not initialized"); + return RETURN_ERROR; + } + + for (i = 0; i < mcx_array_num_elements(&data->a); i++) { + if (RETURN_OK != mcx_array_get_elem(&data->a, i, &first)) { + mcx_log(LOG_ERROR, "ChannelValueDataSetFromReferenceIfElemwisePred: Getting destination element %zu failed", i); + return RETURN_ERROR; + } + + if (RETURN_OK != mcx_array_get_elem(a, i, &second)) { + mcx_log(LOG_ERROR, "ChannelValueDataSetFromReferenceIfElemwisePred: Getting source element %zu failed", i); + return RETURN_ERROR; + } + + if (predicate(&first, &second, a->type)) { + mcx_array_set_elem(&data->a, i, &second); + } + } + + return RETURN_OK; + } else { + + } + switch (type->con) { + default: + if (predicate(data, reference, type)) { + return ChannelValueDataSetFromReference(data, type, reference); + } + break; + } + + return RETURN_OK; +} + +McxStatus ChannelValueDataSetFromReference(ChannelValueData * data, ChannelType * type, const void * reference) { + if (!reference) { return RETURN_OK; } - switch (type) { + switch (type->con) { case CHANNEL_DOUBLE: data->d = * (double *) reference; break; @@ -275,80 +887,137 @@ void ChannelValueDataSetFromReference(ChannelValueData * data, ChannelType type, data->b.data = ((binary_string *) reference)->data; } break; + case CHANNEL_ARRAY: + if (NULL != reference) { + mcx_array * a = (mcx_array *) reference; + + // The first call to SetFromReference fixes the dimensions + if (!data->a.numDims && a->numDims) { + if (RETURN_OK != mcx_array_init(&data->a, a->numDims, a->dims, a->type)) { + return RETURN_ERROR; + } + } + + // Arrays do not support multiplexing (yet) + if (!mcx_array_dims_match(&data->a, a)) { + return RETURN_ERROR; + } + + if (a->data == NULL || data->a.data == NULL) { + return RETURN_ERROR; + } + + memcpy(data->a.data, a->data, ChannelValueTypeSize(data->a.type) * mcx_array_num_elements(&data->a)); + } case CHANNEL_UNKNOWN: default: break; } -} -void ChannelValueSetFromReference(ChannelValue * value, const void * reference) { - if (!reference) { return; } + return RETURN_OK; +} - ChannelValueDataSetFromReference(&value->value, value->type, reference); +McxStatus ChannelValueSetFromReference(ChannelValue * value, const void * reference) { + return ChannelValueDataSetFromReference(&value->value, value->type, reference); } McxStatus ChannelValueSet(ChannelValue * value, const ChannelValue * source) { - if (value->type != source->type) { + if (!ChannelTypeEq(value->type, source->type)) { mcx_log(LOG_ERROR, "Port: Set: Mismatching types. Source type: %s, target type: %s", ChannelTypeToString(source->type), ChannelTypeToString(value->type)); return RETURN_ERROR; } - ChannelValueSetFromReference(value, ChannelValueReference((ChannelValue *) source)); + if (RETURN_OK != ChannelValueSetFromReference(value, ChannelValueDataPointer((ChannelValue *) source))) { + return RETURN_ERROR; + } return RETURN_OK; } -void ChannelValueSetToReference(ChannelValue * value, void * reference) { - switch (value->type) { - case CHANNEL_DOUBLE: - * (double *) reference = value->value.d; - break; - case CHANNEL_INTEGER: - * (int *) reference = value->value.i; - break; - case CHANNEL_BOOL: - * (int *) reference = value->value.i; - break; - case CHANNEL_STRING: - if (* (char **) reference) { - mcx_free(* (char **) reference); - * (char **) reference = NULL; - } - if (value->value.s) { - * (char **) reference = (char *) mcx_calloc(strlen(value->value.s) + 1, sizeof(char)); - if (* (char **) reference) { - strncpy(* (char **) reference, value->value.s, strlen(value->value.s) + 1); +McxStatus ChannelValueDataSetToReference(ChannelValueData * value, ChannelType * type, void * reference) { + switch (type->con) { + case CHANNEL_DOUBLE: + *(double *) reference = value->d; + break; + case CHANNEL_INTEGER: + *(int *) reference = value->i; + break; + case CHANNEL_BOOL: + *(int *) reference = value->i; + break; + case CHANNEL_STRING: + if (*(char **) reference) { + mcx_free(*(char **) reference); + *(char **) reference = NULL; } - } - break; - case CHANNEL_BINARY: - if (NULL != reference && NULL != ((binary_string *) reference)->data) { - mcx_free(((binary_string *) reference)->data); - ((binary_string *) reference)->data = NULL; - } - if (value->value.b.data) { - ((binary_string *) reference)->len = value->value.b.len; - ((binary_string *) reference)->data = (char *) mcx_calloc(value->value.b.len, 1); - if (((binary_string *) reference)->data) { - memcpy(((binary_string *) reference)->data, value->value.b.data, value->value.b.len); + if (value->s) { + *(char **) reference = (char *) mcx_calloc(strlen(value->s) + 1, sizeof(char)); + if (*(char **) reference) { + strncpy(*(char **) reference, value->s, strlen(value->s) + 1); + } else { + return RETURN_ERROR; + } } - } - break; - case CHANNEL_BINARY_REFERENCE: - if (NULL != reference) { - ((binary_string *) reference)->len = value->value.b.len; - ((binary_string *) reference)->data = value->value.b.data; - } - break; - case CHANNEL_UNKNOWN: - default: - break; + break; + case CHANNEL_BINARY: + if (NULL != reference && NULL != ((binary_string *) reference)->data) { + mcx_free(((binary_string *) reference)->data); + ((binary_string *) reference)->data = NULL; + } + if (value->b.data) { + ((binary_string *) reference)->len = value->b.len; + ((binary_string *) reference)->data = (char *) mcx_calloc(value->b.len, 1); + if (((binary_string *) reference)->data) { + memcpy(((binary_string *) reference)->data, value->b.data, value->b.len); + } + } + break; + case CHANNEL_BINARY_REFERENCE: + if (NULL != reference) { + ((binary_string *) reference)->len = value->b.len; + ((binary_string *) reference)->data = value->b.data; + } + break; + case CHANNEL_ARRAY: + if (NULL != reference) { + mcx_array * a = (mcx_array *) reference; + + // First Set fixes the dimensions + if (value->a.numDims && !a->numDims) { + if (RETURN_OK != mcx_array_init(a, value->a.numDims, value->a.dims, value->a.type)) { + return RETURN_ERROR; + } + } + + // Arrays do not support multiplexing (yet) + if (!mcx_array_dims_match(a, &value->a)) { + return RETURN_ERROR; + } + + if (value->a.data == NULL || a->data == NULL) { + return RETURN_ERROR; + } + + memcpy(a->data, value->a.data, ChannelValueTypeSize(a->type) * mcx_array_num_elements(a)); + } + break; + case CHANNEL_UNKNOWN: + default: + break; } + + return RETURN_OK; + } + +McxStatus ChannelValueSetToReference(ChannelValue * value, void * reference) { + return ChannelValueDataSetToReference(&value->value, value->type, reference); +} + #ifdef __cplusplus -size_t ChannelValueTypeSize(ChannelType type) { - switch (type) { +size_t ChannelValueTypeSize(ChannelType * type) { + switch (type->con) { case CHANNEL_DOUBLE: return sizeof(ChannelValueData::d); case CHANNEL_INTEGER: @@ -357,13 +1026,18 @@ size_t ChannelValueTypeSize(ChannelType type) { return sizeof(ChannelValueData::i); case CHANNEL_STRING: return sizeof(ChannelValueData::s); + case CHANNEL_BINARY: + case CHANNEL_BINARY_REFERENCE: + return sizeof(ChannelValueData::b); + case CHANNEL_ARRAY: + return sizeof(ChannelValueData::a); } return 0; } #else //__cplusplus -size_t ChannelValueTypeSize(ChannelType type) { +size_t ChannelValueTypeSize(ChannelType * type) { ChannelValueData value; - switch (type) { + switch (type->con) { case CHANNEL_DOUBLE: return sizeof(value.d); case CHANNEL_INTEGER: @@ -372,13 +1046,22 @@ size_t ChannelValueTypeSize(ChannelType type) { return sizeof(value.i); case CHANNEL_STRING: return sizeof(value.s); + case CHANNEL_BINARY: + case CHANNEL_BINARY_REFERENCE: + return sizeof(value.b); + case CHANNEL_ARRAY: + return sizeof(value.a); } return 0; } #endif //__cplusplus -const char * ChannelTypeToString(ChannelType type) { - switch (type) { +int ChannelTypeMatch(ChannelType * a, ChannelType * b) { + return ChannelTypeEq(a, b); +} + +const char * ChannelTypeToString(ChannelType * type) { + switch (type->con) { case CHANNEL_UNKNOWN: return "Unknown"; case CHANNEL_DOUBLE: @@ -390,19 +1073,26 @@ const char * ChannelTypeToString(ChannelType type) { case CHANNEL_STRING: return "String"; case CHANNEL_BINARY: - case CHANNEL_BINARY_REFERENCE: return "Binary"; + case CHANNEL_BINARY_REFERENCE: + return "BinaryReference"; + case CHANNEL_ARRAY: + return "Array"; default: return ""; } } ChannelValue * ChannelValueClone(ChannelValue * value) { + if (!value) { return NULL; } + ChannelValue * clone = (ChannelValue *) mcx_malloc(sizeof(ChannelValue)); if (!clone) { return NULL; } - ChannelValueInit(clone, ChannelValueType(value)); + // ChannelTypeClone might fail, then clone will be + // ChannelTypeUnknown and ChannelValueSet below returns an error + ChannelValueInit(clone, ChannelTypeClone(ChannelValueType(value))); if (ChannelValueSet(clone, value) != RETURN_OK) { mcx_free(clone); @@ -414,26 +1104,28 @@ ChannelValue * ChannelValueClone(ChannelValue * value) { int ChannelValueLeq(ChannelValue * val1, ChannelValue * val2) { - if (ChannelValueType(val1) != ChannelValueType(val2)) { + if (!ChannelTypeEq(val1->type, val2->type)) { return 0; } - switch (ChannelValueType(val1)) { + switch (ChannelValueType(val1)->con) { case CHANNEL_DOUBLE: return val1->value.d <= val2->value.d; case CHANNEL_INTEGER: return val1->value.i <= val2->value.i; + case CHANNEL_ARRAY: + return mcx_array_leq(&val1->value.a, &val2->value.a); default: return 0; } } int ChannelValueGeq(ChannelValue * val1, ChannelValue * val2) { - if (ChannelValueType(val1) != ChannelValueType(val2)) { + if (!ChannelTypeEq(val1->type, val2->type)) { return 0; } - switch (ChannelValueType(val1)) { + switch (ChannelValueType(val1)->con) { case CHANNEL_DOUBLE: return val1->value.d >= val2->value.d; case CHANNEL_INTEGER: @@ -444,11 +1136,11 @@ int ChannelValueGeq(ChannelValue * val1, ChannelValue * val2) { } int ChannelValueEq(ChannelValue * val1, ChannelValue * val2) { - if (ChannelValueType(val1) != ChannelValueType(val2)) { + if (!ChannelTypeEq(val1->type, val2->type)) { return 0; } - switch (ChannelValueType(val1)) { + switch (ChannelValueType(val1)->con) { case CHANNEL_DOUBLE: return val1->value.d == val2->value.d; case CHANNEL_BOOL: @@ -461,47 +1153,122 @@ int ChannelValueEq(ChannelValue * val1, ChannelValue * val2) { } } +static int ChannelValueArrayElemAddOffset(void * elem, size_t idx, ChannelType * type, void * ctx) { + mcx_array * offset = (mcx_array *) ctx; + + switch (type->con) { + case CHANNEL_DOUBLE: + *(double *) elem = *(double *) elem + ((double *) offset->data)[idx]; + break; + case CHANNEL_INTEGER: + *(int *) elem = *(int *) elem + ((int *) offset->data)[idx]; + break; + default: + mcx_log(LOG_ERROR, "ChannelValueArrayElemAddOffset: Type %s not allowed", ChannelTypeToString(type)); + return 1; + } + + return 0; +} + McxStatus ChannelValueAddOffset(ChannelValue * val, ChannelValue * offset) { - if (ChannelValueType(val) != ChannelValueType(offset)) { + if (!ChannelTypeEq(val->type, offset->type)) { mcx_log(LOG_ERROR, "Port: Add offset: Mismatching types. Value type: %s, offset type: %s", ChannelTypeToString(ChannelValueType(val)), ChannelTypeToString(ChannelValueType(offset))); return RETURN_ERROR; } - switch (ChannelValueType(val)) { + switch (ChannelValueType(val)->con) { case CHANNEL_DOUBLE: val->value.d += offset->value.d; return RETURN_OK; case CHANNEL_INTEGER: val->value.i += offset->value.i; return RETURN_OK; + case CHANNEL_ARRAY: + return mcx_array_map(&val->value.a, ChannelValueArrayElemAddOffset, &offset->value.a); default: mcx_log(LOG_ERROR, "Port: Add offset: Type %s not allowed", ChannelTypeToString(ChannelValueType(val))); return RETURN_ERROR; } } +static int ChannelValueArrayElemScale(void * elem, size_t idx, ChannelType * type, void * ctx) { + mcx_array * factor = (mcx_array *) ctx; + + switch (type->con) { + case CHANNEL_DOUBLE: + *(double *) elem = *(double *) elem * ((double *) factor->data)[idx]; + break; + case CHANNEL_INTEGER: + *(int *) elem = *(int *) elem * ((int *) factor->data)[idx]; + break; + default: + mcx_log(LOG_ERROR, "ChannelValueArrayElemScale: Type %s not allowed", ChannelTypeToString(type)); + return 1; + } + + return 0; +} + McxStatus ChannelValueScale(ChannelValue * val, ChannelValue * factor) { - if (ChannelValueType(val) != ChannelValueType(factor)) { + if (!ChannelTypeEq(val->type, factor->type)) { mcx_log(LOG_ERROR, "Port: Scale: Mismatching types. Value type: %s, factor type: %s", ChannelTypeToString(ChannelValueType(val)), ChannelTypeToString(ChannelValueType(factor))); return RETURN_ERROR; } - switch (ChannelValueType(val)) { + switch (ChannelValueType(val)->con) { case CHANNEL_DOUBLE: val->value.d *= factor->value.d; return RETURN_OK; case CHANNEL_INTEGER: val->value.i *= factor->value.i; return RETURN_OK; + case CHANNEL_ARRAY: + return mcx_array_map(&val->value.a, ChannelValueArrayElemScale, &factor->value.a); default: mcx_log(LOG_ERROR, "Port: Scale: Type %s not allowed", ChannelTypeToString(ChannelValueType(val))); return RETURN_ERROR; } } -ChannelValue ** ArrayToChannelValueArray(void * values, size_t num, ChannelType type) { +// Does not take ownership of dims or data +ChannelValue * ChannelValueNewScalar(ChannelType * type, void * data) { + ChannelValue * value = mcx_malloc(sizeof(ChannelValue)); + if (!value) { + return NULL; + } + + ChannelValueInit(value, ChannelTypeClone(type)); + ChannelValueSetFromReference(value, data); + + return value; +} + +// Does not take ownership of dims or data +ChannelValue * ChannelValueNewArray(size_t numDims, size_t dims[], ChannelType * type, void * data) { + ChannelValue * value = mcx_malloc(sizeof(ChannelValue)); + if (!value) { + return NULL; + } + + ChannelValueInit(value, ChannelTypeArray(type, numDims, dims)); + + if (value->value.a.data && data) { + memcpy(value->value.a.data, data, ChannelValueTypeSize(type) * mcx_array_num_elements(&value->value.a)); + } + + return value; +} + +void ChannelValueDestroy(ChannelValue ** value) { + ChannelValueDestructor(*value); + mcx_free(*value); + *value = NULL; +} + +ChannelValue ** ArrayToChannelValueArray(void * values, size_t num, ChannelType * type) { ChannelValue ** array = NULL; size_t size = ChannelValueTypeSize(type); @@ -526,8 +1293,10 @@ ChannelValue ** ArrayToChannelValueArray(void * values, size_t num, ChannelType return NULL; } - ChannelValueInit(array[i], type); - ChannelValueSetFromReference(array[i], (char *) values + i*size); + ChannelValueInit(array[i], ChannelTypeClone(type)); + if (RETURN_OK != ChannelValueSetFromReference(array[i], (char *) values + i*size)) { + return RETURN_ERROR; + } } return array; diff --git a/src/core/channels/ChannelValue.h b/src/core/channels/ChannelValue.h index 7cbdf2a..a2936bd 100644 --- a/src/core/channels/ChannelValue.h +++ b/src/core/channels/ChannelValue.h @@ -12,18 +12,88 @@ #define MCX_CORE_CHANNELS_CHANNEL_VALUE_H #include "CentralParts.h" +#include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ + +typedef struct ChannelDimension ChannelDimension; + +char * CreateIndexedName(const char * name, unsigned i); + +// possible types of values that can be put on channels +typedef enum ChannelTypeConstructor { + CHANNEL_UNKNOWN = 0, + CHANNEL_DOUBLE = 1, + CHANNEL_INTEGER = 2, + CHANNEL_BOOL = 3, + CHANNEL_STRING = 4, + CHANNEL_BINARY = 5, + CHANNEL_BINARY_REFERENCE = 6, + CHANNEL_ARRAY = 7, +} ChannelTypeConstructor; + +typedef struct ChannelTypeArrayType { + struct ChannelType * inner; + size_t numDims; + size_t * dims; +} ChannelTypeArrayType; + +typedef struct ChannelType { + ChannelTypeConstructor con; + union { + ChannelTypeArrayType a; + } ty; +} ChannelType; + +// pre-defined types +extern ChannelType ChannelTypeUnknown; +extern ChannelType ChannelTypeInteger; +extern ChannelType ChannelTypeDouble; +extern ChannelType ChannelTypeBool; +extern ChannelType ChannelTypeString; +extern ChannelType ChannelTypeBinary; +extern ChannelType ChannelTypeBinaryReference; +ChannelType * ChannelTypeArray(ChannelType * inner, size_t numDims, size_t * dims); +ChannelType * ChannelTypeArrayLongDims(ChannelType * inner, size_t numDims, unsigned long * dims); + +ChannelType * ChannelTypeClone(ChannelType * type); +void ChannelTypeDestructor(ChannelType * type); + +ChannelType * ChannelTypeArrayInner(ChannelType * array); + +int ChannelTypeIsValid(const ChannelType * a); +int ChannelTypeIsScalar(const ChannelType * a); +int ChannelTypeIsArray(const ChannelType * a); +int ChannelTypeIsBinary(const ChannelType * a); + +size_t ChannelTypeNumElements(const ChannelType * type); + +ChannelType * ChannelTypeFromDimension(ChannelType * base_type, ChannelDimension * dimension); + +ChannelType * ChannelTypeBaseType(const ChannelType * a); + +int ChannelTypeEq(const ChannelType * a, const ChannelType * b); +int ChannelTypeConformable(ChannelType * a, ChannelDimension * sliceA, ChannelType * b, ChannelDimension * sliceB); + +typedef struct MapStringChannelType { + const char * key; + ChannelType * value; +} MapStringChannelType; + + typedef struct { double startTime; double endTime; } TimeInterval; +typedef union ChannelValueData ChannelValueData; +typedef struct ChannelValue ChannelValue; + typedef struct { - double (* fn)(TimeInterval * arg, void * env); + int (* fn)(TimeInterval * arg, void * env, ChannelValue * res); void * env; } proc; @@ -32,57 +102,83 @@ typedef struct { char * data; } binary_string; -// possible types of values that can be put on channels -typedef enum ChannelType { - CHANNEL_UNKNOWN = 0, - CHANNEL_DOUBLE = 1, - CHANNEL_INTEGER = 2, - CHANNEL_BOOL = 3, - CHANNEL_STRING = 4, - CHANNEL_BINARY = 5, - CHANNEL_BINARY_REFERENCE = 6, -} ChannelType; +typedef struct { + size_t numDims; + size_t * dims; + ChannelType * type; + void * data; +} mcx_array; + +McxStatus mcx_array_init(mcx_array * a, size_t numDims, size_t * dims, ChannelType * type); +void mcx_array_destroy(mcx_array * a); +int mcx_array_dims_match(mcx_array * a, mcx_array * b); +size_t mcx_array_num_elements(const mcx_array * a); -typedef union ChannelValueData { +typedef int (*mcx_array_map_f_ptr)(void * element, size_t idx, ChannelType * type, void * ctx); + +McxStatus mcx_array_map(mcx_array * a, mcx_array_map_f_ptr fn, void * ctx); +McxStatus mcx_array_get_elem(const mcx_array * a, size_t idx, ChannelValueData * element); +McxStatus mcx_array_set_elem(mcx_array * a, size_t idx, ChannelValueData * element); + +void * mcx_array_get_elem_reference(const mcx_array * a, size_t idx); + +typedef int (*mcx_array_predicate_f_ptr)(void * element, ChannelType * type); +int mcx_array_all(mcx_array * a, mcx_array_predicate_f_ptr predicate); +int mcx_array_leq(const mcx_array * left, const mcx_array * right); + +union ChannelValueData { /* the order is significant. double needs to be the first entry for union initialization to work */ double d; int i; char * s; binary_string b; -} ChannelValueData; + mcx_array a; +}; -typedef struct ChannelValue { - ChannelType type; +struct ChannelValue { + ChannelType * type; ChannelValueData value; -} ChannelValue; +}; -void ChannelValueInit(ChannelValue * value, ChannelType type); +// Takes ownership of type +void ChannelValueInit(ChannelValue * value, ChannelType * type); void ChannelValueDestructor(ChannelValue * value); char * ChannelValueToString(ChannelValue * value); -McxStatus ChannelValueDataToStringBuffer(const ChannelValueData * value, ChannelType type, char * buffer, size_t len); +McxStatus ChannelValueDataToStringBuffer(const ChannelValueData * value, ChannelType * type, char * buffer, size_t len); McxStatus ChannelValueToStringBuffer(const ChannelValue * value, char * buffer, size_t len); -ChannelType ChannelValueType(ChannelValue * value); -void * ChannelValueReference(ChannelValue * value); +ChannelType * ChannelValueType(ChannelValue * value); +void * ChannelValueDataPointer(ChannelValue * value); -void ChannelValueDataDestructor(ChannelValueData * data, ChannelType type); -void ChannelValueDataInit(ChannelValueData * data, ChannelType type); -void ChannelValueDataSetFromReference(ChannelValueData * data, ChannelType type, const void * reference); +void ChannelValueDataDestructor(ChannelValueData * data, ChannelType * type); +void ChannelValueDataInit(ChannelValueData * data, ChannelType * type); +McxStatus ChannelValueDataSetFromReference(ChannelValueData * data, ChannelType * type, const void * reference); +typedef int (*fChannelValueDataSetterPredicate)(void * first, void * second, ChannelType * type); +McxStatus ChannelValueDataSetFromReferenceIfElemwisePred(ChannelValueData * data, + ChannelType * type, + const void * reference, + fChannelValueDataSetterPredicate predicate); +McxStatus ChannelValueDataSetToReference(ChannelValueData * value, ChannelType * type, void * reference); -void ChannelValueSetFromReference(ChannelValue * value, const void * reference); -void ChannelValueSetToReference(ChannelValue * value, void * reference); +McxStatus ChannelValueSetFromReference(ChannelValue * value, const void * reference); +McxStatus ChannelValueSetToReference(ChannelValue * value, void * reference); McxStatus ChannelValueSet(ChannelValue * value, const ChannelValue * source); -size_t ChannelValueTypeSize(ChannelType type); +size_t ChannelValueTypeSize(ChannelType * type); +int ChannelTypeMatch(ChannelType * a, ChannelType * b); + +ChannelValue * ChannelValueNewScalar(ChannelType * type, void * data); +ChannelValue * ChannelValueNewArray(size_t numDims, size_t dims[], ChannelType * type, void * data); +void ChannelValueDestroy(ChannelValue ** value); -ChannelValue ** ArrayToChannelValueArray(void * values, size_t num, ChannelType type); +ChannelValue ** ArrayToChannelValueArray(void * values, size_t num, ChannelType * type); /* * Returns a string representation of ChannelType for use in log * messages. */ -const char * ChannelTypeToString(ChannelType type); +const char * ChannelTypeToString(ChannelType * type); /* * Creates a copy of value. Allocates memory if needed, e.g. when diff --git a/src/core/channels/ChannelValueReference.c b/src/core/channels/ChannelValueReference.c new file mode 100644 index 0000000..152e363 --- /dev/null +++ b/src/core/channels/ChannelValueReference.c @@ -0,0 +1,230 @@ +/******************************************************************************** + * Copyright (c) 2021 AVL List GmbH and others + * + * This program and the accompanying materials are made available under the + * terms of the Apache Software License 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "core/channels/ChannelValueReference.h" +#include "common/logging.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +void DestroyChannelValueReference(ChannelValueReference * ref) { + if (ref->type == CHANNEL_VALUE_REF_SLICE) { + if (ref->ref.slice.dimension) { + DestroyChannelDimension(ref->ref.slice.dimension); + } + } + + mcx_free(ref); +} + + +ChannelValueReference * MakeChannelValueReference(ChannelValue * value, ChannelDimension * slice) { + ChannelValueReference * ref = (ChannelValueReference *) mcx_calloc(1, sizeof(ChannelValueReference)); + + if (!ref) { + return NULL; + } + + if (slice) { + ref->type = CHANNEL_VALUE_REF_SLICE; + ref->ref.slice.dimension = slice; + ref->ref.slice.ref = value; + } else { + ref->type = CHANNEL_VALUE_REF_VALUE; + ref->ref.value = value; + } + + return ref; +} + + +McxStatus ChannelValueReferenceSetFromPointer(ChannelValueReference * ref, const void * ptr, ChannelDimension * srcDimension, TypeConversion * conv) { + if (conv) { + return conv->Convert(conv, ref, ptr); + } + + if (ref->type == CHANNEL_VALUE_REF_VALUE) { + if (ChannelTypeIsArray(ref->ref.value->type)) { + mcx_array * destArray = &ref->ref.value->value.a; + mcx_array * srcArray = (mcx_array *) ptr; + + if (srcArray->data == NULL || destArray->data == NULL) { + mcx_log(LOG_ERROR, "ChannelValueReferenceSetFromPointer: Empty array data given"); + return RETURN_ERROR; + } + + if (srcArray->numDims == 1) { + // if there is only one dimension, values are sequentially stored in memory so we can use memcpy + // we could also check if for numDims > 1, values are sequentially stored or not, but there is no + // need for that now + size_t numBytes = ChannelValueTypeSize(destArray->type) * mcx_array_num_elements(destArray); + size_t srcOffset = srcDimension != 0 ? srcDimension->startIdxs[0] * ChannelValueTypeSize(srcArray->type) : 0; + void * sourceData = (char *) srcArray->data + srcOffset; + + memcpy(destArray->data, sourceData, numBytes); + } else { + // copy element by element + size_t i = 0; + size_t numElems = srcDimension ? ChannelDimensionNumElements(srcDimension) : mcx_array_num_elements(destArray); + + for (i = 0; i < numElems; i++) { + size_t srcIdx = srcDimension ? ChannelDimensionGetIndex(srcDimension, i, srcArray->dims) : i; + void * srcElem = mcx_array_get_elem_reference(srcArray, srcIdx); + void * destElem = mcx_array_get_elem_reference(destArray, i); + memcpy(destElem, srcElem, ChannelValueTypeSize(destArray->type)); + } + } + + return RETURN_OK; + } else { + if (srcDimension) { + mcx_array * src = (mcx_array *) (ptr); + + if (ChannelDimensionNumElements(srcDimension) != 1) { + mcx_log(LOG_ERROR, "ChannelValueReferenceSetFromPointer: Setting scalar value from an array"); + return RETURN_ERROR; + } + + return ChannelValueDataSetFromReference( + &ref->ref.value->value, + ref->ref.value->type, + mcx_array_get_elem_reference(src, ChannelDimensionGetIndex(srcDimension, 0, src->dims))); + } else { + return ChannelValueDataSetFromReference(&ref->ref.value->value, ref->ref.value->type, ptr); + } + } + } else { + if (ChannelTypeIsArray(ref->ref.slice.ref->type)) { + mcx_array * destArray = &ref->ref.slice.ref->value.a; + mcx_array * srcArray = (mcx_array *) ptr; + + ChannelDimension * destDimension = ref->ref.slice.dimension; + + if (srcArray->data == NULL || destArray->data == NULL) { + mcx_log(LOG_ERROR, "ChannelValueReferenceSetFromPointer: Empty array data given"); + return RETURN_ERROR; + } + + if (srcArray->numDims == 1) { + // if there is only one dimension, values are sequentially stored in memory so we can use memcpy + // we could also check if for numDims > 1, values are sequentially stored or not, but there is no + // need for that now + size_t numBytes = ChannelValueTypeSize(destArray->type) * (destDimension->endIdxs[0] - destDimension->startIdxs[0] + 1); + size_t destOffset = destDimension->startIdxs[0] * ChannelValueTypeSize(destArray->type); + void * destData = (char *) destArray->data + destOffset; + size_t srcOffset = srcDimension != 0 ? srcDimension->startIdxs[0] * ChannelValueTypeSize(srcArray->type) : 0; + void * sourceData = (char *) srcArray->data + srcOffset; + + memcpy(destData, sourceData, numBytes); + } else { + // copy element by element + size_t i = 0; + size_t numElems = srcDimension ? ChannelDimensionNumElements(srcDimension) : mcx_array_num_elements(destArray); + + for (i = 0; i < numElems; i++) { + size_t srcIdx = srcDimension ? ChannelDimensionGetIndex(srcDimension, i, srcArray->dims) : i; + size_t destIdx = ChannelDimensionGetIndex(destDimension, i, destArray->dims); + + void * srcElem = mcx_array_get_elem_reference(srcArray, srcIdx); + void * destElem = mcx_array_get_elem_reference(destArray, destIdx); + memcpy(destElem, srcElem, ChannelValueTypeSize(destArray->type)); + } + } + + return RETURN_OK; + } else { + if (srcDimension) { + mcx_array * src = (mcx_array *) (ptr); + + if (ChannelDimensionNumElements(srcDimension) != 1) { + mcx_log(LOG_ERROR, "ChannelValueReferenceSetFromPointer: Setting scalar value from an array"); + return RETURN_ERROR; + } + + return ChannelValueDataSetFromReference( + &ref->ref.slice.ref->value, + ref->ref.slice.ref->type, + mcx_array_get_elem_reference(src, ChannelDimensionGetIndex(srcDimension, 0, src->dims))); + } else { + return ChannelValueDataSetFromReference(&ref->ref.slice.ref->value, ref->ref.slice.ref->type, ptr); + } + } + } + + return RETURN_OK; +} + +McxStatus ChannelValueReferenceElemMap(ChannelValueReference * ref, fChannelValueReferenceElemMapFunc fn, void * ctx) { + switch (ref->type) { + case CHANNEL_VALUE_REF_VALUE: + if (ChannelTypeIsArray(ref->ref.value->type)) { + size_t i = 0; + + for (i = 0; i < mcx_array_num_elements(&ref->ref.value->value.a); i++) { + void * elem = mcx_array_get_elem_reference(&ref->ref.value->value.a, i); + if (!elem) { + return RETURN_ERROR; + } + + if (RETURN_ERROR == fn(elem, i, ChannelValueType(ref->ref.value), ctx)) { + return RETURN_ERROR; + } + } + + return RETURN_OK; + } else { + return fn(ChannelValueDataPointer(ref->ref.value), 0, ChannelValueType(ref->ref.value), ctx); + } + case CHANNEL_VALUE_REF_SLICE: + if (ChannelTypeIsArray(ref->ref.slice.ref->type)) { + size_t i = 0; + + for (i = 0; i < ChannelDimensionNumElements(ref->ref.slice.dimension); i++) { + size_t idx = ChannelDimensionGetIndex(ref->ref.slice.dimension, i, ref->ref.slice.ref->value.a.dims); + + void * elem = mcx_array_get_elem_reference(&ref->ref.slice.ref->value.a, idx); + if (!elem) { + return RETURN_ERROR; + } + + if (RETURN_ERROR == fn(elem, idx, ChannelValueType(ref->ref.slice.ref), ctx)) { + return RETURN_ERROR; + } + } + + return RETURN_OK; + } else { + return fn(ChannelValueDataPointer(ref->ref.slice.ref), 0, ChannelValueType(ref->ref.slice.ref), ctx); + } + default: + mcx_log(LOG_ERROR, "ChannelValueReferenceElemMap: Invalid internal channel value reference type (%d)", ref->type); + return RETURN_ERROR; + } +} + +ChannelType * ChannelValueReferenceGetType(ChannelValueReference * ref) { + switch (ref->type) { + case CHANNEL_VALUE_REF_VALUE: + return ChannelValueType(ref->ref.value); + case CHANNEL_VALUE_REF_SLICE: + return ChannelValueType(ref->ref.slice.ref); + default: + mcx_log(LOG_ERROR, "Invalid internal channel value reference type (%d)", ref->type); + return NULL; + } +} + + + +#ifdef __cplusplus +} /* closing brace for extern "C" */ +#endif /* __cplusplus */ \ No newline at end of file diff --git a/src/core/channels/ChannelValueReference.h b/src/core/channels/ChannelValueReference.h new file mode 100644 index 0000000..10ea9bd --- /dev/null +++ b/src/core/channels/ChannelValueReference.h @@ -0,0 +1,61 @@ +/******************************************************************************** + * Copyright (c) 2021 AVL List GmbH and others + * + * This program and the accompanying materials are made available under the + * terms of the Apache Software License 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#ifndef MCX_CORE_CHANNELS_CHANNEL_VALUE_REFERENCE_H +#define MCX_CORE_CHANNELS_CHANNEL_VALUE_REFERENCE_H + +#include "CentralParts.h" +#include "core/channels/ChannelDimension.h" + +#include "core/Conversion.h" + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +typedef struct ArraySlice { + ChannelDimension * dimension; + ChannelValue * ref; +} ArraySlice; + +typedef enum ChannelValueRefType { + CHANNEL_VALUE_REF_VALUE, + CHANNEL_VALUE_REF_SLICE +} ChannelValueRefType; + + +typedef struct ChannelValueReference { + ChannelValueRefType type; + union { + ChannelValue * value; + ArraySlice slice; + } ref; +} ChannelValueReference; + + +ChannelValueReference * MakeChannelValueReference(ChannelValue * value, ChannelDimension * slice); +void DestroyChannelValueReference(ChannelValueReference * ref); + + +McxStatus +ChannelValueReferenceSetFromPointer(ChannelValueReference * ref, const void * ptr, ChannelDimension * srcDimension, TypeConversion * typeConv); +ChannelType * ChannelValueReferenceGetType(ChannelValueReference * ref); + +typedef McxStatus (*fChannelValueReferenceElemMapFunc)(void * element, size_t idx, ChannelType * type, void * ctx); +McxStatus ChannelValueReferenceElemMap(ChannelValueReference * ref, fChannelValueReferenceElemMapFunc fn, void * ctx); + + +#ifdef __cplusplus +} /* closing brace for extern "C" */ +#endif /* __cplusplus */ + +#endif // MCX_CORE_CHANNELS_CHANNEL_VALUE_REFERENCE_H \ No newline at end of file diff --git a/src/core/channels/Channel_impl.h b/src/core/channels/Channel_impl.h index af2a837..2b330e6 100644 --- a/src/core/channels/Channel_impl.h +++ b/src/core/channels/Channel_impl.h @@ -28,13 +28,15 @@ extern "C" { typedef struct ChannelInData { Object _; // base class - struct Connection * connection; + ObjectContainer * connections; // connections (non-overlapping) going into the channel + Vector * valueReferences; // references to non-overlapping parts of ChannelData::value, where + // values gotten from connections are going to be stored // ---------------------------------------------------------------------- // Conversions - struct TypeConversion * typeConversion; - struct UnitConversion * unitConversion; + ObjectContainer * typeConversions; // conversion objects (or NULL) for each connection in `connections` + ObjectContainer * unitConversions; // conversion objects (or NULL) for each connection in `connections` struct LinearConversion * linearConversion; struct RangeConversion * rangeConversion; @@ -44,6 +46,7 @@ typedef struct ChannelInData { int isDiscrete; void * reference; + ChannelType * type; } ChannelInData; // ---------------------------------------------------------------------- @@ -56,6 +59,9 @@ typedef struct ChannelOutData { // Function pointer that provides the value of the channel when called const proc * valueFunction; + // Used to store results of channel-internal valueFunction calls + ChannelValue valueFunctionRes; + // ---------------------------------------------------------------------- // Conversion diff --git a/src/core/connections/Connection.c b/src/core/connections/Connection.c index 67622d2..01bc80a 100644 --- a/src/core/connections/Connection.c +++ b/src/core/connections/Connection.c @@ -12,6 +12,7 @@ #include "core/connections/Connection.h" #include "core/channels/Channel.h" #include "core/channels/ChannelInfo.h" +#include "core/channels/ChannelValueReference.h" #include "core/connections/ConnectionInfo.h" #include "core/Conversion.h" @@ -127,9 +128,7 @@ McxStatus MakeOneConnection(ConnectionInfo * info, InterExtrapolatingType isInte return RETURN_OK; } -static void LogStepRatios(double sourceStep, double targetStep, double synchStep, ConnectionInfo * info) { - char * connString = ConnectionInfoConnectionString(info); - +static void LogStepRatios(double sourceStep, double targetStep, double synchStep, const char * connString) { if (sourceStep <= synchStep && targetStep <= synchStep) { MCX_DEBUG_LOG("CONN %s: source <= synch && target <= synch", connString); } else if (sourceStep <= synchStep && targetStep > synchStep) { @@ -139,18 +138,13 @@ static void LogStepRatios(double sourceStep, double targetStep, double synchStep } else { MCX_DEBUG_LOG("CONN %s: source > synch && target > synch", connString); } - - mcx_free(connString); } static int ComponentMightNotRespectStepSize(Component * comp) { return FALSE; } -static size_t DetermineFilterBufferSize(ConnectionInfo * info) { - Component * source = info->sourceComponent; - Component * target = info->targetComponent; - +static size_t DetermineFilterBufferSize(Component * source, Component * target, const char * connString) { Model * model = source->GetModel(source); Task * task = model->GetTask(model); @@ -173,10 +167,8 @@ static size_t DetermineFilterBufferSize(ConnectionInfo * info) { buffSize += model->config->interpolationBuffSizeSafetyExt; if (buffSize > model->config->interpolationBuffSizeLimit) { - char * connString = ConnectionInfoConnectionString(info); mcx_log(LOG_WARNING, "%s: buffer limit exceeded (%zu > &zu). Limit can be changed via MC_INTERPOLATION_BUFFER_SIZE_LIMIT.", connString, buffSize, model->config->interpolationBuffSizeLimit); - mcx_free(connString); buffSize = model->config->interpolationBuffSizeLimit; } @@ -184,12 +176,9 @@ static size_t DetermineFilterBufferSize(ConnectionInfo * info) { return buffSize; } -static size_t MemoryFilterHistorySize(ConnectionInfo * info, int extDegree) { +static size_t MemoryFilterHistorySize(Component * sourceComp, Component * targetComp, int extDegree, const char * connString) { size_t size = 0; - Component * sourceComp = info->sourceComponent; - Component * targetComp = info->targetComponent; - Model * model = sourceComp->GetModel(sourceComp); Task * task = model->GetTask(model); @@ -929,11 +918,9 @@ static size_t MemoryFilterHistorySize(ConnectionInfo * info, int extDegree) { } if (size + model->config->memFilterHistoryExtra > limit) { - char * connString = ConnectionInfoConnectionString(info); mcx_log(LOG_WARNING, "%s: history size limit exceeded (%zu > &zu). Limit can be changed via MC_MEM_FILTER_HISTORY_LIMIT. " "Disabling memory filter", connString, size + model->config->memFilterHistoryExtra, limit); - mcx_free(connString); return 0; } @@ -941,7 +928,7 @@ static size_t MemoryFilterHistorySize(ConnectionInfo * info, int extDegree) { return size + model->config->memFilterHistoryExtra; } -static MemoryFilter * SetMemoryFilter(int reverseSearch, ChannelType sourceType, size_t historySize) { +static MemoryFilter * SetMemoryFilter(int reverseSearch, ChannelType * sourceType, size_t historySize) { McxStatus retVal = RETURN_OK; MemoryFilter * filter = (MemoryFilter *)object_create(MemoryFilter); @@ -963,46 +950,51 @@ static MemoryFilter * SetMemoryFilter(int reverseSearch, ChannelType sourceType, return filter; } -ChannelFilter * FilterFactory(Connection * connection) { +ChannelFilter * FilterFactory(ConnectionState * state, + InterExtrapolationType extrapolation_type, + InterExtrapolationParams * extrapolation_params, + ChannelType * channel_type, + InterExtrapolatingType inter_extrapolating_type, + int is_decoupled, + Component * sourceComp, + Component * targetComp, + const char * connString) { ChannelFilter * filter = NULL; McxStatus retVal; - ConnectionInfo * info = connection->GetInfo(connection); - InterExtrapolationType extrapolType = info->interExtrapolationType; - InterExtrapolationParams * params = &info->interExtrapolationParams; - - Component * sourceComp = info->sourceComponent; Model * model = sourceComp->GetModel(sourceComp); Task * task = model->GetTask(model); int useInputsAtEndTime = task->useInputsAtEndTime; - if (ConnectionInfoGetType(info) == CHANNEL_DOUBLE) { - if (!(INTERVAL_COUPLING == params->interpolationInterval && INTERVAL_SYNCHRONIZATION == params->extrapolationInterval)) { + if (ChannelTypeEq(channel_type, &ChannelTypeDouble)) { + if (!(INTERVAL_COUPLING == extrapolation_params->interpolationInterval && + INTERVAL_SYNCHRONIZATION == extrapolation_params->extrapolationInterval)) + { mcx_log(LOG_WARNING, "The use of inter/extrapolation interval settings for double is not supported"); } - if (extrapolType == INTEREXTRAPOLATION_POLYNOMIAL) { + if (extrapolation_type == INTEREXTRAPOLATION_POLYNOMIAL) { - InterExtrapolatingType isInterExtrapol = info->isInterExtrapolating; - if (INTERPOLATING == isInterExtrapol && ConnectionInfoIsDecoupled(info)) { - isInterExtrapol = INTEREXTRAPOLATING; + if (INTERPOLATING == inter_extrapolating_type && is_decoupled) { + inter_extrapolating_type = INTEREXTRAPOLATING; } - int degree = (INTERPOLATING == isInterExtrapol) ? params->interpolationOrder : params->extrapolationOrder; + int degree = (INTERPOLATING == inter_extrapolating_type) ? extrapolation_params->interpolationOrder : + extrapolation_params->extrapolationOrder; - if (EXTRAPOLATING == isInterExtrapol || INTEREXTRAPOLATING == isInterExtrapol) { - size_t memFilterHist = MemoryFilterHistorySize(info, params->extrapolationOrder); + if (EXTRAPOLATING == inter_extrapolating_type || INTEREXTRAPOLATING == inter_extrapolating_type) { + size_t memFilterHist = MemoryFilterHistorySize(sourceComp, targetComp, extrapolation_params->extrapolationOrder, connString); if (0 != memFilterHist) { - filter = (ChannelFilter *) SetMemoryFilter(useInputsAtEndTime, ConnectionInfoGetType(info), memFilterHist); + filter = (ChannelFilter *) SetMemoryFilter(useInputsAtEndTime, channel_type, memFilterHist); if (!filter) { return NULL; } - } else if (INTEREXTRAPOLATING == isInterExtrapol) { + } else if (INTEREXTRAPOLATING == inter_extrapolating_type) { IntExtFilter * intExtFilter = (IntExtFilter *)object_create(IntExtFilter); filter = (ChannelFilter *)intExtFilter; mcx_log(LOG_DEBUG, " Setting up dynamic filter. (%p)", filter); - mcx_log(LOG_DEBUG, " Interpolation order: %d, extrapolation order: %d", params->interpolationOrder, params->extrapolationOrder); - size_t buffSize = DetermineFilterBufferSize(info); - retVal = intExtFilter->Setup(intExtFilter, params->extrapolationOrder, params->interpolationOrder, buffSize); + mcx_log(LOG_DEBUG, " Interpolation order: %d, extrapolation order: %d", extrapolation_params->interpolationOrder, extrapolation_params->extrapolationOrder); + size_t buffSize = DetermineFilterBufferSize(sourceComp, targetComp, connString); + retVal = intExtFilter->Setup(intExtFilter, extrapolation_params->extrapolationOrder, extrapolation_params->interpolationOrder, buffSize); if (RETURN_OK != retVal) { return NULL; } @@ -1017,9 +1009,9 @@ ChannelFilter * FilterFactory(Connection * connection) { } } } else { - size_t memFilterHist = MemoryFilterHistorySize(info, degree); + size_t memFilterHist = MemoryFilterHistorySize(sourceComp, targetComp, degree, connString); if (0 != memFilterHist) { - filter = (ChannelFilter *) SetMemoryFilter(useInputsAtEndTime, ConnectionInfoGetType(info), memFilterHist); + filter = (ChannelFilter *) SetMemoryFilter(useInputsAtEndTime, channel_type, memFilterHist); if (!filter) { return NULL; } @@ -1028,7 +1020,7 @@ ChannelFilter * FilterFactory(Connection * connection) { filter = (ChannelFilter*)intFilter; mcx_log(LOG_DEBUG, " Setting up coupling step interpolation filter. (%p)", filter); mcx_log(LOG_DEBUG, " Interpolation order: %d", degree); - size_t buffSize = DetermineFilterBufferSize(info); + size_t buffSize = DetermineFilterBufferSize(sourceComp, targetComp, connString); retVal = intFilter->Setup(intFilter, degree, buffSize); if (RETURN_OK != retVal) { mcx_log(LOG_ERROR, "Connection: Filter: Could not setup"); @@ -1045,28 +1037,28 @@ ChannelFilter * FilterFactory(Connection * connection) { } else { DiscreteFilter * discreteFilter = NULL; - if (!(0 == params->extrapolationOrder && - 0 == params->interpolationOrder && - INTERVAL_COUPLING == params->interpolationInterval && - INTERVAL_SYNCHRONIZATION == params->extrapolationInterval + if (!(0 == extrapolation_params->extrapolationOrder && + 0 == extrapolation_params->interpolationOrder && + INTERVAL_COUPLING == extrapolation_params->interpolationInterval && + INTERVAL_SYNCHRONIZATION == extrapolation_params->extrapolationInterval )) { mcx_log(LOG_WARNING, "Invalid inter/extrapolation settings for non-double connection detected"); } mcx_log(LOG_DEBUG, "Using constant synchronization step extrapolation for non-double connection"); discreteFilter = (DiscreteFilter *) object_create(DiscreteFilter); - discreteFilter->Setup(discreteFilter, ConnectionInfoGetType(info)); + discreteFilter->Setup(discreteFilter, channel_type); filter = (ChannelFilter *) discreteFilter; } - if (NULL == filter && ConnectionInfoGetType(info) == CHANNEL_DOUBLE) { + if (NULL == filter && ChannelTypeEq(channel_type, &ChannelTypeDouble)) { // TODO: add a check to avoid filters for non-multirate cases - size_t memFilterHist = MemoryFilterHistorySize(info, 0); + size_t memFilterHist = MemoryFilterHistorySize(sourceComp, targetComp, 0, connString); if (0 != memFilterHist) { - filter = (ChannelFilter *) SetMemoryFilter(useInputsAtEndTime, ConnectionInfoGetType(info), memFilterHist); + filter = (ChannelFilter *) SetMemoryFilter(useInputsAtEndTime, channel_type, memFilterHist); if (!filter) { return NULL; } @@ -1077,7 +1069,7 @@ ChannelFilter * FilterFactory(Connection * connection) { } } - filter->AssignState(filter, &connection->state_); + filter->AssignState(filter, state); return filter; } @@ -1090,8 +1082,22 @@ static void ConnectionSetValueReference(Connection * connection, void * referenc connection->value_ = reference; } +static ChannelDimension * ConnectionGetValueDimension(Connection * connection) { + return NULL; +} + +static ChannelType * ConnectionGetValueType(Connection * connection) { + ChannelOut * out = connection->out_; + Channel * channel = (Channel *) out; + ChannelInfo * channelInfo = &channel->info; + + return channelInfo->type; +} + + static void ConnectionDestructor(Connection * connection) { ChannelValueDestructor(&connection->store_); + DestroyConnectionInfo(&connection->info); } static ChannelOut * ConnectionGetSource(Connection * connection) { @@ -1141,6 +1147,10 @@ static McxStatus ConnectionUpdateInitialValue(Connection * connection) { ChannelInfo * inInfo = &in->info; ChannelInfo * outInfo = &out->info; + ChannelValueReference * storeRef = NULL; + + McxStatus retVal = RETURN_OK; + if (connection->state_ != InInitializationMode) { char * buffer = ConnectionInfoConnectionString(info); mcx_log(LOG_ERROR, "Connection %s: Update initial value: Cannot update initial value outside of initialization mode", buffer); @@ -1155,52 +1165,74 @@ static McxStatus ConnectionUpdateInitialValue(Connection * connection) { return RETURN_ERROR; } + storeRef = MakeChannelValueReference(&connection->store_, NULL); + if (!storeRef) { + mcx_log(LOG_ERROR, "Could not create store reference for initial connection"); + return RETURN_ERROR; + } + if (inInfo->initialValue) { - McxStatus retVal = RETURN_OK; - ChannelValue * store = &connection->store_; + TypeConversion * typeConv = NULL; ChannelValue * inChannelValue = inInfo->initialValue; - ChannelValue * inValue = ChannelValueClone(inChannelValue); + ChannelDimension * srcDim = NULL; - if (NULL == inValue) { - mcx_log(LOG_ERROR, "Could not clone initial value for initial connection"); - return RETURN_ERROR; + srcDim = CloneChannelDimension(info->targetDimension); + if (info->targetDimension && !srcDim) { + mcx_log(LOG_ERROR, "Could not clone source dimension"); + retVal = RETURN_ERROR; + goto cleanup_1; + } + retVal = ChannelDimensionAlignIndicesWithZero(srcDim, inInfo->dimension); + if (retVal == RETURN_ERROR) { + mcx_log(LOG_ERROR, "Dimension normalization failed"); + goto cleanup_1; } // The type of the stored value of a connection is the type of the out channel. // If the value is taken from the in channel, the value must be converted. // TODO: It might be a better idea to use the type of the in channel as type of the connection. // Such a change might be more complex to implement. - if (inValue->type != store->type) { - TypeConversion * typeConv = (TypeConversion *) object_create(TypeConversion); - Conversion * conv = (Conversion *) typeConv; - retVal = typeConv->Setup(typeConv, inValue->type, store->type); + if (!ChannelTypeConformable(storeRef->ref.value->type, NULL, inChannelValue->type, srcDim)) { + typeConv = (TypeConversion *) object_create(TypeConversion); + retVal = typeConv->Setup(typeConv, inChannelValue->type, info->targetDimension, storeRef->ref.value->type, NULL); if (RETURN_ERROR == retVal) { mcx_log(LOG_ERROR, "Could not set up initial type conversion"); - object_destroy(typeConv); - mcx_free(inValue); - return RETURN_ERROR; - } - retVal = conv->convert(conv, inValue); - object_destroy(typeConv); - - if (RETURN_ERROR == retVal) { - mcx_log(LOG_ERROR, "Could not convert type of initial value"); - mcx_free(inValue); - return RETURN_ERROR; + retVal = RETURN_ERROR; + goto cleanup_1; } } - retVal = ChannelValueSet(store, inValue); + retVal = ChannelValueReferenceSetFromPointer(storeRef, ChannelValueDataPointer(inChannelValue), srcDim, typeConv); if (RETURN_ERROR == retVal) { mcx_log(LOG_ERROR, "Could not set up initial value in connection"); - mcx_free(inValue); - return RETURN_ERROR; + goto cleanup_1; } - mcx_free(inValue); connection->useInitialValue_ = TRUE; + +cleanup_1: + object_destroy(typeConv); + object_destroy(srcDim); + + if (retVal == RETURN_ERROR) { + goto cleanup; + } } else if (outInfo->initialValue) { - ChannelValueSet(&connection->store_, outInfo->initialValue); + ChannelDimension * targetDim = CloneChannelDimension(info->sourceDimension); + if (info->sourceDimension && !targetDim) { + mcx_log(LOG_ERROR, "Could not clone target dimension"); + retVal = RETURN_ERROR; + goto cleanup; + } + + retVal = ChannelDimensionAlignIndicesWithZero(targetDim, outInfo->dimension); + if (retVal == RETURN_ERROR) { + mcx_log(LOG_ERROR, "Dimension normalization failed"); + object_destroy(targetDim); + goto cleanup; + } + + ChannelValueReferenceSetFromPointer(storeRef, ChannelValueDataPointer(outInfo->initialValue), targetDim, NULL); connection->useInitialValue_ = TRUE; } else { { @@ -1208,10 +1240,21 @@ static McxStatus ConnectionUpdateInitialValue(Connection * connection) { mcx_log(LOG_WARNING, "Connection %s: No initial values are specified for the ports of the connection", buffer); mcx_free(buffer); } - ChannelValueInit(&connection->store_, ConnectionInfoGetType(info)); + + ChannelType * storeType = ChannelTypeFromDimension(ConnectionInfoGetType(info), info->sourceDimension); + if (!storeType) { + mcx_log(LOG_ERROR, "Creating type from the dimension failed"); + retVal = RETURN_ERROR; + goto cleanup; + } + + ChannelValueInit(&connection->store_, storeType); } - return RETURN_OK; +cleanup: + DestroyChannelValueReference(storeRef); + + return retVal; } static void ConnectionInitUpdateFrom(Connection * connection, TimeInterval * time) { @@ -1225,8 +1268,34 @@ static void ConnectionInitUpdateFrom(Connection * connection, TimeInterval * tim // Do nothing } -static void ConnectionInitUpdateTo(Connection * connection, TimeInterval * time) { +static McxStatus ConnectionInitSetToStore(Connection * connection) { + Channel* channel = (Channel*)connection->out_; + ChannelInfo* info = &channel->info; + ChannelValueReference * storeRef = MakeChannelValueReference(&connection->store_, NULL); + McxStatus retVal = RETURN_OK; + + if (!storeRef) { + mcx_log(LOG_ERROR, "Could not create store reference for initial connection"); + return RETURN_ERROR; + } + + ConnectionInfo * connInfo = connection->GetInfo(connection); + ChannelDimension * clone = CloneChannelDimension(connInfo->sourceDimension); + ChannelDimensionAlignIndicesWithZero(clone, info->dimension); + retVal = ChannelValueReferenceSetFromPointer(storeRef, channel->GetValueReference(channel), clone, NULL); + if (RETURN_ERROR == retVal) { + goto cleanup; + } + +cleanup: + DestroyChannelValueReference(storeRef); + + return retVal; +} + +static McxStatus ConnectionInitUpdateTo(Connection * connection, TimeInterval * time) { Channel * channel = (Channel *) connection->out_; + ChannelInfo * info = &channel->info; #ifdef MCX_DEBUG if (time->startTime < MCX_DEBUG_LOG_TIME) { @@ -1236,13 +1305,17 @@ static void ConnectionInitUpdateTo(Connection * connection, TimeInterval * time) #endif if (!connection->useInitialValue_) { - ChannelValueSetFromReference(&connection->store_, channel->GetValueReference(channel)); + if (RETURN_OK != connection->InitSetToStore(connection)) { + return RETURN_ERROR; + } if (channel->IsDefinedDuringInit(channel)) { connection->SetDefinedDuringInit(connection); } } else { connection->SetDefinedDuringInit(connection); } + + return RETURN_OK; } static McxStatus ConnectionEnterInitializationMode(Connection * connection) { @@ -1267,7 +1340,7 @@ static McxStatus ConnectionEnterInitializationMode(Connection * connection) { // set functions for initialization mode connection->UpdateFromInput = ConnectionInitUpdateFrom; connection->UpdateToOutput = ConnectionInitUpdateTo; - connection->value_ = ChannelValueReference(&connection->store_); + connection->value_ = ChannelValueDataPointer(&connection->store_); connection->IsDefinedDuringInit = ConnectionIsDefinedDuringInit; connection->SetDefinedDuringInit = ConnectionSetDefinedDuringInit; @@ -1337,9 +1410,18 @@ McxStatus ConnectionSetup(Connection * connection, ChannelOut * out, ChannelIn * info->hasDiscreteTarget = TRUE; } - connection->info = *info; + retVal = ConnectionInfoSetFrom(&connection->info, info); + if (RETURN_ERROR == retVal) { + return RETURN_ERROR; + } - ChannelValueInit(&connection->store_, outInfo->type); + retVal = connection->SetupStore(connection, out, in, info); + if (RETURN_ERROR == retVal) { + char * buffer = ConnectionInfoConnectionString(info); + mcx_log(LOG_ERROR, "Connection %s: Store setup failed", buffer); + mcx_free(buffer); + return RETURN_ERROR; + } // Add connection to channel out retVal = out->RegisterConnection(out, connection); @@ -1350,7 +1432,7 @@ McxStatus ConnectionSetup(Connection * connection, ChannelOut * out, ChannelIn * return RETURN_ERROR; } - retVal = in->SetConnection(in, connection, outInfo->unitString, outInfo->type); + retVal = in->RegisterConnection(in, connection, outInfo->unitString, outInfo->type); if (RETURN_OK != retVal) { char * buffer = ConnectionInfoConnectionString(info); mcx_log(LOG_ERROR, "Connection %s: Setup connection: Could not register with inport", buffer); @@ -1361,16 +1443,33 @@ McxStatus ConnectionSetup(Connection * connection, ChannelOut * out, ChannelIn * return RETURN_OK; } +McxStatus ConnectionSetupStore(Connection * connection, ChannelOut * out, ChannelIn * in, ConnectionInfo * info) { + Channel * chOut = (Channel *) out; + ChannelInfo * outInfo = &chOut->info; + ChannelType * storeType = ChannelTypeFromDimension(outInfo->type, info->sourceDimension); + + if (!storeType) { + return RETURN_ERROR; + } + + ChannelValueInit(&connection->store_, storeType); + + return RETURN_OK; +} + static Connection * ConnectionCreate(Connection * connection) { McxStatus retVal = RETURN_OK; connection->Setup = NULL; + connection->SetupStore = ConnectionSetupStore; connection->GetSource = ConnectionGetSource; connection->GetTarget = ConnectionGetTarget; connection->GetValueReference = ConnectionGetValueReference; connection->SetValueReference = ConnectionSetValueReference; + connection->GetValueDimension = ConnectionGetValueDimension; + connection->GetValueType = ConnectionGetValueType; connection->GetInfo = ConnectionGetInfo; @@ -1406,7 +1505,7 @@ static Connection * ConnectionCreate(Connection * connection) { connection->isActiveDependency_ = TRUE; - ChannelValueInit(&connection->store_, CHANNEL_UNKNOWN); + ChannelValueInit(&connection->store_, &ChannelTypeUnknown); connection->state_ = InCommunicationMode; @@ -1414,6 +1513,8 @@ static Connection * ConnectionCreate(Connection * connection) { connection->NormalUpdateTo_ = NULL; connection->normalValue_ = NULL; + connection->InitSetToStore = ConnectionInitSetToStore; + return connection; } diff --git a/src/core/connections/Connection.h b/src/core/connections/Connection.h index f4736b9..1e54b67 100644 --- a/src/core/connections/Connection.h +++ b/src/core/connections/Connection.h @@ -45,12 +45,18 @@ typedef McxStatus (* fConnectionSetup)(Connection * channel, struct ChannelOut * out, struct ChannelIn * in, struct ConnectionInfo * info); +typedef McxStatus (* fConnectionSetupStore)(Connection * conn, + struct ChannelOut * out, + struct ChannelIn * in, + struct ConnectionInfo * info); typedef struct ChannelOut * (* fConnectionGetSource)(Connection * connection); typedef struct ChannelIn * (* fConnectionGetTarget)(Connection * connection); typedef void * (* fConnectionGetValueReference)(Connection * connection); typedef void (* fConnectionSetValueReference)(Connection * connection, void * reference); +typedef ChannelDimension * (*fConnectionGetValueDimension)(Connection * connection); +typedef ChannelType * (*fConnectionGetValueType)(Connection * connection); typedef ConnectionInfo * (* fConnectionGetInfo)(Connection * connection); @@ -62,9 +68,10 @@ typedef void (* fConnectionSetVoid)(Connection * connection); typedef void (* fConnectionUpdateFromInput)(Connection * connection, TimeInterval * time); -typedef void (* fConnectionUpdateToOutput)(Connection * connection, TimeInterval * time); +typedef McxStatus (* fConnectionUpdateToOutput)(Connection * connection, TimeInterval * time); typedef McxStatus (* fConnectionUpdateInitialValue)(Connection * connection); +typedef McxStatus (* fConnectionInitSetToStore)(Connection * connection); typedef McxStatus (* fConnectionEnterInitializationMode)(Connection * connection); typedef McxStatus (* fConnectionExitInitializationMode)(Connection * connection, double time); @@ -116,6 +123,8 @@ struct Connection { */ fConnectionSetup Setup; + fConnectionSetupStore SetupStore; + /** * Returns the source out channel. */ @@ -132,6 +141,12 @@ struct Connection { */ fConnectionGetValueReference GetValueReference; + fConnectionGetValueDimension GetValueDimension; + + fConnectionGetValueType GetValueType; + + fConnectionInitSetToStore InitSetToStore; + /** * Set the reference to the value of the connection. This value will be * updated on each call to UpdateToOutput(). @@ -211,7 +226,16 @@ struct Connection { // Common Functionality for Subclasses McxStatus ConnectionSetup(Connection * connection, struct ChannelOut * out, struct ChannelIn * in, ConnectionInfo * info); -struct ChannelFilter * FilterFactory(Connection * connection); +struct ChannelFilter *FilterFactory(ConnectionState *state, + InterExtrapolationType extrapolation_type, + InterExtrapolationParams *extrapolation_params, + ChannelType *channel_type, + InterExtrapolatingType inter_extrapolating_type, + int is_decoupled, + Component * sourceComp, + Component * targetComp, + const char * connString); + #ifdef __cplusplus } /* closing brace for extern "C" */ diff --git a/src/core/connections/ConnectionInfo.c b/src/core/connections/ConnectionInfo.c index c5e7fba..4a4ae3c 100644 --- a/src/core/connections/ConnectionInfo.c +++ b/src/core/connections/ConnectionInfo.c @@ -28,40 +28,40 @@ void ConnectionInfoSetDecoupled(ConnectionInfo * info) { info->isDecoupled_ = TRUE; } -ChannelType ConnectionInfoGetType(ConnectionInfo * info) { +ChannelType * ConnectionInfoGetType(ConnectionInfo * info) { //Return the data type of the corresponding outport of the source component Component * source = NULL; Databus * db = NULL; ChannelInfo * outInfo = NULL; - if (CHANNEL_UNKNOWN != info->connType_) { + if (ChannelTypeIsValid(info->connType_)) { return info->connType_; } if (NULL == info) { mcx_log(LOG_DEBUG, "ConnectionInfo: GetType: no info available"); - return CHANNEL_UNKNOWN; + return &ChannelTypeUnknown; } source = info->sourceComponent; if (NULL == source) { char * buffer = ConnectionInfoConnectionString(info); mcx_log(LOG_DEBUG, "ConnectionInfo '%s': GetType: no source available", buffer); mcx_free(buffer); - return CHANNEL_UNKNOWN; + return &ChannelTypeUnknown; } db = source->GetDatabus(source); if (NULL == db) { char * buffer = ConnectionInfoConnectionString(info); mcx_log(LOG_DEBUG, "ConnectionInfo '%s': GetType: no databus available", buffer); mcx_free(buffer); - return CHANNEL_UNKNOWN; + return &ChannelTypeUnknown; } outInfo = DatabusInfoGetChannel(DatabusGetOutInfo(db), info->sourceChannel); if (!outInfo) { char * buffer = ConnectionInfoConnectionString(info); mcx_log(LOG_DEBUG, "ConnectionInfo '%s': GetType: no outinfo available", buffer); mcx_free(buffer); - return CHANNEL_UNKNOWN; + return &ChannelTypeUnknown; } info->connType_ = outInfo->type; @@ -137,6 +137,22 @@ char * ConnectionInfoConnectionString(ConnectionInfo * info) { return buffer; } +void DestroyConnectionInfo(ConnectionInfo * info) { + if (info->sourceDimension) { DestroyChannelDimension(info->sourceDimension); } + if (info->targetDimension) { DestroyChannelDimension(info->targetDimension); } +} + +McxStatus ConnectionInfoSetFrom(ConnectionInfo * info, const ConnectionInfo * other) { + McxStatus retVal = RETURN_OK; + + memcpy(info, other, sizeof(ConnectionInfo)); + + info->sourceDimension = CloneChannelDimension(other->sourceDimension); + info->targetDimension = CloneChannelDimension(other->targetDimension); + + return RETURN_OK; +} + McxStatus ConnectionInfoInit(ConnectionInfo * info) { info->sourceComponent = NULL; info->targetComponent = NULL; @@ -158,7 +174,10 @@ McxStatus ConnectionInfoInit(ConnectionInfo * info) { info->hasDiscreteTarget = FALSE; - info->connType_ = CHANNEL_UNKNOWN; + info->connType_ = &ChannelTypeUnknown; + + info->sourceDimension = NULL; + info->targetDimension = NULL; return RETURN_OK; } diff --git a/src/core/connections/ConnectionInfo.h b/src/core/connections/ConnectionInfo.h index ec262c9..7b27627 100644 --- a/src/core/connections/ConnectionInfo.h +++ b/src/core/connections/ConnectionInfo.h @@ -13,6 +13,7 @@ #include "CentralParts.h" #include "core/Component_interface.h" +#include "core/channels/ChannelDimension.h" #define DECOUPLE_DEFAULT DECOUPLE_IFNEEDED @@ -38,7 +39,7 @@ typedef struct ConnectionInfo { int hasDiscreteTarget; - ChannelType connType_; + ChannelType * connType_; InterExtrapolatingType isInterExtrapolating; @@ -48,13 +49,17 @@ typedef struct ConnectionInfo { DecoupleType decoupleType; int decouplePriority; + ChannelDimension * sourceDimension; + ChannelDimension * targetDimension; } ConnectionInfo; McxStatus ConnectionInfoInit(ConnectionInfo * info); +McxStatus ConnectionInfoSetFrom(ConnectionInfo * info, const ConnectionInfo * other); +void DestroyConnectionInfo(ConnectionInfo * info); -ChannelType ConnectionInfoGetType(ConnectionInfo * info); +ChannelType * ConnectionInfoGetType(ConnectionInfo * info); int ConnectionInfoIsDecoupled(ConnectionInfo * info); void ConnectionInfoSetDecoupled(ConnectionInfo * info); diff --git a/src/core/connections/ConnectionInfoFactory.c b/src/core/connections/ConnectionInfoFactory.c index 6de46c3..9f0ef0b 100644 --- a/src/core/connections/ConnectionInfoFactory.c +++ b/src/core/connections/ConnectionInfoFactory.c @@ -10,23 +10,25 @@ #include "core/connections/ConnectionInfoFactory.h" #include "core/connections/ConnectionInfo.h" +#include "core/channels/ChannelDimension.h" #include "core/Databus.h" #include "objects/Vector.h" +#include "util/stdlib.h" #include "util/string.h" +#include "core/channels/ChannelValue.h" + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ -static McxStatus ConnectionInfoFactoryInitConnectionInfo( - ConnectionInfo * info, - ObjectContainer * components, - ConnectionInput * connInput, - Component * sourceCompOverride, - Component * targetCompOverride) -{ +static McxStatus ConnectionInfoFactoryInitConnectionInfo(ConnectionInfo * info, + ObjectContainer * components, + ConnectionInput * connInput, + Component * sourceCompOverride, + Component * targetCompOverride) { McxStatus retVal = RETURN_OK; int id = 0; @@ -102,22 +104,40 @@ static McxStatus ConnectionInfoFactoryInitConnectionInfo( { Databus * databus = info->sourceComponent->GetDatabus(info->sourceComponent); DatabusInfo * databusInfo = DatabusGetOutInfo(databus); + ChannelInfo * sourceInfo = NULL; char * inputFromChannel = connInput->fromType == ENDPOINT_SCALAR ? connInput->from.scalarEndpoint->channel : connInput->from.vectorEndpoint->channel; + strFromChannel = mcx_string_copy(inputFromChannel); if (0 == strlen(strFromChannel)) { retVal = input_element_error((InputElement*)connInput, "Source port name is empty"); goto cleanup; } + // arrays/multiplexing: source slice dimensions if (connInput->fromType == ENDPOINT_VECTOR) { - mcx_free(strFromChannel); - strFromChannel = CreateIndexedName(inputFromChannel, connInput->from.vectorEndpoint->startIndex); - if (!strFromChannel) { + ChannelDimension * sourceDimension = MakeChannelDimension(); + if (!sourceDimension) { + retVal = RETURN_ERROR; + goto cleanup; + } + + if (RETURN_OK != ChannelDimensionSetup(sourceDimension, 1)) { + mcx_log(LOG_ERROR, "Source port %s: Could not set number of dimensions", strFromChannel); retVal = RETURN_ERROR; + DestroyChannelDimension(sourceDimension); goto cleanup; } + + if (RETURN_OK != ChannelDimensionSetDimension(sourceDimension, 0, connInput->from.vectorEndpoint->startIndex, connInput->from.vectorEndpoint->endIndex)) { + mcx_log(LOG_ERROR, "Source port %s: Could not set dimension boundaries", strFromChannel); + retVal = RETURN_ERROR; + DestroyChannelDimension(sourceDimension); + goto cleanup; + } + + info->sourceDimension = sourceDimension; } info->sourceChannel = DatabusInfoGetChannelID(databusInfo, strFromChannel); @@ -136,12 +156,36 @@ static McxStatus ConnectionInfoFactoryInitConnectionInfo( connectionInverted = 1; } } + + // check that the source endpoint "fits" into the channel + sourceInfo = DatabusInfoGetChannel(databusInfo, info->sourceChannel); + if (!ChannelDimensionIncludedIn(info->sourceDimension, sourceInfo->dimension)) { + char* channelDimString = ChannelDimensionString(sourceInfo->dimension); + char* connDimString = ChannelDimensionString(info->sourceDimension); + mcx_log(LOG_ERROR, + "Connection: Dimension index mismatch between source port %s of element %s and the connection endpoint: %s vs %s", + strFromChannel, + info->sourceComponent->GetName(info->sourceComponent), + channelDimString, + connDimString); + retVal = RETURN_ERROR; + + if (channelDimString) { + mcx_free(channelDimString); + } + if (connDimString) { + mcx_free(connDimString); + } + + goto cleanup; + } } // target channel { Databus * databus = info->targetComponent->GetDatabus(info->targetComponent); DatabusInfo * databusInfo = NULL; + ChannelInfo * targetInfo = NULL; char * inputToChannel = connInput->toType == ENDPOINT_SCALAR ? connInput->to.scalarEndpoint->channel : connInput->to.vectorEndpoint->channel; @@ -151,19 +195,39 @@ static McxStatus ConnectionInfoFactoryInitConnectionInfo( goto cleanup; } + if (0 == connectionInverted) { + databusInfo = DatabusGetInInfo(databus); + } else { + databusInfo = DatabusGetOutInfo(databus); + } + + // arrays/multiplexing: target slice dimensions if (connInput->toType == ENDPOINT_VECTOR) { - mcx_free(strToChannel); - strToChannel = CreateIndexedName(inputToChannel, connInput->to.vectorEndpoint->startIndex); - if (!strToChannel) { + ChannelDimension * targetDimension = MakeChannelDimension(); + if (!targetDimension) { retVal = RETURN_ERROR; goto cleanup; } - } - if (0 == connectionInverted) { - databusInfo = DatabusGetInInfo(databus); - } else { - databusInfo = DatabusGetOutInfo(databus); + if (RETURN_OK != ChannelDimensionSetup(targetDimension, 1)) { + mcx_log(LOG_ERROR, "Target port %s: Could not set number of dimensions", strToChannel); + retVal = RETURN_ERROR; + DestroyChannelDimension(targetDimension); + goto cleanup; + } + + if (RETURN_OK != ChannelDimensionSetDimension(targetDimension, + 0, + (size_t) connInput->to.vectorEndpoint->startIndex, + (size_t) connInput->to.vectorEndpoint->endIndex)) + { + mcx_log(LOG_ERROR, "Target port %s: Could not set dimension boundaries", strToChannel); + retVal = RETURN_ERROR; + DestroyChannelDimension(targetDimension); + goto cleanup; + } + + info->targetDimension = targetDimension; } info->targetChannel = DatabusInfoGetChannelID(databusInfo, strToChannel); @@ -178,12 +242,36 @@ static McxStatus ConnectionInfoFactoryInitConnectionInfo( retVal = RETURN_ERROR; goto cleanup; } + + // check that the target endpoint "fits" into the channel + targetInfo = DatabusInfoGetChannel(databusInfo, info->targetChannel); + if (!ChannelDimensionIncludedIn(info->targetDimension, targetInfo->dimension)) { + char * channelDimString = ChannelDimensionString(targetInfo->dimension); + char * connDimString = ChannelDimensionString(info->targetDimension); + mcx_log(LOG_ERROR, + "Connection: Dimension index mismatch between connection endpoint and the target port %s of element %s: %s vs %s", + strToChannel, + info->targetComponent->GetName(info->targetComponent), + connDimString, + channelDimString); + retVal = RETURN_ERROR; + + if (channelDimString) { + mcx_free(channelDimString); + } + if (connDimString) { + mcx_free(connDimString); + } + + goto cleanup; + } } // swap endpoints if connection is inverted if (connectionInverted) { int tmp = info->sourceChannel; Component * tmpCmp = info->sourceComponent; + ChannelDimension * tmpDim = info->sourceDimension; info->sourceChannel = info->targetChannel; info->targetChannel = tmp; @@ -191,10 +279,28 @@ static McxStatus ConnectionInfoFactoryInitConnectionInfo( info->sourceComponent = info->targetComponent; info->targetComponent = tmpCmp; + info->sourceDimension = info->targetDimension; + info->targetDimension = tmpDim; + mcx_log(LOG_DEBUG, "Connection: Inverted connection (%s, %s) -- (%s, %s)", info->targetComponent->GetName(info->targetComponent), strFromChannel, info->sourceComponent->GetName(info->sourceComponent), strToChannel); } + { + // check that connection endpoint dimensions match + ChannelDimension * sourceDim = info->sourceDimension; + ChannelDimension * targetDim = info->targetDimension; + + size_t numSourceElems = sourceDim ? ChannelDimensionNumElements(sourceDim) : 1; + size_t numTargetElems = targetDim ? ChannelDimensionNumElements(targetDim) : 1; + + if (numSourceElems != numTargetElems) { + mcx_log(LOG_ERROR, "Connection: Lengths of vectors do not match"); + retVal = RETURN_ERROR; + goto cleanup; + } + } + // extrapolation if (connInput->interExtrapolationType.defined) { info->interExtrapolationType = connInput->interExtrapolationType.value; @@ -252,43 +358,15 @@ Vector * ConnectionInfoFactoryCreateConnectionInfos( goto cleanup; } - list->Setup(list, sizeof(ConnectionInfo), ConnectionInfoInit, NULL, NULL); + list->Setup(list, sizeof(ConnectionInfo), ConnectionInfoInit, ConnectionInfoSetFrom, DestroyConnectionInfo); retVal = ConnectionInfoFactoryInitConnectionInfo(&info, components, connInput, sourceCompOverride, targetCompOverride); if (RETURN_ERROR == retVal) { goto cleanup; } - if (connInput->fromType == ENDPOINT_VECTOR || connInput->toType == ENDPOINT_VECTOR) { - /* vector of connections */ - int fromStartIndex = connInput->fromType == ENDPOINT_VECTOR ? connInput->from.vectorEndpoint->startIndex : 0; - int fromEndIndex = connInput->fromType == ENDPOINT_VECTOR ? connInput->from.vectorEndpoint->endIndex : 0; - - int toStartIndex = connInput->toType == ENDPOINT_VECTOR ? connInput->to.vectorEndpoint->startIndex : 0; - int toEndIndex = connInput->toType == ENDPOINT_VECTOR ? connInput->to.vectorEndpoint->endIndex : 0; - - int i = 0; - int fromStart = info.sourceChannel; - int toStart = info.targetChannel; - - if (fromEndIndex - fromStartIndex != toEndIndex - toStartIndex) { - /* the lenghts of both sides do not match */ - mcx_log(LOG_ERROR, "Connection: Lengths of vectors do not match"); - goto cleanup; - } - - for (i = 0; fromStartIndex + i <= fromEndIndex; i++) { - ConnectionInfo * copy = NULL; - - list->PushBack(list, &info); - copy = (ConnectionInfo *) list->At(list, list->Size(list) - 1); - copy->sourceChannel = fromStart + i; - copy->targetChannel = toStart + i; - } - } else { - /* info is the only connection: leave as is */ - list->PushBack(list, &info); - } + /* info is the only connection: leave as is */ + list->PushBack(list, &info); return list; diff --git a/src/core/connections/ConnectionInfoFactory.h b/src/core/connections/ConnectionInfoFactory.h index 5881b14..b7e0b63 100644 --- a/src/core/connections/ConnectionInfoFactory.h +++ b/src/core/connections/ConnectionInfoFactory.h @@ -20,13 +20,11 @@ extern "C" { #endif /* __cplusplus */ - Vector * ConnectionInfoFactoryCreateConnectionInfos(ObjectContainer * components, ConnectionInput * connInput, Component * sourceCompOverride, Component * targetCompOverride); - #ifdef __cplusplus } /* closing brace for extern "C" */ #endif /* __cplusplus */ diff --git a/src/core/connections/ConnectionInfo_impl.h b/src/core/connections/ConnectionInfo_impl.h new file mode 100644 index 0000000..455b13a --- /dev/null +++ b/src/core/connections/ConnectionInfo_impl.h @@ -0,0 +1,68 @@ +/******************************************************************************** + * Copyright (c) 2020 AVL List GmbH and others + * + * This program and the accompanying materials are made available under the + * terms of the Apache Software License 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#ifndef MCX_CORE_CONNECTIONS_CONNECTIONINFO_IMPL_H +#define MCX_CORE_CONNECTIONS_CONNECTIONINFO_IMPL_H + +/* for interExtrapolation, DecoupleType */ +#include "CentralParts.h" + +/* for ChannelType */ +#include "core/channels/ChannelValue.h" + +/* for ChannelDimension */ +#include "core/channels/ChannelDimension.h" + +/* for Component */ +#include "core/Component.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +// ---------------------------------------------------------------------- +// ConnectionInfo + +extern const struct ObjectClass _ConnectionInfoData; + +typedef struct ConnectionInfoData { + Object _; + + struct Component * sourceComponent; + struct Component * targetComponent; + + int sourceChannel; + int targetChannel; + + ChannelDimension * sourceDimension; + ChannelDimension * targetDimension; + + // Decouple Info: If this connection is decoupled because of an algebraic loop + // in the model (this means that the value of the source for the target is + // behind one timestep) + int isDecoupled; + + InterExtrapolatingType isInterExtrapolating; + + InterExtrapolationType interExtrapolationType; + InterExtrapolationParams * interExtrapolationParams; + + DecoupleType decoupleType; + int decouplePriority; + + int hasDiscreteTarget; + +} ConnectionInfoData; + +#ifdef __cplusplus +} /* closing brace for extern "C" */ +#endif /* __cplusplus */ + +#endif /* MCX_CORE_CONNECTIONS_CONNECTIONINFO_IMPL_H */ \ No newline at end of file diff --git a/src/core/connections/FilteredConnection.c b/src/core/connections/FilteredConnection.c index 79aa80a..9815911 100644 --- a/src/core/connections/FilteredConnection.c +++ b/src/core/connections/FilteredConnection.c @@ -21,16 +21,26 @@ extern "C" { #endif /* __cplusplus */ static FilteredConnectionData * FilteredConnectionDataCreate(FilteredConnectionData * data) { - data->filter = NULL; + data->filters = NULL; + data->numFilters = 0; - ChannelValueInit(&data->store, CHANNEL_UNKNOWN); + ChannelValueInit(&data->updateBuffer, ChannelTypeClone(&ChannelTypeUnknown)); + ChannelValueInit(&data->store, ChannelTypeClone(&ChannelTypeUnknown)); return data; } static void FilteredConnectionDataDestructor(FilteredConnectionData * data) { ChannelValueDestructor(&data->store); - object_destroy(data->filter); + ChannelValueDestructor(&data->updateBuffer); + + if (data->filters) { + size_t i = 0; + for (i = 0; i < data->numFilters; i++) { + object_destroy(data->filters[i]); + } + mcx_free(data->filters); + } } OBJECT_CLASS(FilteredConnectionData, Object); @@ -43,6 +53,8 @@ static McxStatus FilteredConnectionSetup(Connection * connection, ChannelOut * o ChannelInfo * sourceInfo = &((Channel *)out)->info; ChannelInfo * targetInfo = &((Channel *)in)->info; + ChannelType * storeType = NULL; + McxStatus retVal = RETURN_OK; // Decoupling @@ -51,14 +63,22 @@ static McxStatus FilteredConnectionSetup(Connection * connection, ChannelOut * o } // filter will be added after model is connected - filteredConnection->data->filter = NULL; + filteredConnection->data->filters = NULL; + filteredConnection->data->numFilters = 0; + + storeType = ChannelTypeFromDimension(sourceInfo->type, info->sourceDimension); + if (!storeType) { + return RETURN_ERROR; + } // value store - ChannelValueInit(&filteredConnection->data->store, sourceInfo->type); + ChannelValueInit(&filteredConnection->data->store, storeType); // steals ownership of storeType -> no clone needed // value reference - connection->value_ = ChannelValueReference(&filteredConnection->data->store); + connection->value_ = ChannelValueDataPointer(&filteredConnection->data->store); + // initialize the buffer for channel function calls + ChannelValueInit(&filteredConnection->data->updateBuffer, ChannelTypeClone(storeType)); // Connection::Setup() // this has to be done last as it connects the channels @@ -72,15 +92,18 @@ static McxStatus FilteredConnectionSetup(Connection * connection, ChannelOut * o static McxStatus FilteredConnectionEnterCommunicationMode(Connection * connection, double time) { FilteredConnection * filteredConnection = (FilteredConnection *) connection; - ChannelFilter * filter = filteredConnection->GetWriteFilter(filteredConnection); McxStatus retVal = RETURN_OK; + size_t i = 0; - if (filter) { - if (filter->EnterCommunicationMode) { - retVal = filter->EnterCommunicationMode(filter, time); - if (RETURN_OK != retVal) { - return RETURN_ERROR; + for (i = 0; i < filteredConnection->data->numFilters; i++) { + ChannelFilter * filter = filteredConnection->GetWriteFilter(filteredConnection, i); + if (filter) { + if (filter->EnterCommunicationMode) { + retVal = filter->EnterCommunicationMode(filter, time); + if (RETURN_OK != retVal) { + return RETURN_ERROR; + } } } } @@ -93,15 +116,18 @@ static McxStatus FilteredConnectionEnterCouplingStepMode(Connection * connection , double communicationTimeStepSize, double sourceTimeStepSize, double targetTimeStepSize) { FilteredConnection * filteredConnection = (FilteredConnection *) connection; - ChannelFilter * filter = filteredConnection->GetWriteFilter(filteredConnection); McxStatus retVal = RETURN_OK; + size_t i = 0; - if (filter) { - if (filter->EnterCouplingStepMode) { - retVal = filter->EnterCouplingStepMode(filter, communicationTimeStepSize, sourceTimeStepSize, targetTimeStepSize); - if (RETURN_OK != retVal) { - return RETURN_ERROR; + for (i = 0; i < filteredConnection->data->numFilters; i++) { + ChannelFilter * filter = filteredConnection->GetWriteFilter(filteredConnection, i); + if (filter) { + if (filter->EnterCouplingStepMode) { + retVal = filter->EnterCouplingStepMode(filter, communicationTimeStepSize, sourceTimeStepSize, targetTimeStepSize); + if (RETURN_OK != retVal) { + return RETURN_ERROR; + } } } } @@ -110,64 +136,133 @@ static McxStatus FilteredConnectionEnterCouplingStepMode(Connection * connection return RETURN_OK; } -static ChannelFilter * FilteredConnectionGetFilter(FilteredConnection * connection) { - return connection->data->filter; +static ChannelFilter * FilteredConnectionGetFilter(FilteredConnection * connection, size_t idx) { + if (connection->data->filters && idx < connection->data->numFilters) { + return connection->data->filters[idx]; + } + return NULL; +} + +static size_t FilteredConnectionGetNumFilters(FilteredConnection *connection) { + return connection->data->numFilters; } -static void FilteredConnectionSetResult(FilteredConnection * connection, const void * value) { - ChannelValueSetFromReference(&connection->data->store, value); +static McxStatus FilteredConnectionSetResult(FilteredConnection * connection, const void * value) { + return ChannelValueSetFromReference(&connection->data->store, value); +} + +static size_t GetSliceShift(Connection * connection) { + Channel * channel = (Channel *) connection->GetSource(connection); + ChannelInfo * channelInfo = &channel->info; + ChannelDimension * sourceDimension = channelInfo->dimension; + + ConnectionInfo * connInfo = connection->GetInfo(connection); + ChannelDimension * sliceDimension = connInfo->sourceDimension; + + // only 1D at the moment + return sliceDimension->startIdxs[0] - sourceDimension->startIdxs[0]; } static void FilteredConnectionUpdateFromInput(Connection * connection, TimeInterval * time) { FilteredConnection * filteredConnection = (FilteredConnection *) connection; - ChannelFilter * filter = filteredConnection->GetWriteFilter(filteredConnection); Channel * channel = (Channel *) connection->GetSource(connection); + ChannelInfo * info = &channel->info; #ifdef MCX_DEBUG if (time->startTime < MCX_DEBUG_LOG_TIME) { - ChannelInfo * info = &channel->info; MCX_DEBUG_LOG("[%f] FCONN (%s) UpdateFromInput", time->startTime, ChannelInfoGetName(info)); } #endif - if (filter && time->startTime >= 0) { - ChannelValueData value = * (ChannelValueData *) channel->GetValueReference(channel); - filter->SetValue(filter, time->startTime, value); + if (ChannelTypeIsScalar(info->type)) { + ChannelFilter * filter = filteredConnection->GetWriteFilter(filteredConnection, 0); + if (filter && time->startTime >= 0) { + ChannelValueData value = *(ChannelValueData *) channel->GetValueReference(channel); + filter->SetValue(filter, time->startTime, value); + } + } else { + size_t i = 0; + size_t shift = GetSliceShift(connection); + ChannelValueData element; + ChannelValueDataInit(&element, ChannelTypeBaseType(info->type)); + + for (i = 0; i < FilteredConnectionGetNumFilters(filteredConnection); i++) { + ChannelFilter * filter = filteredConnection->GetWriteFilter(filteredConnection, i); + + if (filter && time->startTime >= 0) { + ChannelValueData value = *(ChannelValueData *) channel->GetValueReference(channel); + mcx_array_get_elem(&value.a, i + shift, &element); + filter->SetValue(filter, time->startTime, element); + } + } } } -static void FilteredConnectionUpdateToOutput(Connection * connection, TimeInterval * time) { +static McxStatus FilteredConnectionUpdateToOutput(Connection * connection, TimeInterval * time) { FilteredConnection * filteredConnection = (FilteredConnection *) connection; ChannelFilter * filter = NULL; Channel * channel = (Channel *) connection->GetSource(connection); + ChannelInfo * info = &channel->info; ChannelOut * out = (ChannelOut *) channel; #ifdef MCX_DEBUG if (time->startTime < MCX_DEBUG_LOG_TIME) { - ChannelInfo * info = &channel->info; MCX_DEBUG_LOG("[%f] FCONN (%s) UpdateToOutput", time->startTime, ChannelInfoGetName(info)); } #endif if (out->GetFunction(out)) { proc * p = (proc *) out->GetFunction(out); - double value = 0.0; - value = p->fn(time, p->env); - filteredConnection->SetResult(filteredConnection, &value); + // TODO: Update functions to only update the slices ? + if (p->fn(time, p->env, &filteredConnection->data->updateBuffer) != 0) { + mcx_log(LOG_ERROR, "FilteredConnection: Function failed"); + return RETURN_ERROR; + } + + if (RETURN_OK != filteredConnection->SetResult(filteredConnection, ChannelValueDataPointer(&filteredConnection->data->updateBuffer))) { + mcx_log(LOG_ERROR, "FilteredConnection: SetResult failed"); + return RETURN_ERROR; + } } else { // only filter if time is not negative (negative time means filter disabled) - if (filteredConnection->GetReadFilter(filteredConnection) && time->startTime >= 0) { - ChannelValueData value; - - filter = filteredConnection->GetReadFilter(filteredConnection); - value = filter->GetValue(filter, time->startTime); - - filteredConnection->SetResult(filteredConnection, &value); + if (filteredConnection->GetReadFilter(filteredConnection, 0) && time->startTime >= 0) { + ChannelValueData value = { 0 }; + + if (ChannelTypeIsScalar(info->type)) { + filter = filteredConnection->GetReadFilter(filteredConnection, 0); + value = filter->GetValue(filter, time->startTime); + + if (RETURN_OK != filteredConnection->SetResult(filteredConnection, &value)) { + mcx_log(LOG_ERROR, "FilteredConnection: SetResult failed"); + return RETURN_ERROR; + } + } else { + size_t i = 0; + size_t numFilters = FilteredConnectionGetNumFilters(filteredConnection); + ChannelType * type = info->type; + + mcx_array * elements = (mcx_array *) ChannelValueDataPointer(&filteredConnection->data->updateBuffer); + char * dest = (char *) elements->data; + for (i = 0; i < numFilters; i++) { + filter = filteredConnection->GetReadFilter(filteredConnection, i); + value = filter->GetValue(filter, time->startTime); + + ChannelValueDataSetToReference(&value, ChannelTypeBaseType(type), (void *) dest); + dest += ChannelValueTypeSize(ChannelTypeBaseType(type)); + } + + if (RETURN_OK != filteredConnection->SetResult(filteredConnection, elements)) { + mcx_log(LOG_ERROR, "FilteredConnection: SetResult failed"); + return RETURN_ERROR; + } + } } } + + return RETURN_OK; } static McxStatus AddFilter(Connection * connection) { @@ -176,17 +271,84 @@ static McxStatus AddFilter(Connection * connection) { McxStatus retVal = RETURN_OK; - if (filteredConnection->data->filter) { + if (filteredConnection->data->filters) { mcx_log(LOG_DEBUG, "Connection: Not inserting filter"); } else { - filteredConnection->data->filter = FilterFactory(connection); - if (NULL == filteredConnection->data->filter) { - mcx_log(LOG_DEBUG, "Connection: No Filter created"); - retVal = RETURN_ERROR; + ConnectionInfo * info = connection->GetInfo(connection); + const char * connString = ConnectionInfoConnectionString(info); + ChannelDimension * dimension = info->sourceDimension; + + if (dimension) { // array + size_t i = 0; + + if (dimension->num > 1) { + mcx_log(LOG_ERROR, "Setting filters for multi-dimensional connections is not supported"); + retVal = RETURN_ERROR; + goto cleanup; + } + + filteredConnection->data->numFilters = dimension->endIdxs[0] - dimension->startIdxs[0] + 1; + filteredConnection->data->filters = (ChannelFilter **) mcx_calloc(filteredConnection->data->numFilters, sizeof(ChannelFilter*)); + if (!filteredConnection->data->filters) { + mcx_log(LOG_ERROR, "Creating array filters failed: no memory"); + retVal = RETURN_ERROR; + goto cleanup; + } + + for (i = 0; i < filteredConnection->data->numFilters; i++) { + filteredConnection->data->filters[i] = FilterFactory(&connection->state_, + info->interExtrapolationType, + &info->interExtrapolationParams, + ChannelTypeBaseType(ConnectionInfoGetType(info)), + info->isInterExtrapolating, + ConnectionInfoIsDecoupled(info), + info->sourceComponent, + info->targetComponent, + connString); + if (NULL == filteredConnection->data->filters[i]) { + mcx_log(LOG_DEBUG, "Connection: Array filter creation failed for index %zu", i); + retVal = RETURN_ERROR; + goto cleanup; + } + } + } else { + filteredConnection->data->filters = (ChannelFilter **) mcx_calloc(1, sizeof(ChannelFilter *)); + if (!filteredConnection->data->filters) { + mcx_log(LOG_ERROR, "Creating filter failed: no memory"); + retVal = RETURN_ERROR; + goto cleanup; + } + + filteredConnection->data->numFilters = 1; + filteredConnection->data->filters[0] = FilterFactory(&connection->state_, + info->interExtrapolationType, + &info->interExtrapolationParams, + ConnectionInfoGetType(info), + info->isInterExtrapolating, + ConnectionInfoIsDecoupled(info), + info->sourceComponent, + info->targetComponent, + connString); + if (NULL == filteredConnection->data->filters[0]) { + mcx_log(LOG_DEBUG, "Connection: No Filter created"); + retVal = RETURN_ERROR; + goto cleanup; + } + } + +cleanup: + mcx_free(connString); + if (retVal != RETURN_OK) { + return retVal; } } - return retVal; + return RETURN_OK; +} + +static ChannelType * FilteredConnectionGetValueType(Connection * connection) { + FilteredConnection * filteredConnection = (FilteredConnection *) connection; + return filteredConnection->data->store.type; } static void FilteredConnectionDestructor(FilteredConnection * filteredConnection) { @@ -204,6 +366,7 @@ static FilteredConnection * FilteredConnectionCreate(FilteredConnection * filter connection->EnterCouplingStepMode = FilteredConnectionEnterCouplingStepMode; connection->AddFilter = AddFilter; + connection->GetValueType = FilteredConnectionGetValueType; filteredConnection->GetReadFilter = FilteredConnectionGetFilter; filteredConnection->GetWriteFilter = FilteredConnectionGetFilter; diff --git a/src/core/connections/FilteredConnection.h b/src/core/connections/FilteredConnection.h index ef2f335..01f0f4e 100644 --- a/src/core/connections/FilteredConnection.h +++ b/src/core/connections/FilteredConnection.h @@ -20,9 +20,9 @@ extern "C" { typedef struct FilteredConnection FilteredConnection; -typedef struct ChannelFilter * (* fConnectionGetFilter)(FilteredConnection * connection); +typedef struct ChannelFilter * (* fConnectionGetFilter)(FilteredConnection * connection, size_t idx); -typedef void (* fFilteredConnectionSetResult)(FilteredConnection * connection, const void * value); +typedef McxStatus (* fFilteredConnectionSetResult)(FilteredConnection * connection, const void * value); extern const struct ObjectClass _FilteredConnection; diff --git a/src/core/connections/FilteredConnection_impl.h b/src/core/connections/FilteredConnection_impl.h index 09ab299..8071a00 100644 --- a/src/core/connections/FilteredConnection_impl.h +++ b/src/core/connections/FilteredConnection_impl.h @@ -28,7 +28,11 @@ typedef struct FilteredConnectionData { // storage of the filtered value provided by the output channel ChannelValue store; - ChannelFilter * filter; + // storage for temporary results during out channel updates + ChannelValue updateBuffer; + + ChannelFilter ** filters; + size_t numFilters; } FilteredConnectionData; diff --git a/src/core/connections/filters/DiscreteFilter.c b/src/core/connections/filters/DiscreteFilter.c index ad80218..2ad8241 100644 --- a/src/core/connections/filters/DiscreteFilter.c +++ b/src/core/connections/filters/DiscreteFilter.c @@ -18,7 +18,9 @@ static McxStatus DiscreteFilterSetValue(ChannelFilter * filter, double time, Cha DiscreteFilter * discreteFilter = (DiscreteFilter *) filter; if (InCommunicationMode != * filter->state) { - ChannelValueSetFromReference(&discreteFilter->lastCouplingStepValue, &value); + if (RETURN_OK != ChannelValueSetFromReference(&discreteFilter->lastCouplingStepValue, &value)) { + return RETURN_ERROR; + } } return RETURN_OK; @@ -27,7 +29,7 @@ static McxStatus DiscreteFilterSetValue(ChannelFilter * filter, double time, Cha static ChannelValueData DiscreteFilterGetValue(ChannelFilter * filter, double time) { DiscreteFilter * discreteFilter = (DiscreteFilter *) filter; - return * (ChannelValueData *) ChannelValueReference(&discreteFilter->lastSynchronizationStepValue); + return * (ChannelValueData *) ChannelValueDataPointer(&discreteFilter->lastSynchronizationStepValue); } static McxStatus DiscreteFilterEnterCouplingStepMode(ChannelFilter * filter @@ -44,9 +46,9 @@ static McxStatus DiscreteFilterEnterCommunicationMode(ChannelFilter * filter, do return RETURN_OK; } -static McxStatus DiscreteFilterSetup(DiscreteFilter * filter, ChannelType type) { - ChannelValueInit(&filter->lastSynchronizationStepValue, type); - ChannelValueInit(&filter->lastCouplingStepValue, type); +static McxStatus DiscreteFilterSetup(DiscreteFilter * filter, ChannelType * type) { + ChannelValueInit(&filter->lastSynchronizationStepValue, ChannelTypeClone(type)); + ChannelValueInit(&filter->lastCouplingStepValue, ChannelTypeClone(type)); return RETURN_OK; } @@ -67,8 +69,8 @@ static DiscreteFilter * DiscreteFilterCreate(DiscreteFilter * discreteFilter) { discreteFilter->Setup = DiscreteFilterSetup; - ChannelValueInit(&discreteFilter->lastSynchronizationStepValue, CHANNEL_UNKNOWN); - ChannelValueInit(&discreteFilter->lastCouplingStepValue, CHANNEL_UNKNOWN); + ChannelValueInit(&discreteFilter->lastSynchronizationStepValue, ChannelTypeClone(&ChannelTypeUnknown)); + ChannelValueInit(&discreteFilter->lastCouplingStepValue, ChannelTypeClone(&ChannelTypeUnknown)); return discreteFilter; } diff --git a/src/core/connections/filters/DiscreteFilter.h b/src/core/connections/filters/DiscreteFilter.h index e98af60..cca3e6e 100644 --- a/src/core/connections/filters/DiscreteFilter.h +++ b/src/core/connections/filters/DiscreteFilter.h @@ -19,7 +19,7 @@ extern "C" { typedef struct DiscreteFilter DiscreteFilter; -typedef McxStatus (* fDiscreteFilterSetup)(DiscreteFilter * filter, ChannelType type); +typedef McxStatus (* fDiscreteFilterSetup)(DiscreteFilter * filter, ChannelType * type); extern const struct ObjectClass _DiscreteFilter; diff --git a/src/core/connections/filters/MemoryFilter.c b/src/core/connections/filters/MemoryFilter.c index b4fddd0..ac14fa0 100644 --- a/src/core/connections/filters/MemoryFilter.c +++ b/src/core/connections/filters/MemoryFilter.c @@ -39,7 +39,7 @@ static McxStatus MemoryFilterSetValue(ChannelFilter * filter, double time, Chann #ifdef MCX_DEBUG if (time < MCX_DEBUG_LOG_TIME) { - if (memoryFilter->valueHistoryWrite[0].type == CHANNEL_DOUBLE) { + if (ChannelTypeEq(memoryFilter->valueHistoryWrite[0].type, &ChannelTypeDouble)) { MCX_DEBUG_LOG("MemoryFilter: F SET (%x) (%f, %f)", filter, time, value.d); } else { MCX_DEBUG_LOG("MemoryFilter: F SET (%x) (%f, -)", filter, time); @@ -62,7 +62,7 @@ static ChannelValueData MemoryFilterGetValueReverse(ChannelFilter * filter, doub if (double_eq(memoryFilter->timeHistoryRead[i], time)) { #ifdef MCX_DEBUG if (time < MCX_DEBUG_LOG_TIME) { - if (memoryFilter->valueHistoryRead[i].type == CHANNEL_DOUBLE) { + if (ChannelTypeEq(memoryFilter->valueHistoryRead[i].type, &ChannelTypeDouble)) { MCX_DEBUG_LOG("MemoryFilter: F GET (%x) (%f, %f)", filter, time, memoryFilter->valueHistoryRead[i].value.d); } else { MCX_DEBUG_LOG("MemoryFilter: F GET (%x) (%f, -)", filter, time); @@ -95,7 +95,7 @@ static ChannelValueData MemoryFilterGetValueReverse(ChannelFilter * filter, doub #ifdef MCX_DEBUG if (time < MCX_DEBUG_LOG_TIME) { - if (memoryFilter->valueHistoryRead[i].type == CHANNEL_DOUBLE) { + if (ChannelTypeEq(memoryFilter->valueHistoryRead[i].type, &ChannelTypeDouble)) { MCX_DEBUG_LOG("MemoryFilter: F GET CLOSEST (%x) (%f, %f)", filter, time, memoryFilter->valueHistoryRead[i].value.d); } else { MCX_DEBUG_LOG("MemoryFilter: F GET CLOSEST (%x) (%f, -)", filter, time); @@ -116,7 +116,7 @@ static ChannelValueData MemoryFilterGetValue(ChannelFilter * filter, double time if (double_eq(memoryFilter->timeHistoryRead[i], time)) { #ifdef MCX_DEBUG if (time < MCX_DEBUG_LOG_TIME) { - if (memoryFilter->valueHistoryRead[i].type == CHANNEL_DOUBLE) { + if (ChannelTypeEq(memoryFilter->valueHistoryRead[i].type, &ChannelTypeDouble)) { MCX_DEBUG_LOG("MemoryFilter: F GET (%x) (%f, %f)", filter, time, memoryFilter->valueHistoryRead[i].value.d); } else { MCX_DEBUG_LOG("MemoryFilter: F GET (%x) (%f, -)", filter, time); @@ -149,7 +149,7 @@ static ChannelValueData MemoryFilterGetValue(ChannelFilter * filter, double time #ifdef MCX_DEBUG if (time < MCX_DEBUG_LOG_TIME) { - if (memoryFilter->valueHistoryRead[i].type == CHANNEL_DOUBLE) { + if (ChannelTypeEq(memoryFilter->valueHistoryRead[i].type, &ChannelTypeDouble)) { MCX_DEBUG_LOG("MemoryFilter: F GET CLOSEST (%x) (%f, %f)", filter, time, memoryFilter->valueHistoryRead[i].value.d); } else { MCX_DEBUG_LOG("MemoryFilter: F GET CLOSEST (%x) (%f, -)", filter, time); @@ -206,7 +206,7 @@ static McxStatus MemoryFilterEnterCommunicationMode(ChannelFilter * filter, doub return RETURN_OK; } -static McxStatus MemoryFilterSetup(MemoryFilter * filter, ChannelType type, size_t historySize, int reverseSearch) { +static McxStatus MemoryFilterSetup(MemoryFilter * filter, ChannelType * type, size_t historySize, int reverseSearch) { ChannelFilter * channelFilter = (ChannelFilter *)filter; size_t i = 0; @@ -241,11 +241,11 @@ static McxStatus MemoryFilterSetup(MemoryFilter * filter, ChannelType type, size } for (i = 0; i < filter->historySize; i++) { - ChannelValueInit(filter->valueHistoryRead + i, type); + ChannelValueInit(filter->valueHistoryRead + i, ChannelTypeClone(type)); } for (i = 0; i < filter->historySize; i++) { - ChannelValueInit(filter->valueHistoryWrite + i, type); + ChannelValueInit(filter->valueHistoryWrite + i, ChannelTypeClone(type)); } return RETURN_OK; diff --git a/src/core/connections/filters/MemoryFilter.h b/src/core/connections/filters/MemoryFilter.h index 7b97d4a..1561fe6 100644 --- a/src/core/connections/filters/MemoryFilter.h +++ b/src/core/connections/filters/MemoryFilter.h @@ -19,7 +19,7 @@ extern "C" { typedef struct MemoryFilter MemoryFilter; -typedef McxStatus (* fMemoryFilterSetup)(MemoryFilter * filter, ChannelType type, size_t historySize, int reverseSearch); +typedef McxStatus (* fMemoryFilterSetup)(MemoryFilter * filter, ChannelType * type, size_t historySize, int reverseSearch); extern const struct ObjectClass _MemoryFilter; diff --git a/src/core/parameters/ParameterProxies.c b/src/core/parameters/ParameterProxies.c new file mode 100644 index 0000000..e48e4ae --- /dev/null +++ b/src/core/parameters/ParameterProxies.c @@ -0,0 +1,217 @@ +/******************************************************************************** + * Copyright (c) 2021 AVL List GmbH and others + * + * This program and the accompanying materials are made available under the + * terms of the Apache Software License 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "core/parameters/ParameterProxies.h" +#include "util/string.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +static void ScalarParameterProxySetValue(ScalarParameterProxy * proxy, Fmu2Value * value) { + proxy->value_ = value; +} + +static ChannelType * ScalarParameterProxyGetType(ScalarParameterProxy * proxy) { + if (proxy->value_) { + return ChannelValueType(&proxy->value_->val); + } + + return &ChannelTypeUnknown; +} + +static Fmu2Value * ScalarParameterProxyGetValue(ScalarParameterProxy * proxy) { + return proxy->value_; +} + +static void ScalarParameterProxyDestructor(ScalarParameterProxy * proxy) { + // don't destroy value as it is just a weak reference +} + +static ScalarParameterProxy * ScalarParameterProxyCreate(ScalarParameterProxy * proxy) { + proxy->SetValue = ScalarParameterProxySetValue; + proxy->GetType = ScalarParameterProxyGetType; + proxy->GetValue = ScalarParameterProxyGetValue; + + proxy->value_ = NULL; + + return proxy; +} + +OBJECT_CLASS(ScalarParameterProxy, Object); + + + +static McxStatus ArrayParameterProxyAddValue(ArrayParameterProxy * proxy, Fmu2Value * value) { + McxStatus retVal = RETURN_OK; + + if (proxy->values_->Size(proxy->values_) > 0) { + Fmu2Value * val = (Fmu2Value *)proxy->values_->At(proxy->values_, 0); + + // check that data types match + if (!ChannelTypeEq(ChannelValueType(&val->val), ChannelValueType(&value->val))) { + mcx_log(LOG_ERROR, "Adding value of '%s' to array proxy '%s' failed: Data type mismatch", value->name, proxy->name_); + return RETURN_ERROR; + } + + // check that units match + if (!val->unit && value->unit || val->unit && !value->unit || val->unit && value->unit && strcmp(val->unit, value->unit) != 0) { + mcx_log(LOG_ERROR, "Adding value of '%s' to array proxy '%s' failed: Unit mismatch", value->name, proxy->name_); + return RETURN_ERROR; + } + } + + // add the value to the internal container + retVal = proxy->values_->PushBackNamed(proxy->values_, (Object *)value, value->name); + if (retVal == RETURN_ERROR) { + mcx_log(LOG_ERROR, "Adding value of '%s' to array proxy '%s' failed", value->name, proxy->name_); + return RETURN_ERROR; + } + + return RETURN_OK; +} + +static McxStatus ArrayParameterProxySetup(ArrayParameterProxy * proxy, const char * name, size_t numDims, size_t * dims) { + proxy->name_ = mcx_string_copy(name); + if (name && !proxy->name_) { + mcx_log(LOG_ERROR, "Array proxy setup failed: Not enough memory"); + return RETURN_ERROR; + } + + proxy->numDims_ = numDims; + proxy->dims_ = (size_t *)mcx_calloc(numDims, sizeof(size_t)); + if (!proxy->dims_) { + mcx_log(LOG_ERROR, "Array proxy setup failed: Not enough memory"); + return RETURN_ERROR; + } + + memcpy((void *)proxy->dims_, (void *)dims, numDims * sizeof(size_t)); + + return RETURN_OK; +} + +static const char * ArrayParameterProxyGetName(ArrayParameterProxy * proxy) { + return proxy->name_; +} + +static const char * ArrayParameterProxyGetDesc(ArrayParameterProxy * proxy) { + if (proxy->values_->Size(proxy->values_) > 0) { + Fmu2Value * value = (Fmu2Value *)proxy->values_->At(proxy->values_, 0); + return value->info->desc; + } + + return NULL; +} + +static const char * ArrayParameterProxyGetUnit(ArrayParameterProxy * proxy) { + if (proxy->values_->Size(proxy->values_) > 0) { + Fmu2Value * value = (Fmu2Value *)proxy->values_->At(proxy->values_, 0); + return value->unit; + } + + return NULL; +} + +static ObjectContainer * ArrayParameterProxyGetValues(ArrayParameterProxy * proxy) { + return proxy->values_; +} + +static size_t ArrayParameterProxyGetNumDims(ArrayParameterProxy * proxy) { + return proxy->numDims_; +} + +static size_t ArrayParameterProxyGetDim(ArrayParameterProxy * proxy, size_t idx) { + if (idx >= proxy->numDims_) { + return (size_t)(-1); + } + + return proxy->dims_[idx]; +} + +static ChannelType * ArrayParameterProxyGetType(ArrayParameterProxy * proxy) { + if (proxy->values_->Size(proxy->values_) > 0) { + Fmu2Value * value = (Fmu2Value *)proxy->values_->At(proxy->values_, 0); + return ChannelValueType(&value->val); + } + + return &ChannelTypeUnknown; +} + +static void ArrayParameterProxyDestructor(ArrayParameterProxy * proxy) { + if (proxy->name_) { mcx_free(proxy->name_); } + if (proxy->dims_) { mcx_free(proxy->dims_); } + + proxy->numDims_ = 0; + + // don't destroy the contained objects as they are just weak references + object_destroy(proxy->values_); +} + +static fmi2_value_reference_t ArrayParameterProxyGetValueReference(ArrayParameterProxy * proxy, size_t idx) { + if (idx < proxy->values_->Size(proxy->values_)) { + Fmu2Value * value = (Fmu2Value *)proxy->values_->At(proxy->values_, idx); + return value->data->vr.scalar; + } + + return (fmi2_value_reference_t)(-1);; +} + +static ChannelValueData * ArrayParameterProxyGetMin(ArrayParameterProxy * proxy) { + if (proxy->values_->Size(proxy->values_) > 0) { + Fmu2Value * value = (Fmu2Value *)proxy->values_->At(proxy->values_, 0); + return value->info->min; + } + + return NULL; +} + +static ChannelValueData * ArrayParameterProxyGetMax(ArrayParameterProxy * proxy) { + if (proxy->values_->Size(proxy->values_) > 0) { + Fmu2Value * value = (Fmu2Value *)proxy->values_->At(proxy->values_, 0); + return value->info->max; + } + + return NULL; +} + +static ArrayParameterProxy * ArrayParameterProxyCreate(ArrayParameterProxy * proxy) { + proxy->Setup = ArrayParameterProxySetup; + proxy->AddValue = ArrayParameterProxyAddValue; + + proxy->GetName = ArrayParameterProxyGetName; + proxy->GetDesc = ArrayParameterProxyGetDesc; + proxy->GetUnit = ArrayParameterProxyGetUnit; + proxy->GetValues = ArrayParameterProxyGetValues; + proxy->GetDim = ArrayParameterProxyGetDim; + proxy->GetNumDims = ArrayParameterProxyGetNumDims; + proxy->GetType = ArrayParameterProxyGetType; + proxy->GetValueReference = ArrayParameterProxyGetValueReference; + proxy->GetMin = ArrayParameterProxyGetMin; + proxy->GetMax = ArrayParameterProxyGetMax; + + proxy->values_ = (ObjectContainer *)object_create(ObjectContainer); + if (!proxy->values_) { + return NULL; + } + + proxy->name_ = NULL; + proxy->dims_ = NULL; + proxy->numDims_ = 0; + + return proxy; +} + +OBJECT_CLASS(ArrayParameterProxy, Object); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ \ No newline at end of file diff --git a/src/core/parameters/ParameterProxies.h b/src/core/parameters/ParameterProxies.h new file mode 100644 index 0000000..f4cf7a8 --- /dev/null +++ b/src/core/parameters/ParameterProxies.h @@ -0,0 +1,92 @@ +/******************************************************************************** + * Copyright (c) 2021 AVL List GmbH and others + * + * This program and the accompanying materials are made available under the + * terms of the Apache Software License 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0. + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#ifndef MCX_CORE_PARAMETERS_PARAMETER_PROXIES_H +#define MCX_CORE_PARAMETERS_PARAMETER_PROXIES_H + +#include "CentralParts.h" + +#include "fmu/Fmu2Value.h" +#include "objects/ObjectContainer.h" + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +struct ScalarParameterProxy; +typedef struct ScalarParameterProxy ScalarParameterProxy; + +typedef void (*fScalarParameterProxySetValue)(ScalarParameterProxy * proxy, Fmu2Value * value); +typedef ChannelType * (*fScalarParameterProxyGetType)(ScalarParameterProxy * proxy); +typedef Fmu2Value * (*fScalarParameterProxyGetValue)(ScalarParameterProxy * proxy); + +extern const struct ObjectClass _ScalarParameterProxy; + +struct ScalarParameterProxy { + Object _; + + fScalarParameterProxySetValue SetValue; + fScalarParameterProxyGetType GetType; + fScalarParameterProxyGetValue GetValue; + + Fmu2Value * value_; +}; + + +struct ArrayParameterProxy; +typedef struct ArrayParameterProxy ArrayParameterProxy; + +typedef McxStatus(*fArrayParameterProxyAddValue)(ArrayParameterProxy * proxy, Fmu2Value * value); +typedef McxStatus(*fArrayParameterProxySetup)(ArrayParameterProxy * proxy, const char * name, size_t numDims, size_t * dims); +typedef const char * (*fArrayParameterProxyGetName)(ArrayParameterProxy * proxy); +typedef const char * (*fArrayParameterProxyGetDesc)(ArrayParameterProxy * proxy); +typedef const char * (*fArrayParameterProxyGetUnit)(ArrayParameterProxy * proxy); +typedef size_t (*fArrayParameterProxyGetNumDims)(ArrayParameterProxy * proxy); +typedef size_t (*fArrayParameterProxyGetDim)(ArrayParameterProxy * proxy, size_t idx); +typedef ObjectContainer * (*fArrayParameterProxyGetValues)(ArrayParameterProxy * proxy); +typedef ChannelType * (*fArrayParameterProxyGetType)(ArrayParameterProxy * proxy); +typedef fmi2_value_reference_t (*fArrayParameterProxyGetValueReference)(ArrayParameterProxy * proxy, size_t idx); +typedef ChannelValueData * (*fArrayParameterProxyGetMin)(ArrayParameterProxy * proxy); +typedef ChannelValueData * (*fArrayParameterProxyGetMax)(ArrayParameterProxy * proxy); + +extern const struct ObjectClass _ArrayParameterProxy; + +struct ArrayParameterProxy { + Object _; + + fArrayParameterProxySetup Setup; + fArrayParameterProxyAddValue AddValue; + fArrayParameterProxyGetValues GetValues; + fArrayParameterProxyGetName GetName; + fArrayParameterProxyGetDesc GetDesc; + fArrayParameterProxyGetUnit GetUnit; + fArrayParameterProxyGetNumDims GetNumDims; + fArrayParameterProxyGetDim GetDim; + fArrayParameterProxyGetType GetType; + fArrayParameterProxyGetValueReference GetValueReference; + fArrayParameterProxyGetMin GetMin; + fArrayParameterProxyGetMax GetMax; + + size_t numDims_; + size_t * dims_; + + char * name_; + + ObjectContainer * values_; // of Fmu2Value +}; + + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* MCX_CORE_PARAMETERS_PARAMETER_PROXIES_H */ \ No newline at end of file diff --git a/src/fmu/Fmu1Value.c b/src/fmu/Fmu1Value.c index f5cc149..7f2818a 100644 --- a/src/fmu/Fmu1Value.c +++ b/src/fmu/Fmu1Value.c @@ -13,69 +13,294 @@ #include "fmu/common_fmu1.h" #include "util/string.h" +#include "util/stdlib.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ + +static void Fmu1ValueDataDestructor(Fmu1ValueData * data) { + if (data->type == FMU1_VALUE_ARRAY) { + if (data->var.array.dims) { + mcx_free(data->var.array.dims); + } + if (data->var.array.values) { + mcx_free(data->var.array.values); + } + data->var.array.numDims = 0; + + if (data->vr.array.values) { + mcx_free(data->vr.array.values); + } + } + + data->type = FMU1_VALUE_INVALID; +} + +static Fmu1ValueData * Fmu1ValueDataCreate(Fmu1ValueData * data) { + memset(data, 0, sizeof(Fmu1ValueData)); + + data->type = FMU1_VALUE_INVALID; + + return data; +} + +OBJECT_CLASS(Fmu1ValueData, Object); + + +size_t Fmu1ValueDataArrayNumElems(const Fmu1ValueData * data) { + size_t i = 0; + size_t num = 1; + + if (data->type != FMU1_VALUE_ARRAY) { + return 0; + } + + for (i = 0; i < data->var.array.numDims; i++) { + num *= data->var.array.dims[i]; + } + + return num; +} + + +static Fmu1ValueData * Fmu1ValueDataScalarMake(fmi1_import_variable_t * scalar) { + Fmu1ValueData * data = NULL; + + if (!scalar) { + return NULL; + } + + data = (Fmu1ValueData *) object_create(Fmu1ValueData); + if (data) { + data->type = FMU1_VALUE_SCALAR; + data->var.scalar = scalar; + data->vr.scalar = fmi1_import_get_variable_vr(scalar); + } + + return data; +} + +static Fmu1ValueData * Fmu1ValueDataArrayMake(size_t numDims, size_t dims[], fmi1_import_variable_t ** values) { + Fmu1ValueData * data = NULL; + + if (numDims == 0) { + return NULL; + } + + data = (Fmu1ValueData *) object_create(Fmu1ValueData); + if (data) { + size_t i = 0; + size_t num = 1; + + for (i = 0; i < numDims; i++) { + num *= dims[i]; + } + + data->type = FMU1_VALUE_ARRAY; + + data->var.array.numDims = numDims; + data->var.array.dims = mcx_copy(dims, sizeof(size_t) * numDims); + if (!data->var.array.dims) { + goto cleanup; + } + + data->var.array.values = mcx_copy(values, sizeof(fmi1_import_variable_t *) * num); + if (!data->var.array.values) { + goto cleanup; + } + + data->vr.array.values = mcx_calloc(num, sizeof(fmi1_value_reference_t)); + if (!data->vr.array.values) { + goto cleanup; + } + + for (i = 0; i < num; i++) { + data->vr.array.values[i] = fmi2_import_get_variable_vr(data->var.array.values[i]); + } + } + return data; + +cleanup: + object_destroy(data); + return NULL; +} + + static McxStatus Fmu1ValueSetFromChannelValue(Fmu1Value * v, ChannelValue * val) { return ChannelValueSet(&v->val, val); } +static McxStatus Fmu1ValueSetup(Fmu1Value * value, const char * name, Fmu1ValueData * data, Channel * channel) { + if (!name || !data) { + mcx_log(LOG_ERROR, "Fmu1Value: Setup failed: Name or data missing"); + return RETURN_ERROR; + } + + value->name = mcx_string_copy(name); + value->data = data; + value->channel = channel; + + if (!value->name) { + mcx_log(LOG_ERROR, "Fmu1Value: Setup failed: Can not copy name"); + return RETURN_ERROR; + } + + switch (data->type) { + case FMU1_VALUE_SCALAR: + { + fmi1_base_type_enu_t t = fmi1_import_get_variable_base_type(data->var.scalar); + ChannelValueInit(&value->val, ChannelTypeClone(Fmi1TypeToChannelType(t))); + break; + } + case FMU1_VALUE_ARRAY: + { + fmi1_base_type_enu_t t = fmi1_import_get_variable_base_type(data->var.array.values[0]); + ChannelValueInit(&value->val, ChannelTypeArray(Fmi1TypeToChannelType(t), data->var.array.numDims, data->var.array.dims)); + break; + } + default: + mcx_log(LOG_ERROR, "Fmu1Value: Setup failed: Invalid data type"); + return RETURN_ERROR; + } + + return RETURN_OK; +} + void Fmu1ValueDestructor(Fmu1Value * v) { if (v->name) { mcx_free(v->name); v->name = NULL; } + object_destroy(v->data); + ChannelValueDestructor(&v->val); } Fmu1Value * Fmu1ValueCreate(Fmu1Value * v) { v->SetFromChannelValue = Fmu1ValueSetFromChannelValue; + v->Setup = Fmu1ValueSetup; v->name = NULL; - v->var = NULL; + v->data = NULL; v->channel = NULL; - ChannelValueInit(&v->val, CHANNEL_UNKNOWN); + ChannelValueInit(&v->val, ChannelTypeClone(&ChannelTypeUnknown)); return v; } OBJECT_CLASS(Fmu1Value, Object); -Fmu1Value * Fmu1ValueMake(const char * name, fmi1_import_variable_t * var, Channel * channel) { + +static Fmu1Value * Fmu1ValueMake(const char * name, Fmu1ValueData * data, Channel * channel) { Fmu1Value * value = (Fmu1Value *)object_create(Fmu1Value); if (value) { - McxStatus retVal = RETURN_OK; - fmi1_base_type_enu_t t; - - if (!name || !var) { - mcx_log(LOG_ERROR, "Fmu1Value: Setup failed: Name or data missing"); - mcx_free(value); + if (RETURN_OK != value->Setup(value, name, data, channel)) { + object_destroy(value); return NULL; } + } - value->channel = channel; + return value; +} - t = fmi1_import_get_variable_base_type(var); +Fmu1Value * Fmu1ValueScalarMake(const char * name, fmi1_import_variable_t * var, Channel * channel) { + Fmu1ValueData * data = Fmu1ValueDataScalarMake(var); + return Fmu1ValueMake(name, data, channel); +} - value->name = mcx_string_copy(name); - value->vr = fmi1_import_get_variable_vr(var); - value->var = var; - ChannelValueInit(&value->val, Fmi1TypeToChannelType(t)); +Fmu1Value * Fmu1ValueArrayMake(const char * name, size_t numDims, size_t * dims, fmi1_import_variable_t ** vars, Channel * channel) { + Fmu1ValueData * data = Fmu1ValueDataArrayMake(numDims, dims, vars); + return Fmu1ValueMake(name, data, channel); +} - if (!value->name) { - mcx_log(LOG_ERROR, "Fmu1Value: Setup failed: Cannot copy name"); - mcx_free(value); - return NULL; +Fmu1Value * Fmu1ValueReadScalar(const char * logPrefix, ChannelType * type, Channel * channel, const char * channelName, fmi1_import_t * fmiImport) { + Fmu1Value * value = NULL; + fmi1_import_variable_t * var = NULL; + + var = fmi1_import_get_variable_by_name(fmiImport, channelName); + if (!var) { + mcx_log(LOG_ERROR, "%s: Could not get variable %s", logPrefix, channelName); + return NULL; + } + + if (!ChannelTypeEq(type, Fmi1TypeToChannelType(fmi1_import_get_variable_base_type(var)))) { + mcx_log(LOG_ERROR, "%s: Variable types of %s do not match", logPrefix, channelName); + mcx_log(LOG_ERROR, + "%s: Expected: %s, Imported from FMU: %s", + logPrefix, + ChannelTypeToString(type), + ChannelTypeToString(Fmi1TypeToChannelType(fmi1_import_get_variable_base_type(var)))); + return NULL; + } + + value = Fmu1ValueScalarMake(channelName, var, channel); + if (!value) { + mcx_log(LOG_ERROR, "%s: Could not set value for channel %s", logPrefix, channelName); + return RETURN_ERROR; + } + + return value; +} + +Fmu1Value * Fmu1ValueReadArray(const char * logPrefix, ChannelType * type, Channel * channel, const char * channelName, ChannelDimension * dimension, fmi1_import_t * fmiImport) { + Fmu1Value * value = NULL; + fmi1_import_variable_t * var = NULL; + + if (dimension->num > 1) { + mcx_log(LOG_ERROR, "%s: Port %s: Invalid dimension", logPrefix, channelName); + goto cleanup; + } + + size_t i = 0; + size_t startIdx = dimension->startIdxs[0]; + size_t endIdx = dimension->endIdxs[0]; + + fmi1_import_variable_t ** vars = mcx_calloc(endIdx - startIdx + 1, sizeof(fmi1_import_variable_t *)); + if (!vars) { + goto cleanup; + } + + for (i = startIdx; i <= endIdx; i++) { + char * indexedChannelName = CreateIndexedName(channelName, i); + fmi1_import_variable_t * var = fmi1_import_get_variable_by_name(fmiImport, indexedChannelName); + if (!var) { + mcx_log(LOG_ERROR, "%s: Could not get variable %s", logPrefix, indexedChannelName); + goto cleanup; + } + if (!ChannelTypeEq(ChannelTypeArrayInner(type), Fmi1TypeToChannelType(fmi1_import_get_variable_base_type(var)))) { + mcx_log(LOG_ERROR, "%s: Variable types of %s do not match", logPrefix, indexedChannelName); + mcx_log(LOG_ERROR, + "%s: Expected: %s, Imported from FMU: %s", + logPrefix, + ChannelTypeToString(ChannelTypeArrayInner(type)), + ChannelTypeToString(Fmi1TypeToChannelType(fmi1_import_get_variable_base_type(var)))); + goto cleanup; } + vars[i - startIdx] = var; + + mcx_free(indexedChannelName); + } + + size_t dims[] = {endIdx - startIdx + 1}; + value = Fmu1ValueArrayMake(channelName, 1 /* numDims */, dims, vars, channel); + if (!value) { + mcx_log(LOG_ERROR, "%s: Could not set value for channel %s", logPrefix, channelName); + goto cleanup; + } + +cleanup: + if (vars) { + mcx_free(vars); } return value; } + #ifdef __cplusplus } /* closing brace for extern "C" */ #endif /* __cplusplus */ \ No newline at end of file diff --git a/src/fmu/Fmu1Value.h b/src/fmu/Fmu1Value.h index 4fe99dd..3844657 100644 --- a/src/fmu/Fmu1Value.h +++ b/src/fmu/Fmu1Value.h @@ -19,11 +19,43 @@ extern "C" { #endif /* __cplusplus */ + +typedef enum Fmu1ValueType { + FMU1_VALUE_SCALAR, + FMU1_VALUE_ARRAY, + FMU1_VALUE_INVALID +} Fmu1ValueType; + +extern const struct ObjectClass _Fmu1ValueData; + +typedef struct Fmu1ValueData { + Object _; + + Fmu1ValueType type; + union { + fmi1_import_variable_t * scalar; + struct { + size_t numDims; + size_t * dims; + fmi1_import_variable_t ** values; + } array; + } var; + union { + fmi1_value_reference_t scalar; + struct { + fmi1_value_reference_t * values; + } array; + } vr; +} Fmu1ValueData; + +size_t Fmu1ValueDataArrayNumElems(const Fmu1ValueData * data); + struct Fmu1Value; typedef struct Fmu1Value Fmu1Value; typedef McxStatus(*fFmu1ValueSetFromChannelValue)(Fmu1Value * v, ChannelValue * val); +typedef McxStatus (*fFmu1ValueSetup)(Fmu1Value * value, const char * name, Fmu1ValueData * data, Channel * channel); extern const struct ObjectClass _Fmu1Value; @@ -31,16 +63,30 @@ struct Fmu1Value { Object _; /* base class */ fFmu1ValueSetFromChannelValue SetFromChannelValue; + fFmu1ValueSetup Setup; char * name; Channel * channel; - fmi1_value_reference_t vr; - fmi1_import_variable_t * var; + Fmu1ValueData * data; ChannelValue val; }; -Fmu1Value * Fmu1ValueMake(const char * name, fmi1_import_variable_t * var, Channel * channel); +Fmu1Value * Fmu1ValueScalarMake(const char * name, fmi1_import_variable_t * var, Channel * channel); +Fmu1Value * Fmu1ValueArrayMake(const char * name, size_t numDims, size_t * dims, fmi1_import_variable_t ** vars, Channel * channel); + +Fmu1Value * Fmu1ValueReadScalar(const char * logPrefix, + ChannelType * type, + Channel * channel, + const char * channelName, + fmi1_import_t * fmiImport); +Fmu1Value * Fmu1ValueReadArray(const char * logPrefix, + ChannelType * type, + Channel * channel, + const char * channelName, + ChannelDimension * dimension, + fmi1_import_t * fmiImport); + #ifdef __cplusplus } /* closing brace for extern "C" */ diff --git a/src/fmu/Fmu2Value.c b/src/fmu/Fmu2Value.c index 68b2779..cf2e0e5 100644 --- a/src/fmu/Fmu2Value.c +++ b/src/fmu/Fmu2Value.c @@ -11,8 +11,10 @@ #include "fmu/Fmu2Value.h" #include "core/channels/ChannelValue.h" +#include "core/connections/ConnectionInfoFactory.h" #include "CentralParts.h" #include "util/string.h" +#include "util/stdlib.h" #include "fmu/common_fmu2.h" #ifdef __cplusplus @@ -20,20 +22,124 @@ extern "C" { #endif /* __cplusplus */ static void Fmu2ValueDataDestructor(Fmu2ValueData * data) { + if (data->type == FMU2_VALUE_ARRAY) { + mcx_free(data->data.array.dims); + mcx_free(data->data.array.values); + + mcx_free(data->vr.array.values); + } } static Fmu2ValueData * Fmu2ValueDataCreate(Fmu2ValueData * data) { - data->type = FMU2_VALUE_INVALID; + memset(data, 0, sizeof(Fmu2ValueData)); - data->data.binary.lo = NULL; - data->data.binary.hi = NULL; - data->data.binary.size = NULL; + data->type = FMU2_VALUE_INVALID; return data; } OBJECT_CLASS(Fmu2ValueData, Object); +Fmu2VariableInfo * Fmu2VariableInfoMake(fmi2_import_variable_t * var) { + Fmu2VariableInfo * info = (Fmu2VariableInfo *)object_create(Fmu2VariableInfo); + + if (info) { + fmi2_base_type_enu_t type = fmi2_import_get_variable_base_type(var); + ChannelValueData min = { 0 }; + int minDefined = FALSE; + ChannelValueData max = { 0 }; + int maxDefined = FALSE; + + char * xmlDesc = fmi2_import_get_variable_description(var); + info->desc = mcx_string_copy(xmlDesc); + if (xmlDesc && !info->desc) { + goto cleanup; + } + + switch (type) { + case fmi2_base_type_real: + info->type = &ChannelTypeDouble; + min.d = fmi2_import_get_real_variable_min(fmi2_import_get_variable_as_real(var)); + minDefined = min.d != -DBL_MAX; + max.d = fmi2_import_get_real_variable_max(fmi2_import_get_variable_as_real(var)); + maxDefined = max.d != DBL_MAX; + break; + case fmi2_base_type_int: + info->type = &ChannelTypeInteger; + min.i = fmi2_import_get_integer_variable_min(fmi2_import_get_variable_as_integer(var)); + minDefined = min.i != -INT_MIN; + max.i = fmi2_import_get_integer_variable_max(fmi2_import_get_variable_as_integer(var)); + maxDefined = max.i != INT_MAX; + break; + case fmi2_base_type_enum: + info->type = &ChannelTypeInteger; + min.i = fmi2_import_get_enum_variable_min(fmi2_import_get_variable_as_enum(var)); + minDefined = min.i != -INT_MIN; + max.i = fmi2_import_get_enum_variable_max(fmi2_import_get_variable_as_enum(var)); + maxDefined = max.i != INT_MAX; + break; + case fmi2_base_type_str: + info->type = &ChannelTypeString; + break; + case fmi2_base_type_bool: + info->type = &ChannelTypeBool; + break; + default: + info->type = &ChannelTypeUnknown; + break; + } + + if (minDefined) { + info->min = (ChannelValueData *)mcx_calloc(1, sizeof(ChannelValueData)); + if (!info->min) { + goto cleanup; + } + ChannelValueDataSetFromReference(info->min, info->type, &min); + } + + if (maxDefined) { + info->max = (ChannelValueData*)mcx_calloc(1, sizeof(ChannelValueData)); + if (!info->max) { + goto cleanup; + } + ChannelValueDataSetFromReference(info->max, info->type, &max); + } + } + + return info; + +cleanup: + object_destroy(info); + return NULL; +} + +static void Fmu2VariableInfoDestructor(Fmu2VariableInfo * info) { + if (info->min) { + ChannelValueDataDestructor(info->min, info->type); + mcx_free(info->min); + } + if (info->max) { + ChannelValueDataDestructor(info->max, info->type); + mcx_free(info->max); + } + + if (info->desc) { mcx_free(info->desc); } +} + +static Fmu2VariableInfo * Fmu2VariableInfoCreate(Fmu2VariableInfo * info) { + info->type = &ChannelTypeUnknown; + + info->min = NULL; + info->max = NULL; + + info->desc = NULL; + + return info; +} + +OBJECT_CLASS(Fmu2VariableInfo, Object); + + Fmu2ValueData * Fmu2ValueDataScalarMake(fmi2_import_variable_t * scalar) { Fmu2ValueData * data = (Fmu2ValueData *) object_create(Fmu2ValueData); @@ -50,6 +156,44 @@ Fmu2ValueData * Fmu2ValueDataScalarMake(fmi2_import_variable_t * scalar) { return data; } +Fmu2ValueData * Fmu2ValueDataArrayMake(size_t numDims, size_t dims[], fmi2_import_variable_t ** values) { + Fmu2ValueData * data = NULL; + + if (!numDims) { + return NULL; + } + + data = (Fmu2ValueData *) object_create(Fmu2ValueData); + if (data) { + size_t num = 1; + size_t i = 0; + + for (i = 0; i < numDims; i++) { + num *= dims[i]; + } + + data->type = FMU2_VALUE_ARRAY; + data->data.array.numDims = numDims; + data->data.array.dims = mcx_copy(dims, sizeof(size_t) * numDims); + if (!data->data.array.dims) { goto error; } + + data->data.array.values = mcx_copy(values, num * sizeof(fmi2_import_variable_t *)); + if (!data->data.array.values) { goto error; } + + data->vr.array.values = mcx_calloc(num, sizeof(fmi2_value_reference_t)); + if (!data->vr.array.values) { goto error; } + + for (i = 0; i < num; i++) { + data->vr.array.values[i] = fmi2_import_get_variable_vr(data->data.array.values[i]); + } + } + + return data; +error: + if (data) { object_destroy(data); } + return NULL; +} + Fmu2ValueData * Fmu2ValueDataBinaryMake(fmi2_import_variable_t * hi, fmi2_import_variable_t * lo, fmi2_import_variable_t * size) { Fmu2ValueData * data = (Fmu2ValueData *) object_create(Fmu2ValueData); @@ -75,47 +219,157 @@ static McxStatus Fmu2ValueSetFromChannelValue(Fmu2Value * v, ChannelValue * val) return ChannelValueSet(&v->val, val); } -static McxStatus Fmu2ValueSetup(Fmu2Value * v, const char * name, Fmu2ValueData * data, const char * unit, Channel * channel) { +static McxStatus Fmu2ValueGetVariableStart(fmi2_base_type_enu_t t, fmi2_import_variable_t * var, ChannelValue * value) { + + switch (t) { + case fmi2_base_type_real: + value->value.d = fmi2_import_get_real_variable_start(fmi2_import_get_variable_as_real(var)); + break; + case fmi2_base_type_int: + value->value.i = fmi2_import_get_integer_variable_start(fmi2_import_get_variable_as_integer(var)); + break; + case fmi2_base_type_bool: + value->value.i = fmi2_import_get_boolean_variable_start(fmi2_import_get_variable_as_boolean(var)); + break; + case fmi2_base_type_str: { + const char * buffer = fmi2_import_get_string_variable_start(fmi2_import_get_variable_as_string(var)); + if (RETURN_OK != ChannelValueSetFromReference(value, &buffer)) { + return RETURN_ERROR; + } + break; + } + case fmi2_base_type_enum: + value->value.i = fmi2_import_get_enum_variable_start(fmi2_import_get_variable_as_enum(var)); + break; + default: + mcx_log(LOG_ERROR, "Fmu2Value: Setup failed: Base type %s not supported", fmi2_base_type_to_string(t)); + return RETURN_ERROR; + } + + return RETURN_OK; +} + +static McxStatus Fmu2ValueGetArrayVariableStart(fmi2_base_type_enu_t t, fmi2_import_variable_t * var, mcx_array * a, size_t i) { + + switch (t) { + case fmi2_base_type_real: + ((double *)a->data)[i] = fmi2_import_get_real_variable_start(fmi2_import_get_variable_as_real(var)); + break; + case fmi2_base_type_int: + ((int *)a->data)[i] = fmi2_import_get_integer_variable_start(fmi2_import_get_variable_as_integer(var)); + break; + case fmi2_base_type_enum: + ((int *)a->data)[i] = fmi2_import_get_enum_variable_start(fmi2_import_get_variable_as_enum(var)); + break; + case fmi2_base_type_bool: + ((int *)a->data)[i] = fmi2_import_get_boolean_variable_start(fmi2_import_get_variable_as_boolean(var)); + break; + default: + mcx_log(LOG_ERROR, "Fmu2Value: Setup failed: Array base type %s not supported", fmi2_base_type_to_string(t)); + return RETURN_ERROR; + } + + return RETURN_OK; +} + +static McxStatus Fmu2ValueGetBinaryVariableStart(fmi2_import_variable_t * varHi, fmi2_import_variable_t * varLo, fmi2_import_variable_t * varSize, ChannelValue * value) { fmi2_base_type_enu_t t; + t = fmi2_import_get_variable_base_type(varHi); + if (t != fmi2_base_type_int) { + mcx_log(LOG_ERROR, "Fmu2Value: Setup failed: Binary base type (hi) %s not supported", fmi2_base_type_to_string(t)); + return RETURN_ERROR; + } + t = fmi2_import_get_variable_base_type(varLo); + if (t != fmi2_base_type_int) { + mcx_log(LOG_ERROR, "Fmu2Value: Setup failed: Binary base type (lo) %s not supported", fmi2_base_type_to_string(t)); + return RETURN_ERROR; + } + t = fmi2_import_get_variable_base_type(varSize); + if (t != fmi2_base_type_int) { + mcx_log(LOG_ERROR, "Fmu2Value: Setup failed: Binary base type (size) %s not supported", fmi2_base_type_to_string(t)); + return RETURN_ERROR; + } + + fmi2_integer_t hi = fmi2_import_get_integer_variable_start(fmi2_import_get_variable_as_integer(varHi)); + fmi2_integer_t lo = fmi2_import_get_integer_variable_start(fmi2_import_get_variable_as_integer(varLo)); + fmi2_integer_t size = fmi2_import_get_integer_variable_start(fmi2_import_get_variable_as_integer(varSize)); + + binary_string b; + + b.len = size; + b.data = (char *) ((((long long)hi & 0xffffffff) << 32) | (lo & 0xffffffff)); + + if (RETURN_OK != ChannelValueSetFromReference(value, &b)) { + mcx_log(LOG_ERROR, "Fmu2Value: Could not set value"); + return RETURN_ERROR; + } + + return RETURN_OK; +} + + +static McxStatus Fmu2ValueSetup(Fmu2Value * v, const char * name, Fmu2ValueData * data, const char * unit, Channel * channel) { if (!name || !data) { mcx_log(LOG_ERROR, "Fmu2Value: Setup failed: Name or data missing"); return RETURN_ERROR; } - t = fmi2_import_get_variable_base_type(data->data.scalar); - v->name = mcx_string_copy(name); v->unit = mcx_string_copy(unit); v->data = data; v->channel = channel; - ChannelValueInit(&v->val, Fmi2TypeToChannelType(t)); if (!v->name) { mcx_log(LOG_ERROR, "Fmu2Value: Setup failed: Cannot copy name"); return RETURN_ERROR; } - switch (t) { - case fmi2_base_type_real: - v->val.value.d = fmi2_import_get_real_variable_start(fmi2_import_get_variable_as_real(data->data.scalar)); - break; - case fmi2_base_type_int: - v->val.value.i = fmi2_import_get_integer_variable_start(fmi2_import_get_variable_as_integer(data->data.scalar)); - break; - case fmi2_base_type_bool: - v->val.value.i = fmi2_import_get_boolean_variable_start(fmi2_import_get_variable_as_boolean(data->data.scalar)); - break; - case fmi2_base_type_str: { - const char * buffer = fmi2_import_get_string_variable_start(fmi2_import_get_variable_as_string(data->data.scalar)); - ChannelValueSetFromReference(&v->val, &buffer); - break; - } - case fmi2_base_type_enum: - v->val.value.i = fmi2_import_get_enum_variable_start(fmi2_import_get_variable_as_enum(data->data.scalar)); - break; - default: - mcx_log(LOG_ERROR, "Fmu2Value: Setup failed: Base type %s not supported", fmi2_base_type_to_string(t)); + if (v->data->type == FMU2_VALUE_SCALAR) { + fmi2_base_type_enu_t t = fmi2_import_get_variable_base_type(data->data.scalar); + + ChannelValueInit(&v->val, ChannelTypeClone(Fmi2TypeToChannelType(t))); + + + if (RETURN_OK != Fmu2ValueGetVariableStart(t, data->data.scalar, &v->val)) { + return RETURN_ERROR; + } + } else if (v->data->type == FMU2_VALUE_ARRAY) { + fmi2_base_type_enu_t t = fmi2_import_get_variable_base_type(data->data.array.values[0]); + + ChannelValueInit(&v->val, ChannelTypeArray(Fmi2TypeToChannelType(t), data->data.array.numDims, data->data.array.dims)); + + if (data->data.array.numDims == 0) { + mcx_log(LOG_ERROR, "Fmu2Value: Setup failed: Array of dimension 0 is not supported"); + return RETURN_ERROR; + } + + size_t i = 0, n = 1; + + for (i = 0; i < data->data.array.numDims; i++) { + n *= data->data.array.dims[i]; + } + + mcx_array * a = (mcx_array *) ChannelValueDataPointer(&v->val); + + for (i = 0; i < n; i++) { + if (RETURN_OK != Fmu2ValueGetArrayVariableStart(t, data->data.array.values[i], a, i)) { + return RETURN_ERROR; + } + } + } else if (v->data->type == FMU2_VALUE_BINARY_OSI) { + ChannelValueInit(&v->val, &ChannelTypeBinary); + + if (RETURN_OK != Fmu2ValueGetBinaryVariableStart( + data->data.binary.hi, + data->data.binary.lo, + data->data.binary.size, + &v->val)) { + return RETURN_ERROR; + } + + + } else { return RETURN_ERROR; } @@ -135,7 +389,9 @@ static void Fmu2ValueDestructor(Fmu2Value * v) { mcx_free(v->unit); v->unit = NULL; } + Fmu2ValueDataDestructor(v->data); object_destroy(v->data); + object_destroy(v->info); ChannelValueDestructor(&v->val); } @@ -148,7 +404,9 @@ static Fmu2Value * Fmu2ValueCreate(Fmu2Value * v) { v->unit = NULL; v->data = NULL; v->channel = NULL; - ChannelValueInit(&v->val, CHANNEL_UNKNOWN); + v->info = NULL; + + ChannelValueInit(&v->val, ChannelTypeClone(&ChannelTypeUnknown)); return v; } @@ -174,6 +432,15 @@ Fmu2Value * Fmu2ValueScalarMake(const char * name, fmi2_import_variable_t * scal Fmu2ValueData * data = Fmu2ValueDataScalarMake(scalar); Fmu2Value * value = Fmu2ValueMake(name, data, unit, channel); + value->info = Fmu2VariableInfoMake(scalar); + + return value; +} + +Fmu2Value * Fmu2ValueArrayMake(const char * name, size_t numDims, size_t dims[], fmi2_import_variable_t ** values, const char * unit, Channel * channel) { + Fmu2ValueData * data = Fmu2ValueDataArrayMake(numDims, dims, values); + Fmu2Value * value = Fmu2ValueMake(name, data, unit, channel); + return value; } @@ -188,6 +455,86 @@ void Fmu2ValuePrintDebug(Fmu2Value * val) { mcx_log(LOG_DEBUG, "Fmu2Value { name: \"%s\" }", val->name); } +Fmu2Value * Fmu2ReadFmu2ScalarValue(const char * logPrefix, ChannelType * type, const char * channelName, const char * unitString, fmi2_import_t * fmiImport) { + Fmu2Value * val = NULL; + fmi2_import_variable_t * var = NULL; + + var = fmi2_import_get_variable_by_name(fmiImport, channelName); + if (!var) { + mcx_log(LOG_ERROR, "%s: Could not get variable %s", logPrefix, channelName); + return NULL; + } + + if (!ChannelTypeEq(type, Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(var)))) { + mcx_log(LOG_ERROR, "%s: Variable types of %s do not match", logPrefix, channelName); + mcx_log(LOG_ERROR, "%s: Expected: %s, Imported from FMU: %s", logPrefix, + ChannelTypeToString(type), + ChannelTypeToString(Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(var)))); + return NULL; + } + + val = Fmu2ValueScalarMake(channelName, var, unitString, NULL); + if (!val) { + mcx_log(LOG_ERROR, "%s: Could not set value for channel %s", logPrefix, channelName); + return NULL; + } + + return val; +} + +Fmu2Value * Fmu2ReadFmu2ArrayValue(const char * logPrefix, ChannelType * type, const char * channelName, ChannelDimension * dimension, const char * unitString, fmi2_import_t * fmiImport) { + Fmu2Value * val = NULL; + fmi2_import_variable_t * var = NULL; + + if (dimension->num > 1) { + mcx_log(LOG_ERROR, "%s: Port %s: Invalid dimension", logPrefix, channelName); + goto cleanup; + } + + size_t i = 0; + size_t startIdx = dimension->startIdxs[0]; + size_t endIdx = dimension->endIdxs[0]; + + fmi2_import_variable_t ** vars = mcx_calloc(endIdx - startIdx + 1, sizeof(fmi2_import_variable_t *)); + if (!vars) { + goto cleanup; + } + + for (i = startIdx; i <= endIdx; i++) { + char * indexedChannelName = CreateIndexedName(channelName, i); + fmi2_import_variable_t * var = fmi2_import_get_variable_by_name(fmiImport, indexedChannelName); + if (!var) { + mcx_log(LOG_ERROR, "%s: Could not get variable %s", logPrefix, indexedChannelName); + goto cleanup; + } + if (!ChannelTypeEq(ChannelTypeArrayInner(type), Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(var)))) { + mcx_log(LOG_ERROR, "%s: Variable types of %s do not match", logPrefix, indexedChannelName); + mcx_log(LOG_ERROR, "%s: Expected: %s, Imported from FMU: %s", logPrefix, + ChannelTypeToString(ChannelTypeArrayInner(type)), + ChannelTypeToString(Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(var)))); + goto cleanup; + } + vars[i - startIdx] = var; + + mcx_free(indexedChannelName); + } + + size_t dims[] = { endIdx - startIdx + 1 }; + val = Fmu2ValueArrayMake(channelName, 1 /* numDims */, dims, vars, unitString, NULL); + if (!val) { + mcx_log(LOG_ERROR, "%s: Could not set value for channel %s", logPrefix, channelName); + goto cleanup; + } + +cleanup: + if (vars) { + mcx_free(vars); + } + + return val; +} + + #ifdef __cplusplus } /* closing brace for extern "C" */ #endif /* __cplusplus */ \ No newline at end of file diff --git a/src/fmu/Fmu2Value.h b/src/fmu/Fmu2Value.h index 7664554..fb85f7e 100644 --- a/src/fmu/Fmu2Value.h +++ b/src/fmu/Fmu2Value.h @@ -13,6 +13,7 @@ #include "CentralParts.h" #include "core/channels/Channel.h" +#include "core/channels/ChannelDimension.h" #include "fmilib.h" #ifdef __cplusplus @@ -26,9 +27,10 @@ typedef struct Fmu2ValueData Fmu2ValueData; extern const struct ObjectClass _Fmu2ValueData; typedef enum Fmu2ValueType { - FMU2_VALUE_SCALAR - , FMU2_VALUE_BINARY_OSI - , FMU2_VALUE_INVALID + FMU2_VALUE_SCALAR, + FMU2_VALUE_ARRAY, + FMU2_VALUE_BINARY_OSI, + FMU2_VALUE_INVALID } Fmu2ValueType; struct Fmu2ValueData { @@ -37,6 +39,11 @@ struct Fmu2ValueData { Fmu2ValueType type; union { fmi2_import_variable_t * scalar; + struct { + size_t numDims; + size_t * dims; + fmi2_import_variable_t ** values; + } array; struct { fmi2_import_variable_t * lo; fmi2_import_variable_t * hi; @@ -45,6 +52,9 @@ struct Fmu2ValueData { } data; union { fmi2_value_reference_t scalar; + struct { + fmi2_value_reference_t * values; + } array; struct { fmi2_value_reference_t lo; fmi2_value_reference_t hi; @@ -54,8 +64,24 @@ struct Fmu2ValueData { }; Fmu2ValueData * Fmu2ValueDataScalarMake(fmi2_import_variable_t * scalar); +Fmu2ValueData * Fmu2ValueDataArrayMake(size_t numDims, size_t dims[], fmi2_import_variable_t ** values); Fmu2ValueData * Fmu2ValueDataBinaryMake(fmi2_import_variable_t * hi, fmi2_import_variable_t * lo, fmi2_import_variable_t * size); + +extern const struct ObjectClass _Fmu2VariableInfo; + +typedef struct Fmu2VariableInfo { + Object _; + + ChannelType * type; + + ChannelValueData * min; + ChannelValueData * max; + + char * desc; +} Fmu2VariableInfo; + + struct Fmu2Value; typedef struct Fmu2Value Fmu2Value; @@ -78,15 +104,20 @@ struct Fmu2Value { Channel * channel; Fmu2ValueData * data; + Fmu2VariableInfo * info; ChannelValue val; }; Fmu2Value * Fmu2ValueMake(const char * name, Fmu2ValueData * data, const char * unit, Channel * channel); Fmu2Value * Fmu2ValueScalarMake(const char * name, fmi2_import_variable_t * scalar, const char * unit, Channel * channel); +Fmu2Value * Fmu2ValueArrayMake(const char * name, size_t numDims, size_t dims[], fmi2_import_variable_t ** values, const char * unit, Channel * channel); Fmu2Value * Fmu2ValueBinaryMake(const char * name, fmi2_import_variable_t * hi, fmi2_import_variable_t * lo, fmi2_import_variable_t * size, Channel * channel); void Fmu2ValuePrintDebug(Fmu2Value * val); +Fmu2Value * Fmu2ReadFmu2ScalarValue(const char * logPrefix, ChannelType * type, const char * channelName, const char * unitString, fmi2_import_t * fmiImport); +Fmu2Value * Fmu2ReadFmu2ArrayValue(const char * logPrefix, ChannelType * type, const char * channelName, ChannelDimension * dimension, const char * unitString, fmi2_import_t * fmiImport); + #ifdef __cplusplus } /* closing brace for extern "C" */ #endif /* __cplusplus */ diff --git a/src/fmu/common_fmu1.c b/src/fmu/common_fmu1.c index dc72d00..949e66d 100644 --- a/src/fmu/common_fmu1.c +++ b/src/fmu/common_fmu1.c @@ -111,20 +111,20 @@ static fmi1_callback_functions_t fmi1Callbacks = { NULL }; -ChannelType Fmi1TypeToChannelType(fmi1_base_type_enu_t type) { +ChannelType * Fmi1TypeToChannelType(fmi1_base_type_enu_t type) { switch (type) { case fmi1_base_type_real: - return CHANNEL_DOUBLE; + return &ChannelTypeDouble; case fmi1_base_type_int: - return CHANNEL_INTEGER; + return &ChannelTypeInteger; case fmi1_base_type_bool: - return CHANNEL_BOOL; + return &ChannelTypeBool; case fmi1_base_type_str: - return CHANNEL_STRING; + return &ChannelTypeString; case fmi1_base_type_enum: - return CHANNEL_INTEGER; + return &ChannelTypeInteger; default: - return CHANNEL_UNKNOWN; + return &ChannelTypeUnknown; } } @@ -282,15 +282,17 @@ Fmu1Value * Fmu1ReadParamValue(ScalarParameterInput * input, fmi1_import_t * imp return NULL; } - ChannelValueInit(&chVal, input->type); - ChannelValueSetFromReference(&chVal, &input->value.value); + ChannelValueInit(&chVal, ChannelTypeClone(input->type)); + if (RETURN_OK != ChannelValueSetFromReference(&chVal, &input->value.value)) { + return NULL; + } var = fmi1_import_get_variable_by_name(import, input->name); if (!var) { return NULL; } - val = Fmu1ValueMake(input->name, var, NULL); + val = Fmu1ValueScalarMake(input->name, var, NULL); if (!val) { return NULL; } @@ -373,19 +375,23 @@ static ObjectContainer * Fmu1ReadArrayParamValues(const char * name, goto cleanup_1; } - val = Fmu1ValueMake(varName, var, NULL); + val = Fmu1ValueScalarMake(varName, var, NULL); if (!val) { retVal = RETURN_ERROR; goto cleanup_1; } - if (input->type == CHANNEL_DOUBLE) { - ChannelValueInit(&chVal, CHANNEL_DOUBLE); - ChannelValueSetFromReference(&chVal, &((double *)input->values)[index]); + if (ChannelTypeEq(input->type, &ChannelTypeDouble)) { + ChannelValueInit(&chVal, ChannelTypeClone(&ChannelTypeDouble)); + if (RETURN_OK != ChannelValueSetFromReference(&chVal, &((double *)input->values)[index])) { + return RETURN_ERROR; + } } else { // integer - ChannelValueInit(&chVal, CHANNEL_INTEGER); - ChannelValueSetFromReference(&chVal, &((int *)input->values)[index]); + ChannelValueInit(&chVal, ChannelTypeClone(&ChannelTypeInteger)); + if (RETURN_OK != ChannelValueSetFromReference(&chVal, &((int *)input->values)[index])) { + return RETURN_ERROR; + } } retVal = val->SetFromChannelValue(val, &chVal); @@ -467,16 +473,6 @@ ObjectContainer * Fmu1ReadParams(ParametersInput * input, fmi1_import_t * import } if (parameterInput->type == PARAMETER_ARRAY) { - // read parameter dimensions (if any) - should only be defined for array parameters - if (parameterInput->parameter.arrayParameter->numDims >= 2 && - parameterInput->parameter.arrayParameter->dims[1] && - !parameterInput->parameter.arrayParameter->dims[0]) { - mcx_log(LOG_ERROR, "FMU: Array parameter %s: Missing definition for the first dimension " - "while the second dimension is defined.", parameterInput->parameter.arrayParameter->name); - ret = NULL; - goto cleanup; - } - // array - split it into scalars vals = Fmu1ReadArrayParamValues(name, parameterInput->parameter.arrayParameter, import, params); if (vals == NULL) { @@ -532,9 +528,6 @@ ObjectContainer * Fmu1ReadParams(ParametersInput * input, fmi1_import_t * import McxStatus Fmu1SetVariable(Fmu1CommonStruct * fmu, Fmu1Value * fmuVal) { fmi1_status_t status = fmi1_status_ok; - fmi1_import_variable_t * var = fmuVal->var; - fmi1_value_reference_t vr[] = { fmuVal->vr }; - Channel * channel = fmuVal->channel; if (channel && FALSE == channel->IsDefinedDuringInit(channel)) { MCX_DEBUG_LOG("Fmu1SetVariable: %s not set: no defined value during initialization", fmuVal->name); @@ -542,13 +535,14 @@ McxStatus Fmu1SetVariable(Fmu1CommonStruct * fmu, Fmu1Value * fmuVal) { } ChannelValue * const chVal = &fmuVal->val; - ChannelType type = ChannelValueType(chVal); + ChannelType * type = ChannelValueType(chVal); - switch (type) { + switch (type->con) { case CHANNEL_DOUBLE: { + fmi1_value_reference_t vr[] = {fmuVal->data->vr.scalar}; double value = chVal->value.d; - if (fmi1_variable_is_negated_alias == fmi1_import_get_variable_alias_kind(var)) { + if (fmi1_variable_is_negated_alias == fmi1_import_get_variable_alias_kind(fmuVal->data->var.scalar)) { value *= -1.; } status = fmi1_import_set_real(fmu->fmiImport, vr, 1, &value); @@ -556,8 +550,9 @@ McxStatus Fmu1SetVariable(Fmu1CommonStruct * fmu, Fmu1Value * fmuVal) { break; case CHANNEL_INTEGER: { + fmi1_value_reference_t vr[] = {fmuVal->data->vr.scalar}; int value = chVal->value.i; - if (fmi1_variable_is_negated_alias == fmi1_import_get_variable_alias_kind(var)) { + if (fmi1_variable_is_negated_alias == fmi1_import_get_variable_alias_kind(fmuVal->data->var.scalar)) { value *= -1; } status = fmi1_import_set_integer(fmu->fmiImport, vr, 1, &value); @@ -565,16 +560,38 @@ McxStatus Fmu1SetVariable(Fmu1CommonStruct * fmu, Fmu1Value * fmuVal) { break; case CHANNEL_BOOL: { + fmi1_value_reference_t vr[] = {fmuVal->data->vr.scalar}; fmi1_boolean_t value = chVal->value.i; - if (fmi1_variable_is_negated_alias == fmi1_import_get_variable_alias_kind(var)) { + if (fmi1_variable_is_negated_alias == fmi1_import_get_variable_alias_kind(fmuVal->data->var.scalar)) { value = !value; } status = fmi1_import_set_boolean(fmu->fmiImport, vr, 1, &value); } break; case CHANNEL_STRING: + { + fmi1_value_reference_t vr[] = {fmuVal->data->vr.scalar}; status = fmi1_import_set_string(fmu->fmiImport, vr, 1, (fmi1_string_t *) &chVal->value.s); break; + } + case CHANNEL_ARRAY: + { + fmi1_value_reference_t * vrs = fmuVal->data->vr.array.values; + mcx_array * a = (mcx_array *) ChannelValueDataPointer(&fmuVal->val); + + size_t num = mcx_array_num_elements(a); + void * vals = a->data; + + if (ChannelTypeEq(a->type, &ChannelTypeDouble)) { + status = fmi1_import_set_real(fmu->fmiImport, vrs, num, vals); + } else if (ChannelTypeEq(a->type, &ChannelTypeInteger)) { + status = fmi1_import_set_integer(fmu->fmiImport, vrs, num, vals); + } else { + mcx_log(LOG_ERROR, "FMU: Unsupported array variable type: %s", ChannelTypeToString(a->type)); + return RETURN_ERROR; + } + break; + } default: mcx_log(LOG_WARNING, "FMU: Unknown variable type"); break; @@ -583,13 +600,13 @@ McxStatus Fmu1SetVariable(Fmu1CommonStruct * fmu, Fmu1Value * fmuVal) { if (fmi1_status_ok != status) { if (fmi1_status_error == status || fmi1_status_fatal == status) { fmu->runOk = fmi1_false; - mcx_log(LOG_ERROR, "FMU: Setting of variable %s (%d) failed", fmi1_import_get_variable_name(var), vr[0]); + mcx_log(LOG_ERROR, "FMU: Setting of variable %s failed", fmuVal->name); return RETURN_ERROR; } else { if (fmi1_status_warning == status) { - mcx_log(LOG_WARNING, "FMU: Setting of variable %s (%d) returned with a warning", fmi1_import_get_variable_name(var), vr[0]); + mcx_log(LOG_WARNING, "FMU: Setting of variable %s returned with a warning", fmuVal->name); } else if (fmi1_status_discard == status) { - mcx_log(LOG_WARNING, "FMU: Setting of variable %s (%d) discarded", fmi1_import_get_variable_name(var), vr[0]); + mcx_log(LOG_WARNING, "FMU: Setting of variable %s discarded", fmuVal->name); } } } else { @@ -622,37 +639,64 @@ McxStatus Fmu1SetVariableArray(Fmu1CommonStruct * fmu, ObjectContainer * vals) { McxStatus Fmu1GetVariable(Fmu1CommonStruct * fmu, Fmu1Value * fmuVal) { fmi1_status_t status = fmi1_status_ok; - fmi1_import_variable_t * var = fmuVal->var; - fmi1_value_reference_t vr[] = { fmuVal->vr }; - ChannelValue * const chVal = &fmuVal->val; - ChannelType type = ChannelValueType(chVal); + ChannelType * type = ChannelValueType(chVal); - switch (type) { + switch (type->con) { case CHANNEL_DOUBLE: - status = fmi1_import_get_real(fmu->fmiImport, vr, 1, (fmi1_real_t *)ChannelValueReference(chVal)); - if (fmi1_variable_is_negated_alias == fmi1_import_get_variable_alias_kind(var)) { + { + fmi1_value_reference_t vr[] = {fmuVal->data->vr.scalar}; + status = fmi1_import_get_real(fmu->fmiImport, vr, 1, (fmi1_real_t *) ChannelValueDataPointer(chVal)); + if (fmi1_variable_is_negated_alias == fmi1_import_get_variable_alias_kind(fmuVal->data->var.scalar)) { fmuVal->val.value.d *= -1.; } break; + } case CHANNEL_INTEGER: - status = fmi1_import_get_integer(fmu->fmiImport, vr, 1, (fmi1_integer_t *)ChannelValueReference(chVal)); - if (fmi1_variable_is_negated_alias == fmi1_import_get_variable_alias_kind(var)) { + { + fmi1_value_reference_t vr[] = {fmuVal->data->vr.scalar}; + status = fmi1_import_get_integer(fmu->fmiImport, vr, 1, (fmi1_integer_t *) ChannelValueDataPointer(chVal)); + if (fmi1_variable_is_negated_alias == fmi1_import_get_variable_alias_kind(fmuVal->data->var.scalar)) { fmuVal->val.value.i *= -1; } break; + } case CHANNEL_BOOL: - status = fmi1_import_get_boolean(fmu->fmiImport, vr, 1, (fmi1_boolean_t *)ChannelValueReference(chVal)); - if (fmi1_variable_is_negated_alias == fmi1_import_get_variable_alias_kind(var)) { + { + fmi1_value_reference_t vr[] = {fmuVal->data->vr.scalar}; + status = fmi1_import_get_boolean(fmu->fmiImport, vr, 1, (fmi1_boolean_t *) ChannelValueDataPointer(chVal)); + if (fmi1_variable_is_negated_alias == fmi1_import_get_variable_alias_kind(fmuVal->data->var.scalar)) { fmuVal->val.value.i = !fmuVal->val.value.i; } break; + } case CHANNEL_STRING: { + fmi1_value_reference_t vr[] = {fmuVal->data->vr.scalar}; char * buffer = NULL; status = fmi1_import_get_string(fmu->fmiImport, vr, 1, (fmi1_string_t *)&buffer); - ChannelValueSetFromReference(chVal, &buffer); + if (RETURN_OK != ChannelValueSetFromReference(chVal, &buffer)) { + return RETURN_ERROR; + } + break; + } + case CHANNEL_ARRAY: + { + fmi1_value_reference_t * vrs = fmuVal->data->vr.array.values; + mcx_array * a = (mcx_array *) ChannelValueDataPointer(&fmuVal->val); + + size_t num = mcx_array_num_elements(a); + void * vals = a->data; + + if (ChannelTypeEq(a->type, &ChannelTypeDouble)) { + status = fmi1_import_get_real(fmu->fmiImport, vrs, num, vals); + } else if (ChannelTypeEq(a->type, &ChannelTypeInteger)) { + status = fmi1_import_get_integer(fmu->fmiImport, vrs, num, vals); + } else { + mcx_log(LOG_ERROR, "FMU: Unsupported array variable type: %s", ChannelTypeToString(a->type)); + return RETURN_ERROR; + } break; } default: @@ -663,7 +707,7 @@ McxStatus Fmu1GetVariable(Fmu1CommonStruct * fmu, Fmu1Value * fmuVal) { if (fmi1_status_ok != status) { if (fmi1_status_error == status || fmi1_status_fatal == status) { fmu->runOk = fmi1_false; - mcx_log(LOG_ERROR, "FMU: Getting of variable %s (%d) failed", fmi1_import_get_variable_name(var), vr[0]); + mcx_log(LOG_ERROR, "FMU: Getting of variable %s failed", fmuVal->name); return RETURN_ERROR; } else { // TODO: handle warning @@ -715,7 +759,7 @@ McxStatus fmi1CreateValuesOutOfVariables(ObjectContainer * vals, fmi1_import_var Fmu1Value * val = NULL; const char * name = fmi1_import_get_variable_name(actualVar); - val = Fmu1ValueMake(name, actualVar, NULL); + val = Fmu1ValueScalarMake(name, actualVar, NULL); retVal = vals->PushBack(vals, (Object *)val); if (RETURN_ERROR == retVal) { @@ -737,7 +781,7 @@ McxStatus fmi1AddLocalChannelsFromLocalValues(ObjectContainer * vals, const char for (i = 0; i < sizeList; i++) { Fmu1Value * val = (Fmu1Value *) vals->At(vals, i); - fmi1_import_variable_t * actualVar = val->var; + fmi1_import_variable_t * actualVar = val->data->var.scalar; // TODO array const char * name = NULL; fmi1_base_type_enu_t type; @@ -762,7 +806,7 @@ McxStatus fmi1AddLocalChannelsFromLocalValues(ObjectContainer * vals, const char } - retVal = DatabusAddLocalChannel(db, name, buffer, unitName, ChannelValueReference(&val->val), ChannelValueType(&val->val)); + retVal = DatabusAddLocalChannel(db, name, buffer, unitName, ChannelValueDataPointer(&val->val), ChannelValueType(&val->val)); mcx_free(buffer); if (RETURN_ERROR == retVal) { mcx_log(LOG_ERROR, "%s: Adding channel %s to databus failed", compName, name); diff --git a/src/fmu/common_fmu1.h b/src/fmu/common_fmu1.h index eed6676..945e655 100644 --- a/src/fmu/common_fmu1.h +++ b/src/fmu/common_fmu1.h @@ -36,7 +36,7 @@ typedef enum enum_VAR_TYPE { VAR_STRING } FMI_VAR_TYPE; -ChannelType Fmi1TypeToChannelType(fmi1_base_type_enu_t type); +ChannelType * Fmi1TypeToChannelType(fmi1_base_type_enu_t type); typedef struct Fmu1CommonStruct { diff --git a/src/fmu/common_fmu2.c b/src/fmu/common_fmu2.c index 2aaf9d7..cb8fd8c 100644 --- a/src/fmu/common_fmu2.c +++ b/src/fmu/common_fmu2.c @@ -9,11 +9,13 @@ ********************************************************************************/ #include "fmu/common_fmu2.h" +#include "core/channels/ChannelValue.h" #include "fmu/common_fmu.h" /* for jm callbacks */ #include "fmu/Fmu2Value.h" #include "core/channels/ChannelInfo.h" +#include "core/parameters/ParameterProxies.h" #include "reader/model/parameters/ArrayParameterDimensionInput.h" #include "reader/model/parameters/ParameterInput.h" @@ -23,6 +25,8 @@ #include "util/stdlib.h" #include "util/signals.h" +#include "objects/Map.h" + #include "fmilib.h" #ifdef __cplusplus @@ -30,8 +34,8 @@ extern "C" { #endif /* __cplusplus */ -fmi2_base_type_enu_t ChannelTypeToFmi2Type(ChannelType type) { - switch (type) { +fmi2_base_type_enu_t ChannelTypeToFmi2Type(ChannelType * type) { + switch (type->con) { case CHANNEL_DOUBLE: return fmi2_base_type_real; case CHANNEL_INTEGER: @@ -45,20 +49,20 @@ fmi2_base_type_enu_t ChannelTypeToFmi2Type(ChannelType type) { } } -ChannelType Fmi2TypeToChannelType(fmi2_base_type_enu_t type) { +ChannelType * Fmi2TypeToChannelType(fmi2_base_type_enu_t type) { switch (type) { case fmi2_base_type_real: - return CHANNEL_DOUBLE; + return &ChannelTypeDouble; case fmi2_base_type_int: - return CHANNEL_INTEGER; + return &ChannelTypeInteger; case fmi2_base_type_bool: - return CHANNEL_BOOL; + return &ChannelTypeBool; case fmi2_base_type_str: - return CHANNEL_STRING; + return &ChannelTypeString; case fmi2_base_type_enum: - return CHANNEL_INTEGER; + return &ChannelTypeInteger; default: - return CHANNEL_UNKNOWN; + return &ChannelTypeUnknown; } } @@ -93,6 +97,8 @@ McxStatus Fmu2CommonStructInit(Fmu2CommonStruct * fmu) { fmu->tunableParams = (ObjectContainer *) object_create(ObjectContainer); fmu->initialValues = (ObjectContainer *) object_create(ObjectContainer); + fmu->arrayParams = (ObjectContainer *) object_create(ObjectContainer); + fmu->connectedIn = (ObjectContainer *) object_create(ObjectContainer); fmu->numLogCategories = 0; @@ -243,6 +249,11 @@ void Fmu2CommonStructDestructor(Fmu2CommonStruct * fmu) { object_destroy(fmu->params); } + if (fmu->arrayParams) { + fmu->arrayParams->DestroyObjects(fmu->arrayParams); + object_destroy(fmu->arrayParams); + } + if (fmu->initialValues) { fmu->initialValues->DestroyObjects(fmu->initialValues); object_destroy(fmu->initialValues); @@ -290,8 +301,10 @@ Fmu2Value * Fmu2ReadParamValue(ScalarParameterInput * input, return NULL; } - ChannelValueInit(&chVal, input->type); - ChannelValueSetFromReference(&chVal, &input->value.value); + ChannelValueInit(&chVal, ChannelTypeClone(input->type)); + if (RETURN_OK != ChannelValueSetFromReference(&chVal, &input->value.value)) { + return NULL; + } var = fmi2_import_get_variable_by_name(import, input->name); if (!var) { @@ -348,8 +361,8 @@ static ObjectContainer* Fmu2ReadArrayParamValues(const char * name, end2 = input->dims[1]->end; } - for (k = start2; k <= end2; k++) { - for (j = start1; j <= end1; j++, index++) { + for (k = start1; k <= end1; k++) { + for (j = start2; j <= end2; j++, index++) { Fmu2Value * val = NULL; char * varName = (char *) mcx_calloc(stringBufferLength, sizeof(char)); fmi2_import_variable_t * var = NULL; @@ -363,7 +376,7 @@ static ObjectContainer* Fmu2ReadArrayParamValues(const char * name, if (input->numDims == 2) { snprintf(varName, stringBufferLength, "%s[%zu,%zu]", name, k, j); } else { - snprintf(varName, stringBufferLength, "%s[%zu]", name, j); + snprintf(varName, stringBufferLength, "%s[%zu]", name, k); } var = fmi2_import_get_variable_by_name(import, varName); @@ -379,12 +392,18 @@ static ObjectContainer* Fmu2ReadArrayParamValues(const char * name, goto fmu2_read_array_param_values_for_cleanup; } - if (input->type == CHANNEL_DOUBLE) { - ChannelValueInit(&chVal, CHANNEL_DOUBLE); - ChannelValueSetFromReference(&chVal, &((double *)input->values)[index]); + if (ChannelTypeEq(input->type, &ChannelTypeDouble)) { + ChannelValueInit(&chVal, ChannelTypeClone(&ChannelTypeDouble)); + if (RETURN_OK != ChannelValueSetFromReference(&chVal, &((double *)input->values)[index])) { + retVal = RETURN_ERROR; + goto fmu2_read_array_param_values_for_cleanup; + } } else { // integer - ChannelValueInit(&chVal, CHANNEL_INTEGER); - ChannelValueSetFromReference(&chVal, &((int *)input->values)[index]); + ChannelValueInit(&chVal, ChannelTypeClone(&ChannelTypeInteger)); + if (RETURN_OK != ChannelValueSetFromReference(&chVal, &((int *)input->values)[index])) { + retVal = RETURN_ERROR; + goto fmu2_read_array_param_values_for_cleanup; + } } retVal = val->SetFromChannelValue(val, &chVal); @@ -451,98 +470,131 @@ static ObjectContainer* Fmu2ReadArrayParamValues(const char * name, } // Reads parameters from the input file (both scalar and array). -// +// If arrayParams is given, creates proxy views to array elements. // Ignores parameters provided via the `ignore` argument. -ObjectContainer * Fmu2ReadParams(ParametersInput * input, fmi2_import_t * import, ObjectContainer * ignore) { - ObjectContainer * params = (ObjectContainer *) object_create(ObjectContainer); - ObjectContainer * ret = params; // used for unified cleanup via goto +McxStatus Fmu2ReadParams(ObjectContainer * params, ObjectContainer * arrayParams, ParametersInput * input, fmi2_import_t * import, ObjectContainer * ignore) { + McxStatus retVal = RETURN_OK; size_t i = 0; size_t num = 0; - McxStatus retVal = RETURN_OK; if (!params) { - return NULL; + return RETURN_ERROR; } num = input->parameters->Size(input->parameters); for (i = 0; i < num; i++) { ParameterInput * parameterInput = (ParameterInput *) input->parameters->At(input->parameters, i); - char * name = NULL; - Fmu2Value * val = NULL; - ObjectContainer * vals = NULL; - name = mcx_string_copy(parameterInput->parameter.arrayParameter->name); if (!name) { - ret = NULL; - goto fmu2_read_params_for_cleanup; + retVal = RETURN_ERROR; + goto cleanup_0; } // ignore the parameter if it is in the `ignore` container if (ignore && ignore->GetNameIndex(ignore, name) >= 0) { - goto fmu2_read_params_for_cleanup; + goto cleanup_0; } if (parameterInput->type == PARAMETER_ARRAY) { - // read parameter dimensions (if any) - should only be defined for array parameters - if (parameterInput->parameter.arrayParameter->numDims >=2 && - parameterInput->parameter.arrayParameter->dims[1] && - !parameterInput->parameter.arrayParameter->dims[0]) { - mcx_log(LOG_ERROR, "FMU: Array parameter %s: Missing definition for the first dimension " - "while the second dimension is defined.", parameterInput->parameter.arrayParameter->name); - ret = NULL; - goto fmu2_read_params_for_cleanup; - } + ObjectContainer * vals = NULL; + ArrayParameterProxy * proxy = NULL; + size_t j = 0; // array - split it into scalars vals = Fmu2ReadArrayParamValues(name, parameterInput->parameter.arrayParameter, import, params); if (vals == NULL) { mcx_log(LOG_ERROR, "FMU: Could not read array parameter %s", name); - ret = NULL; - goto fmu2_read_params_for_cleanup; + retVal = RETURN_ERROR; + goto cleanup_1; + } + + if (arrayParams) { + // set up a proxy that will reference the individual scalars + proxy = (ArrayParameterProxy *) object_create(ArrayParameterProxy); + if (!proxy) { + mcx_log(LOG_ERROR, "FMU: Creating an array proxy failed: No memory"); + retVal = RETURN_ERROR; + goto cleanup_1; + } + + retVal = proxy->Setup(proxy, name, parameterInput->parameter.arrayParameter->numDims, parameterInput->parameter.arrayParameter->dims); + if (RETURN_ERROR == retVal) { + mcx_log(LOG_ERROR, "FMU Array parameter %s: Array proxy setup failed", name); + goto cleanup_1; + } } // store the scalar values - size_t j = 0; for (j = 0; j < vals->Size(vals); j++) { - Object * v = vals->At(vals, j); - retVal = params->PushBackNamed(params, v, ((Fmu2Value*)v)->name); + Fmu2Value * v = (Fmu2Value *) vals->At(vals, j); + retVal = params->PushBackNamed(params, (Object *) v, v->name); if (RETURN_OK != retVal) { - ret = NULL; - goto fmu2_read_params_for_cleanup; + mcx_log(LOG_ERROR, "FMU: Adding element #%zu of parameter %s failed", j, name); + goto cleanup_1; + } + + vals->SetAt(vals, j, NULL); + + if (arrayParams) { + retVal = proxy->AddValue(proxy, v); + if (RETURN_ERROR == retVal) { + mcx_log(LOG_ERROR, "FMU: Adding proxy to element #%zu of parameter %s failed", j, name); + goto cleanup_1; + } + } + } + + if (arrayParams) { + retVal = arrayParams->PushBackNamed(arrayParams, (Object *) proxy, proxy->GetName(proxy)); + if (RETURN_ERROR == retVal) { + mcx_log(LOG_ERROR, "FMU: Adding proxy for %s failed", name); + goto cleanup_1; + } + } + +cleanup_1: + if (RETURN_ERROR == retVal) { + object_destroy(proxy); + if (vals) { + vals->DestroyObjects(vals); } } object_destroy(vals); + + if (RETURN_ERROR == retVal) { + goto cleanup_0; + } } else { + Fmu2Value * val = NULL; + // read the scalar value val = Fmu2ReadParamValue(parameterInput->parameter.scalarParameter, import); if (val == NULL) { mcx_log(LOG_ERROR, "FMU: Could not read parameter value of parameter %s", name); - ret = NULL; - goto fmu2_read_params_for_cleanup; + retVal = RETURN_ERROR; + goto cleanup_2; } // store the read value retVal = params->PushBackNamed(params, (Object * ) val, name); if (RETURN_OK != retVal) { - ret = NULL; - goto fmu2_read_params_for_cleanup; + goto cleanup_2; } - } -fmu2_read_params_for_cleanup: - if (name) { mcx_free(name); } - if (ret == NULL) { - if (val) { object_destroy(val); } - if (vals) { - vals->DestroyObjects(vals); - object_destroy(vals); +cleanup_2: + if (RETURN_ERROR == retVal) { + object_destroy(val); } + } - goto cleanup; +cleanup_0: + if (name) { mcx_free(name); } + if (retVal == RETURN_ERROR) { + return RETURN_ERROR; } } @@ -555,30 +607,22 @@ ObjectContainer * Fmu2ReadParams(ParametersInput * input, fmi2_import_t * import status = params->Sort(params, NaturalComp, NULL); if (RETURN_OK != status) { mcx_log(LOG_ERROR, "FMU: Unable to sort parameters"); - ret = NULL; - goto cleanup; + return RETURN_ERROR; } for (i = 0; i < n - 1; i++) { Fmu2Value * a = (Fmu2Value *) params->At(params, i); Fmu2Value * b = (Fmu2Value *) params->At(params, i + 1); - if (! strcmp(a->name, b->name)) { + if (!strcmp(a->name, b->name)) { mcx_log(LOG_ERROR, "FMU: Duplicate definition of parameter %s", a->name); - ret = NULL; - goto cleanup; + return RETURN_ERROR; } } } } -cleanup: - if (ret == NULL) { - params->DestroyObjects(params); - object_destroy(params); - } - - return ret; + return retVal; } @@ -617,7 +661,356 @@ McxStatus Fmu2UpdateTunableParamValues(ObjectContainer * tunableParams, ObjectCo return retVal; } +typedef struct ConnectedElems{ + int is_connected; + size_t num_elems; + size_t * elems; +} ConnectedElems; + +typedef struct ChannelElement { + size_t channel_idx; + size_t elem_idx; +} ChannelElement; + +static Vector * GetAllElems(ChannelElement * elems, size_t num, size_t channel_idx) { + size_t i = 0; + Vector * indices = (Vector *) object_create(Vector); + if (!indices) { + mcx_log(LOG_ERROR, "GetAllElems: Not enough memory"); + return NULL; + } + + indices->Setup(indices, sizeof(size_t), NULL, NULL, NULL); + + for (i = 0; i < num; i++) { + if (elems[i].channel_idx == channel_idx) { + if (RETURN_ERROR == indices->PushBack(indices, &elems[i].elem_idx)) { + mcx_log(LOG_ERROR, "GetAllElems: Collecting element indices failed"); + object_destroy(indices); + return NULL; + } + } + } + + return indices; +} + +McxStatus Fmu2SetDependencies(Fmu2CommonStruct * fmu2, Databus * db, Dependencies * deps, int init) { + McxStatus ret_val = RETURN_OK; + + size_t *start_index = NULL; + size_t *dependency = NULL; + char *factor_kind = NULL; + + size_t i = 0, j = 0, k = 0; + size_t num_dependencies = 0; + size_t dep_idx = 0; + + // mapping between dependency indices (from the modelDescription file) and the dependency <-> channel mapping + SizeTSizeTMap *dependencies_to_in_channels = (SizeTSizeTMap*)object_create(SizeTSizeTMap); + // mapping between unknown indices (from the modelDescription file) and the unknowns <-> channel list + SizeTSizeTMap *unknowns_to_out_channels = (SizeTSizeTMap*)object_create(SizeTSizeTMap); + + // get dependency information via the fmi library + fmi2_import_variable_list_t * init_unknowns = NULL; + + if (init) { + init_unknowns = fmi2_import_get_initial_unknowns_list(fmu2->fmiImport); + fmi2_import_get_initial_unknowns_dependencies(fmu2->fmiImport, &start_index, &dependency, &factor_kind); + } else { + init_unknowns = fmi2_import_get_outputs_list(fmu2->fmiImport); + fmi2_import_get_outputs_dependencies(fmu2->fmiImport, &start_index, &dependency, &factor_kind); + } + + size_t num_init_unknowns = fmi2_import_get_variable_list_size(init_unknowns); + + // the dependency information in is encoded via variable indices in modelDescription.xml + // our dependency matrix uses channel indices + // to align those 2 index types we use helper dictionaries which store the mapping between them + + // map each dependency index to an input channel index + ObjectContainer *in_vars = fmu2->in; + size_t num_in_vars = in_vars->Size(in_vars); + + DatabusInfo * db_info = DatabusGetInInfo(db); + size_t num_in_channels = DatabusInfoGetChannelNum(db_info); + + // list describing for each channel which channel elements are connected + ConnectedElems * in_channel_connectivity = (ConnectedElems *) mcx_calloc(num_in_channels, sizeof(ConnectedElems)); + if (!in_channel_connectivity) { + mcx_log(LOG_ERROR, "SetDependenciesFMU2: Input connectivity map allocation failed"); + ret_val = RETURN_ERROR; + goto cleanup; + } + + // List which for each dependency describes which channel and element index it corresponds to + // The index of elements in this list doesn't correspond to the dependency index from the modelDescription file + ChannelElement * dependencies_to_inputs = (ChannelElement *) mcx_calloc(DatabusGetInChannelsElemNum(db), sizeof(ChannelElement)); + if (!dependencies_to_inputs) { + mcx_log(LOG_ERROR, "SetDependenciesFMU2: Dependencies to inputs map allocation failed"); + ret_val = RETURN_ERROR; + goto cleanup; + } + + // list used to know the mapping between unknowns and channels/elements + ChannelElement * unknowns_to_outputs = (ChannelElement *) mcx_calloc(DatabusGetOutChannelsElemNum(db), sizeof(ChannelElement)); + if (!unknowns_to_outputs) { + mcx_log(LOG_ERROR, "SetDependenciesFMU2: Unknowns to outputs map allocation failed"); + ret_val = RETURN_ERROR; + goto cleanup; + } + + // List used to later find ommitted elements + ChannelElement * processed_output_elems = (ChannelElement *) mcx_calloc(DatabusGetOutChannelsElemNum(db), sizeof(ChannelElement)); + if (!processed_output_elems) { + mcx_log(LOG_ERROR, "SetDependenciesFMU2: Processed output elements allocation failed"); + ret_val = RETURN_ERROR; + goto cleanup; + } + + for (i = 0; i < num_in_vars; ++i) { + Fmu2Value *val = (Fmu2Value *)in_vars->At(in_vars, i); + Channel * ch = (Channel *) DatabusGetInChannel(db, i); + ChannelIn * in = (ChannelIn *) ch; + ChannelInfo * info = DatabusInfoGetChannel(db_info, i); + if (ch->IsConnected(ch)) { + if (ChannelTypeIsArray(info->type)) { + in_channel_connectivity[i].is_connected = TRUE; + in_channel_connectivity[i].num_elems = 0; + in_channel_connectivity[i].elems = (int *) mcx_calloc(ChannelDimensionNumElements(info->dimension), sizeof(size_t)); + if (!in_channel_connectivity[i].elems) { + mcx_log(LOG_ERROR, "SetDependenciesFMU2: Input connectivity element container allocation failed"); + ret_val = RETURN_ERROR; + goto cleanup; + } + + // if an element index appears in elems, it means that element is connected + Vector * connInfos = in->GetConnectionInfos(in); + for (j = 0; j < connInfos->Size(connInfos); j++) { + ConnectionInfo * connInfo = *(ConnectionInfo**) connInfos->At(connInfos, j); + size_t k = 0; + + for (k = 0; k < ChannelDimensionNumElements(connInfo->targetDimension); k++) { + size_t idx = ChannelDimensionGetIndex(connInfo->targetDimension, k, info->type->ty.a.dims) - info->dimension->startIdxs[0]; + in_channel_connectivity[i].elems[in_channel_connectivity[i].num_elems++] = idx; + } + } + object_destroy(connInfos); + } else { + in_channel_connectivity[i].is_connected = TRUE; + // scalar channels are treated like they have 1 element (equal to zero) + in_channel_connectivity[i].num_elems = 1; + in_channel_connectivity[i].elems = (int*)mcx_calloc(1, sizeof(size_t)); + if (!in_channel_connectivity[i].elems) { + mcx_log(LOG_ERROR, "SetDependenciesFMU2: Input connectivity element allocation failed"); + ret_val = RETURN_ERROR; + goto cleanup; + } + in_channel_connectivity[i].elems[0] = 0; + } + } + + if (val->data->type == FMU2_VALUE_SCALAR) { + fmi2_import_variable_t *var = val->data->data.scalar; + size_t idx = fmi2_import_get_variable_original_order(var) + 1; + dependencies_to_in_channels->Add(dependencies_to_in_channels, idx, dep_idx); + + dependencies_to_inputs[dep_idx].channel_idx = i; + dep_idx++; + } else if (val->data->type == FMU2_VALUE_ARRAY) { + size_t num_elems = 1; + size_t j = 0; + + for (j = 0; j < val->data->data.array.numDims; j++) { + num_elems *= val->data->data.array.dims[j]; + } + + for (j = 0; j < num_elems; j++) { + fmi2_import_variable_t * var = val->data->data.array.values[j]; + size_t idx = fmi2_import_get_variable_original_order(var) + 1; + dependencies_to_in_channels->Add(dependencies_to_in_channels, idx, dep_idx); + + dependencies_to_inputs[dep_idx].channel_idx = i; + dependencies_to_inputs[dep_idx].elem_idx = j; + dep_idx++; + } + } + } + + // element is not present in modelDescription.xml + // The dependency matrix consists of only 1 (if input is connected) + if (start_index == NULL) { + for (i = 0; i < GetDependencyNumOut(deps); ++i) { + for (j = 0; j < GetDependencyNumIn(deps); ++j) { + if (in_channel_connectivity[j].is_connected) { + ret_val = SetDependency(deps, j, i, DEP_DEPENDENT); + if (RETURN_OK != ret_val) { + goto cleanup; + } + } + } + } + + goto cleanup; + } + + // map each initial_unkown index to an output channel index + // for array channels, there might be multiple entries initial_unknown_idx -> channel_idx + ObjectContainer *out_vars = fmu2->out; + size_t num_out_vars = out_vars->Size(out_vars); + size_t unknown_idx = 0; + for (i = 0; i < num_out_vars; ++i) { + Fmu2Value *val = (Fmu2Value *)out_vars->At(out_vars, i); + + if (val->data->type == FMU2_VALUE_SCALAR) { + fmi2_import_variable_t *var = val->data->data.scalar; + size_t idx = fmi2_import_get_variable_original_order(var) + 1; + unknowns_to_out_channels->Add(unknowns_to_out_channels, idx, unknown_idx); + + unknowns_to_outputs[unknown_idx].channel_idx = i; + unknown_idx++; + } else if (val->data->type == FMU2_VALUE_ARRAY) { + size_t num_elems = 1; + size_t j = 0; + + for (j = 0; j < val->data->data.array.numDims; j++) { + num_elems *= val->data->data.array.dims[j]; + } + + for (j = 0; j < num_elems; j++) { + fmi2_import_variable_t * var = val->data->data.array.values[j]; + size_t idx = fmi2_import_get_variable_original_order(var) + 1; + unknowns_to_out_channels->Add(unknowns_to_out_channels, idx, unknown_idx); + + unknowns_to_outputs[unknown_idx].channel_idx = i; + unknowns_to_outputs[unknown_idx].elem_idx = j; + unknown_idx++; + } + } + } + + // fill up the dependency matrix + size_t processed_elems = 0; + for (i = 0; i < num_init_unknowns; ++i) { + fmi2_import_variable_t *init_unknown = fmi2_import_get_variable(init_unknowns, i); + size_t init_unknown_idx = fmi2_import_get_variable_original_order(init_unknown) + 1; + + SizeTSizeTElem * out_pair = unknowns_to_out_channels->Get(unknowns_to_out_channels, init_unknown_idx); + if (out_pair == NULL) { + continue; // in case some variables are ommitted from the input file + } + + ChannelElement * out_elem = &unknowns_to_outputs[out_pair->value]; + + processed_output_elems[processed_elems].channel_idx = out_elem->channel_idx; + processed_output_elems[processed_elems].elem_idx = out_elem->elem_idx; + processed_elems++; + + num_dependencies = start_index[i + 1] - start_index[i]; + for (j = 0; j < num_dependencies; ++j) { + dep_idx = dependency[start_index[i] + j]; + if (dep_idx == 0) { + // The element does not explicitly define a `dependencies` attribute + // In this case it depends on all inputs + for (k = 0; k < num_in_channels; ++k) { + if (in_channel_connectivity[k].is_connected) { + ret_val = SetDependency(deps, k, out_elem->channel_idx, DEP_DEPENDENT); + if (RETURN_OK != ret_val) { + goto cleanup; + } + } + } + } else { + // The element explicitly defines its dependencies + SizeTSizeTElem * in_pair = dependencies_to_in_channels->Get(dependencies_to_in_channels, dep_idx); + if (in_pair) { + ChannelElement * dep = &dependencies_to_inputs[in_pair->value]; + + ConnectedElems * elems = &in_channel_connectivity[dep->channel_idx]; + if (elems->is_connected) { + size_t k = 0; + for (k = 0; k < elems->num_elems; k++) { + if (elems->elems[k] == dep->elem_idx) { + ret_val = SetDependency(deps, dep->channel_idx, out_elem->channel_idx, DEP_DEPENDENT); + if (RETURN_OK != ret_val) { + goto cleanup; + } + break; + } + } + } + } + } + } + } + + // Initial unknowns which are ommitted from the element in + // modelDescription.xml file depend on all inputs + for (i = 0; i < num_out_vars; ++i) { + Fmu2Value * val = (Fmu2Value *) out_vars->At(out_vars, i); + + Vector * elems = GetAllElems(processed_output_elems, processed_elems, i); + + if (val->data->type == FMU2_VALUE_ARRAY) { + size_t num_elems = 1; + size_t j = 0; + + for (j = 0; j < val->data->data.array.numDims; j++) { + num_elems *= val->data->data.array.dims[j]; + } + + for (j = 0; j < num_elems; j++) { + if (!elems->Contains(elems, &j)) { + if (fmi2_import_get_initial(val->data->data.array.values[j]) != fmi2_initial_enu_exact) { + for (k = 0; k < num_in_channels; ++k) { + if (in_channel_connectivity[k].is_connected) { + ret_val = SetDependency(deps, k, i, DEP_DEPENDENT); + if (RETURN_OK != ret_val) { + goto cleanup; + } + } + } + } + } + } + } else { + if (elems->Size(elems) == 0) { + if (fmi2_import_get_initial(val->data->data.scalar) != fmi2_initial_enu_exact) { + for (k = 0; k < num_in_channels; ++k) { + if (in_channel_connectivity[k].is_connected) { + ret_val = SetDependency(deps, k, i, DEP_DEPENDENT); + if (RETURN_OK != ret_val) { + goto cleanup_1; + } + } + } + } + } + } + + object_destroy(elems); + continue; + +cleanup_1: + object_destroy(elems); + goto cleanup; + } + +cleanup: // free dynamically allocated objects + object_destroy(dependencies_to_in_channels); + object_destroy(unknowns_to_out_channels); + if (in_channel_connectivity) { mcx_free(in_channel_connectivity); } + if (dependencies_to_inputs) { mcx_free(dependencies_to_inputs); } + if (unknowns_to_outputs) { mcx_free(unknowns_to_outputs); } + if (processed_output_elems) { mcx_free(processed_output_elems); } + fmi2_import_free_variable_list(init_unknowns); + + return ret_val; +} + + +// TODO: move into fmu2value? McxStatus Fmu2SetVariable(Fmu2CommonStruct * fmu, Fmu2Value * fmuVal) { fmi2_status_t status = fmi2_status_ok; @@ -630,16 +1023,16 @@ McxStatus Fmu2SetVariable(Fmu2CommonStruct * fmu, Fmu2Value * fmuVal) { } ChannelValue * const chVal = &fmuVal->val; - ChannelType type = ChannelValueType(chVal); + ChannelType * type = ChannelValueType(chVal); - switch (type) { + switch (type->con) { case CHANNEL_DOUBLE: { fmi2_value_reference_t vr[] = {fmuVal->data->vr.scalar}; - status = fmi2_import_set_real(fmu->fmiImport, vr, 1, (const fmi2_real_t *) ChannelValueReference(chVal)); + status = fmi2_import_set_real(fmu->fmiImport, vr, 1, (const fmi2_real_t *) ChannelValueDataPointer(chVal)); - MCX_DEBUG_LOG("Set %s(%d)=%f", fmuVal->name, vr[0], *(double*)ChannelValueReference(chVal)); + MCX_DEBUG_LOG("Set %s(%d)=%f", fmuVal->name, vr[0], *(double*)ChannelValueDataPointer(chVal)); break; } @@ -647,9 +1040,9 @@ McxStatus Fmu2SetVariable(Fmu2CommonStruct * fmu, Fmu2Value * fmuVal) { { fmi2_value_reference_t vr[] = { fmuVal->data->vr.scalar }; - status = fmi2_import_set_integer(fmu->fmiImport, vr, 1, (const fmi2_integer_t *) ChannelValueReference(chVal)); + status = fmi2_import_set_integer(fmu->fmiImport, vr, 1, (const fmi2_integer_t *) ChannelValueDataPointer(chVal)); - MCX_DEBUG_LOG("Set %s(%d)=%d", fmuVal->name, vr[0], *(int*)ChannelValueReference(chVal)); + MCX_DEBUG_LOG("Set %s(%d)=%d", fmuVal->name, vr[0], *(int*)ChannelValueDataPointer(chVal)); break; } @@ -657,7 +1050,7 @@ McxStatus Fmu2SetVariable(Fmu2CommonStruct * fmu, Fmu2Value * fmuVal) { { fmi2_value_reference_t vr[] = { fmuVal->data->vr.scalar }; - status = fmi2_import_set_boolean(fmu->fmiImport, vr, 1, (const fmi2_boolean_t *) ChannelValueReference(chVal)); + status = fmi2_import_set_boolean(fmu->fmiImport, vr, 1, (const fmi2_boolean_t *) ChannelValueDataPointer(chVal)); break; } @@ -665,14 +1058,14 @@ McxStatus Fmu2SetVariable(Fmu2CommonStruct * fmu, Fmu2Value * fmuVal) { { fmi2_value_reference_t vr[] = { fmuVal->data->vr.scalar }; - status = fmi2_import_set_string(fmu->fmiImport, vr, 1, (fmi2_string_t *) ChannelValueReference(chVal)); + status = fmi2_import_set_string(fmu->fmiImport, vr, 1, (fmi2_string_t *) ChannelValueDataPointer(chVal)); break; } case CHANNEL_BINARY: case CHANNEL_BINARY_REFERENCE: { - binary_string * binary = (binary_string *) ChannelValueReference(chVal); + binary_string * binary = (binary_string *) ChannelValueDataPointer(chVal); fmi2_value_reference_t vrs [] = { fmuVal->data->vr.binary.lo , fmuVal->data->vr.binary.hi @@ -688,8 +1081,27 @@ McxStatus Fmu2SetVariable(Fmu2CommonStruct * fmu, Fmu2Value * fmuVal) { break; } + case CHANNEL_ARRAY: + { + fmi2_value_reference_t * vrs = fmuVal->data->vr.array.values; + mcx_array * a = (mcx_array *) ChannelValueDataPointer(&fmuVal->val); + + size_t num = mcx_array_num_elements(a); + void * vals = a->data; + + if (ChannelTypeEq(a->type, &ChannelTypeDouble)) { + status = fmi2_import_set_real(fmu->fmiImport, vrs, num, vals); + } else if (ChannelTypeEq(a->type, &ChannelTypeInteger)) { + status = fmi2_import_set_integer(fmu->fmiImport, vrs, num, vals); + } else { + mcx_log(LOG_ERROR, "FMU: Unsupported array variable type: %s", ChannelTypeToString(a->type)); + return RETURN_ERROR; + } + + break; + } default: - mcx_log(LOG_ERROR, "FMU: Unknown variable type"); + mcx_log(LOG_ERROR, "FMU: Unknown variable type: %s", ChannelTypeToString(type)); return RETURN_ERROR; } @@ -739,16 +1151,16 @@ McxStatus Fmu2GetVariable(Fmu2CommonStruct * fmu, Fmu2Value * fmuVal) { char * const name = fmuVal->name; ChannelValue * const chVal = &fmuVal->val; - ChannelType type = ChannelValueType(chVal); + ChannelType * type = ChannelValueType(chVal); - switch (type) { + switch (type->con) { case CHANNEL_DOUBLE: { fmi2_value_reference_t vr[] = { fmuVal->data->vr.scalar }; - status = fmi2_import_get_real(fmu->fmiImport, vr, 1, (fmi2_real_t *) ChannelValueReference(chVal)); + status = fmi2_import_get_real(fmu->fmiImport, vr, 1, (fmi2_real_t *) ChannelValueDataPointer(chVal)); - MCX_DEBUG_LOG("Get %s(%d)=%f", fmuVal->name, vr[0], *(double*)ChannelValueReference(chVal)); + MCX_DEBUG_LOG("Get %s(%d)=%f", fmuVal->name, vr[0], *(double*)ChannelValueDataPointer(chVal)); break; } @@ -756,7 +1168,7 @@ McxStatus Fmu2GetVariable(Fmu2CommonStruct * fmu, Fmu2Value * fmuVal) { { fmi2_value_reference_t vr[] = { fmuVal->data->vr.scalar }; - status = fmi2_import_get_integer(fmu->fmiImport, vr, 1, (fmi2_integer_t *) ChannelValueReference(chVal)); + status = fmi2_import_get_integer(fmu->fmiImport, vr, 1, (fmi2_integer_t *) ChannelValueDataPointer(chVal)); break; } @@ -764,7 +1176,7 @@ McxStatus Fmu2GetVariable(Fmu2CommonStruct * fmu, Fmu2Value * fmuVal) { { fmi2_value_reference_t vr[] = { fmuVal->data->vr.scalar }; - status = fmi2_import_get_boolean(fmu->fmiImport, vr, 1, (fmi2_boolean_t *) ChannelValueReference(chVal)); + status = fmi2_import_get_boolean(fmu->fmiImport, vr, 1, (fmi2_boolean_t *) ChannelValueDataPointer(chVal)); break; } @@ -775,7 +1187,9 @@ McxStatus Fmu2GetVariable(Fmu2CommonStruct * fmu, Fmu2Value * fmuVal) { char * buffer = NULL; status = fmi2_import_get_string(fmu->fmiImport, vr, 1, (fmi2_string_t *) &buffer); - ChannelValueSetFromReference(chVal, &buffer); + if (RETURN_OK != ChannelValueSetFromReference(chVal, &buffer)) { + return RETURN_ERROR; + } break; } @@ -796,7 +1210,28 @@ McxStatus Fmu2GetVariable(Fmu2CommonStruct * fmu, Fmu2Value * fmuVal) { binary.len = vs[2]; binary.data = (char *) ((((long long)vs[1] & 0xffffffff) << 32) | (vs[0] & 0xffffffff)); - ChannelValueSetFromReference(chVal, &binary); + if (RETURN_OK != ChannelValueSetFromReference(chVal, &binary)) { + return RETURN_ERROR; + } + + break; + } + case CHANNEL_ARRAY: + { + fmi2_value_reference_t * vrs = fmuVal->data->vr.array.values; + mcx_array * a = (mcx_array *) ChannelValueDataPointer(&fmuVal->val); + + size_t num = mcx_array_num_elements(a); + void * vals = a->data; + + if (ChannelTypeEq(a->type, &ChannelTypeDouble)) { + status = fmi2_import_get_real(fmu->fmiImport, vrs, num, vals); + } else if (ChannelTypeEq(a->type, &ChannelTypeInteger)) { + status = fmi2_import_get_integer(fmu->fmiImport, vrs, num, vals); + } else { + // TODO: log message + return RETURN_ERROR; + } break; } @@ -878,9 +1313,9 @@ McxStatus Fmi2RegisterLocalChannelsAtDatabus(ObjectContainer * vals, const char const char * name = val->name; fmi2_import_unit_t * unit = NULL; const char * unitName; - ChannelType type = ChannelValueType(&val->val); + ChannelType * type = ChannelValueType(&val->val); - if (CHANNEL_DOUBLE == type) { + if (ChannelTypeEq(&ChannelTypeDouble, type)) { unit = fmi2_import_get_real_variable_unit(fmi2_import_get_variable_as_real(val->data->data.scalar)); } if (unit) { @@ -895,7 +1330,7 @@ McxStatus Fmi2RegisterLocalChannelsAtDatabus(ObjectContainer * vals, const char return RETURN_ERROR; } - retVal = DatabusAddLocalChannel(db, name, buffer, unitName, ChannelValueReference(&val->val), ChannelValueType(&val->val)); + retVal = DatabusAddLocalChannel(db, name, buffer, unitName, ChannelValueDataPointer(&val->val), ChannelValueType(&val->val)); if (RETURN_OK != retVal) { mcx_log(LOG_ERROR, "%s: Adding channel %s to databus failed", compName, name); return RETURN_ERROR; @@ -919,11 +1354,11 @@ ObjectContainer * Fmu2ValueScalarListFromVarList(fmi2_import_variable_list_t * v for (i = 0; i < num; i++) { fmi2_import_variable_t * var = fmi2_import_get_variable(vars, i); char * name = (char *)fmi2_import_get_variable_name(var); - ChannelType type = Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(var)); + ChannelType * type = Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(var)); Fmu2Value * value = Fmu2ValueScalarMake(name, var, NULL, NULL); if (value) { - list->PushBack(list, (Object *) value); + list->PushBackNamed(list, (Object *) value, name); } else { list->DestroyObjects(list); object_destroy(list); @@ -952,7 +1387,7 @@ ObjectContainer * Fmu2ValueScalarListFromValVarList(ObjectContainer * vals, fmi2 for (i = 0; i < num; i++) { fmi2_import_variable_t * var = fmi2_import_get_variable(vars, i); char * name = (char *)fmi2_import_get_variable_name(var); - ChannelType type = Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(var)); + ChannelType * type = Fmi2TypeToChannelType(fmi2_import_get_variable_base_type(var)); ChannelValue * chVal = (ChannelValue *) vals->At(vals, i); Fmu2Value * value = NULL; @@ -1072,6 +1507,31 @@ void Fmu2MarkTunableParamsAsInputAsDiscrete(ObjectContainer * in) { mcx_log(LOG_DEBUG, "Setting input \"%s\" as discrete", ChannelInfoGetLogName(info)); in->SetDiscrete(in); } + } else if (val->data->type == FMU2_VALUE_ARRAY) { + size_t num_elems = 1; + size_t j = 0; + int all_tunable = TRUE; + + for (j = 0; j < val->data->data.array.numDims; j++) { + num_elems *= val->data->data.array.dims[j]; + } + + for (j = 0; j < num_elems; j++) { + fmi2_import_variable_t * var = val->data->data.array.values[j]; + fmi2_causality_enu_t causality = fmi2_import_get_causality(var); + + if (fmi2_causality_enu_input == causality) { + all_tunable = FALSE; + break; + } + } + + if (all_tunable) { + ChannelIn * in = (ChannelIn *) val->channel; + ChannelInfo * info = &((Channel *) in)->info; + mcx_log(LOG_DEBUG, "Setting input \"%s\" as discrete", ChannelInfoGetLogName(info)); + in->SetDiscrete(in); + } } } } diff --git a/src/fmu/common_fmu2.h b/src/fmu/common_fmu2.h index 0f61179..5b19e8a 100644 --- a/src/fmu/common_fmu2.h +++ b/src/fmu/common_fmu2.h @@ -19,18 +19,22 @@ #include "reader/model/components/specific_data/FmuInput.h" #include "reader/model/parameters/ParametersInput.h" +#include "core/Dependency.h" + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ -ChannelType Fmi2TypeToChannelType(fmi2_base_type_enu_t type); +ChannelType * Fmi2TypeToChannelType(fmi2_base_type_enu_t type); const char * Fmi2TypeToString(fmi2_base_type_enu_t type); struct Fmu2CommonStruct; typedef struct Fmu2CommonStruct Fmu2CommonStruct; -ObjectContainer * Fmu2ReadParams(ParametersInput * input, fmi2_import_t * import, ObjectContainer * ignore); +McxStatus Fmu2ReadParams(ObjectContainer * params, ObjectContainer * arrayParams, ParametersInput * input, fmi2_import_t * import, ObjectContainer * ignore); + +McxStatus Fmu2SetDependencies(Fmu2CommonStruct * fmu2, Databus * db, Dependencies * deps, int init); // TODO: rename all variablearrays to something better McxStatus Fmu2SetVariableArray(Fmu2CommonStruct * fmu, ObjectContainer * vals); @@ -53,6 +57,8 @@ struct Fmu2CommonStruct { ObjectContainer * params; ObjectContainer * initialValues; + ObjectContainer * arrayParams; // proxy views to array parameter elements (from params) (type: ArrayParameterProxy) + ObjectContainer * connectedIn; ObjectContainer * localValues; diff --git a/src/objects/ObjectContainer.c b/src/objects/ObjectContainer.c index 6e451a7..c7c9cdc 100644 --- a/src/objects/ObjectContainer.c +++ b/src/objects/ObjectContainer.c @@ -526,7 +526,7 @@ static ObjectContainer * ObjectContainerFilterCtx(ObjectContainer * container, f for (i = 0; i < container->size; i++) { Object * obj = container->At(container, i); if (predicate(obj, ctx)) { - filtered->PushBack(filtered, obj); + filtered->PushBackNamed(filtered, obj, container->GetElementName(container, i)); } } diff --git a/src/reader/model/components/specific_data/ConstantInput.c b/src/reader/model/components/specific_data/ConstantInput.c index c14a354..a5b5559 100644 --- a/src/reader/model/components/specific_data/ConstantInput.c +++ b/src/reader/model/components/specific_data/ConstantInput.c @@ -19,7 +19,7 @@ static void ScalarConstantValueInputDestructor(ScalarConstantValueInput * input) } static ScalarConstantValueInput * ScalarConstantValueInputCreate(ScalarConstantValueInput * input) { - input->type = CHANNEL_UNKNOWN; + input->type = &ChannelTypeUnknown; input->value.d = 0.0; return input; @@ -32,7 +32,7 @@ static void ArrayConstantValueInputDestructor(ArrayConstantValueInput * input) { } static ArrayConstantValueInput * ArrayConstantValueInputCreate(ArrayConstantValueInput * input) { - input->type = CHANNEL_UNKNOWN; + input->type = &ChannelTypeUnknown; input->numValues = 0; input->values = NULL; diff --git a/src/reader/model/components/specific_data/ConstantInput.h b/src/reader/model/components/specific_data/ConstantInput.h index 5a38b5b..6896fbd 100644 --- a/src/reader/model/components/specific_data/ConstantInput.h +++ b/src/reader/model/components/specific_data/ConstantInput.h @@ -25,7 +25,7 @@ extern const ObjectClass _ScalarConstantValueInput; typedef struct ScalarConstantValueInput { InputElement _; - ChannelType type; + ChannelType * type; ChannelValueData value; } ScalarConstantValueInput; @@ -34,7 +34,7 @@ extern const ObjectClass _ArrayConstantValueInput; typedef struct ArrayConstantValueInput { InputElement _; - ChannelType type; + ChannelType * type; size_t numValues; void * values; diff --git a/src/reader/model/parameters/ArrayParameterInput.c b/src/reader/model/parameters/ArrayParameterInput.c index 203c426..b12419a 100644 --- a/src/reader/model/parameters/ArrayParameterInput.c +++ b/src/reader/model/parameters/ArrayParameterInput.c @@ -29,7 +29,7 @@ static void ArrayParameterInputDestructor(ArrayParameterInput * input) { } if (input->values) { - if (input->type == CHANNEL_STRING) { + if (ChannelTypeEq(input->type, &ChannelTypeString)) { size_t i = 0; for (i = 0; i < input->numValues; i++) { @@ -47,7 +47,7 @@ static ArrayParameterInput * ArrayParameterInputCreate(ArrayParameterInput * inp input->numDims = 0; input->dims = NULL; - input->type = CHANNEL_UNKNOWN; + input->type = &ChannelTypeUnknown; input->numValues = 0; input->values = NULL; diff --git a/src/reader/model/parameters/ArrayParameterInput.h b/src/reader/model/parameters/ArrayParameterInput.h index 8c45675..cf0a0dc 100644 --- a/src/reader/model/parameters/ArrayParameterInput.h +++ b/src/reader/model/parameters/ArrayParameterInput.h @@ -29,7 +29,7 @@ typedef struct ArrayParameterInput { size_t numDims; ArrayParameterDimensionInput ** dims; - ChannelType type; + ChannelType * type; size_t numValues; void * values; diff --git a/src/reader/model/parameters/ScalarParameterInput.c b/src/reader/model/parameters/ScalarParameterInput.c index 6b217c6..1244cda 100644 --- a/src/reader/model/parameters/ScalarParameterInput.c +++ b/src/reader/model/parameters/ScalarParameterInput.c @@ -19,7 +19,7 @@ static void ScalarParameterInputDestructor(ScalarParameterInput * input) { if (input->unit) { mcx_free(input->unit); } - if (input->type == CHANNEL_STRING && input->value.defined && input->value.value.s) { + if (ChannelTypeEq(input->type, &ChannelTypeString) && input->value.defined && input->value.value.s) { mcx_free(input->value.value.s); } } @@ -27,7 +27,7 @@ static void ScalarParameterInputDestructor(ScalarParameterInput * input) { static ScalarParameterInput * ScalarParameterInputCreate(ScalarParameterInput * input) { input->name = NULL; - input->type = CHANNEL_UNKNOWN; + input->type = &ChannelTypeUnknown; OPTIONAL_UNSET(input->value); input->unit = NULL; diff --git a/src/reader/model/parameters/ScalarParameterInput.h b/src/reader/model/parameters/ScalarParameterInput.h index b52d1ae..5be3ac0 100644 --- a/src/reader/model/parameters/ScalarParameterInput.h +++ b/src/reader/model/parameters/ScalarParameterInput.h @@ -24,7 +24,7 @@ typedef struct ScalarParameterInput { char * name; - ChannelType type; + ChannelType * type; OPTIONAL_VALUE(ChannelValueData) value; char * unit; diff --git a/src/reader/model/ports/ScalarPortInput.c b/src/reader/model/ports/ScalarPortInput.c index 50be17d..13e309b 100644 --- a/src/reader/model/ports/ScalarPortInput.c +++ b/src/reader/model/ports/ScalarPortInput.c @@ -81,37 +81,49 @@ static McxStatus CopyFrom(ScalarPortInput * self, ScalarPortInput * src) { self->min.defined = src->min.defined; if (self->min.defined) { ChannelValueDataInit(&self->min.value, self->type); - ChannelValueDataSetFromReference(&self->min.value, self->type, &src->min.value); + if (RETURN_OK != ChannelValueDataSetFromReference(&self->min.value, self->type, &src->min.value)) { + return RETURN_ERROR; + } } self->max.defined = src->max.defined; if (self->max.defined) { ChannelValueDataInit(&self->max.value, self->type); - ChannelValueDataSetFromReference(&self->max.value, self->type, &src->max.value); + if (RETURN_OK != ChannelValueDataSetFromReference(&self->max.value, self->type, &src->max.value)) { + return RETURN_ERROR; + } } self->scale.defined = src->scale.defined; if (self->scale.defined) { ChannelValueDataInit(&self->scale.value, self->type); - ChannelValueDataSetFromReference(&self->scale.value, self->type, &src->scale.value); + if (RETURN_OK != ChannelValueDataSetFromReference(&self->scale.value, self->type, &src->scale.value)) { + return RETURN_ERROR; + } } self->offset.defined = src->offset.defined; if (self->offset.defined) { ChannelValueDataInit(&self->offset.value, self->type); - ChannelValueDataSetFromReference(&self->offset.value, self->type, &src->offset.value); + if (RETURN_OK != ChannelValueDataSetFromReference(&self->offset.value, self->type, &src->offset.value)) { + return RETURN_ERROR; + } } self->default_.defined = src->default_.defined; if (self->default_.defined) { ChannelValueDataInit(&self->default_.value, self->type); - ChannelValueDataSetFromReference(&self->default_.value, self->type, &src->default_.value); + if (RETURN_OK != ChannelValueDataSetFromReference(&self->default_.value, self->type, &src->default_.value)) { + return RETURN_ERROR; + } } self->initial.defined = src->initial.defined; if (self->initial.defined) { ChannelValueDataInit(&self->initial.value, self->type); - ChannelValueDataSetFromReference(&self->initial.value, self->type, &src->initial.value); + if (RETURN_OK != ChannelValueDataSetFromReference(&self->initial.value, self->type, &src->initial.value)) { + return RETURN_ERROR; + } } self->writeResults = src->writeResults; @@ -120,8 +132,8 @@ static McxStatus CopyFrom(ScalarPortInput * self, ScalarPortInput * src) { } -static void PrintOptionalChannelValueData(char * prefix, ChannelType type, OPTIONAL_VALUE(ChannelValueData) value) { - switch(type) { +static void PrintOptionalChannelValueData(char * prefix, ChannelType * type, OPTIONAL_VALUE(ChannelValueData) value) { + switch(type->con) { case CHANNEL_DOUBLE: if (value.defined) { mcx_log(LOG_DEBUG, "%s%f,", prefix, value.value.d); @@ -196,7 +208,7 @@ static ScalarPortInput * ScalarPortInputCreate(ScalarPortInput * input) { input->id = NULL; input->unit = NULL; - input->type = CHANNEL_UNKNOWN; + input->type = &ChannelTypeUnknown; OPTIONAL_UNSET(input->min); OPTIONAL_UNSET(input->max); diff --git a/src/reader/model/ports/ScalarPortInput.h b/src/reader/model/ports/ScalarPortInput.h index 258e38e..1361328 100644 --- a/src/reader/model/ports/ScalarPortInput.h +++ b/src/reader/model/ports/ScalarPortInput.h @@ -32,7 +32,7 @@ struct ScalarPortInput { char * id; char * unit; - ChannelType type; + ChannelType * type; OPTIONAL_VALUE(ChannelValueData) min; OPTIONAL_VALUE(ChannelValueData) max; diff --git a/src/reader/model/ports/VectorPortInput.c b/src/reader/model/ports/VectorPortInput.c index bd74ebc..a5d5ab4 100644 --- a/src/reader/model/ports/VectorPortInput.c +++ b/src/reader/model/ports/VectorPortInput.c @@ -144,20 +144,12 @@ static McxStatus CopyFrom(VectorPortInput * self, VectorPortInput * src) { self->default_ = NULL; } - if (src->writeResults) { - self->writeResults = (int *) mcx_calloc(len, sizeof(int)); - if (!self->writeResults) { - return RETURN_ERROR; - } - memcpy(self->writeResults, src->writeResults, len * sizeof(int)); - } else { - self->writeResults = NULL; - } + self->writeResults = src->writeResults; return RETURN_OK; } -static void PrintVec(char * prefix, ChannelType type, size_t len, void * value) { +static void PrintVec(char * prefix, ChannelType * type, size_t len, void * value) { char buffer[4096] = { 0 }; size_t num = 0; @@ -167,7 +159,7 @@ static void PrintVec(char * prefix, ChannelType type, size_t len, void * value) if (value) { for (i = 0; i < len; i++) { - switch(type) { + switch(type->con) { case CHANNEL_DOUBLE: num += sprintf(buffer + num, " %f", ((double*)value)[i]); break; @@ -187,6 +179,14 @@ static void PrintVec(char * prefix, ChannelType type, size_t len, void * value) mcx_log(LOG_DEBUG, "%s", buffer); } +static void PrintOptionalInt(char * prefix, OPTIONAL_VALUE(int) value) { + if (value.defined) { + mcx_log(LOG_DEBUG, "%s%d,", prefix, value.value); + } else { + mcx_log(LOG_DEBUG, "%s-,", prefix); + } +} + void VectorPortInputPrint(VectorPortInput * input) { size_t len = input->endIndex - input->startIndex + 1; @@ -205,7 +205,7 @@ void VectorPortInputPrint(VectorPortInput * input) { PrintVec(" .default: ", input->type, len, input->default_); PrintVec(" .initial: ", input->type, len, input->initial); - PrintVec(" .writeResults: ", input->type, len, input->writeResults); + PrintOptionalInt(" .writeResults: ", input->writeResults); mcx_log(LOG_DEBUG, "}"); } @@ -221,7 +221,6 @@ static void VectorPortInputDestructor(VectorPortInput * input) { if (input->offset) { mcx_free(input->offset); } if (input->default_) { mcx_free(input->default_); } if (input->initial) { mcx_free(input->initial); } - if (input->writeResults) { mcx_free(input->writeResults); } } static VectorPortInput * VectorPortInputCreate(VectorPortInput * input) { @@ -236,7 +235,7 @@ static VectorPortInput * VectorPortInputCreate(VectorPortInput * input) { input->id = NULL; input->unit = NULL; - input->type = CHANNEL_UNKNOWN; + input->type = &ChannelTypeUnknown; input->min = NULL; input->max = NULL; @@ -247,7 +246,7 @@ static VectorPortInput * VectorPortInputCreate(VectorPortInput * input) { input->default_ = NULL; input->initial = NULL; - input->writeResults = NULL; + OPTIONAL_UNSET(input->writeResults); inputElement->Clone = Clone; input->CopyFrom = CopyFrom; diff --git a/src/reader/model/ports/VectorPortInput.h b/src/reader/model/ports/VectorPortInput.h index 417767b..8dacb80 100644 --- a/src/reader/model/ports/VectorPortInput.h +++ b/src/reader/model/ports/VectorPortInput.h @@ -35,7 +35,7 @@ struct VectorPortInput { char * id; char * unit; - ChannelType type; + ChannelType * type; void * min; void * max; @@ -46,7 +46,7 @@ struct VectorPortInput { void * default_; void * initial; - int * writeResults; + OPTIONAL_VALUE(int) writeResults; fVectorPortInputCopyFrom CopyFrom; }; diff --git a/src/reader/ssp/Parameters.c b/src/reader/ssp/Parameters.c index 03963d7..83b24b2 100644 --- a/src/reader/ssp/Parameters.c +++ b/src/reader/ssp/Parameters.c @@ -25,6 +25,7 @@ extern "C" { #endif /* __cplusplus */ + typedef struct SSDParameterValue SSDParameterValue; typedef McxStatus(*fSSDParameterValueSetup)(SSDParameterValue * paramValue, xmlNodePtr node, ObjectContainer * units); @@ -614,12 +615,12 @@ static McxStatus CollectParameterValues(xmlNodePtr paramBindingsNode, ObjectCont return RETURN_OK; } -static MapStringInt _typeMappingScalar[] = { - {"Real", CHANNEL_DOUBLE}, - {"Integer", CHANNEL_INTEGER}, - {"Boolean", CHANNEL_BOOL}, - {"String", CHANNEL_STRING}, - {NULL, 0}, +static MapStringChannelType _typeMappingScalar[] = { + {"Real", &ChannelTypeDouble}, + {"Integer", &ChannelTypeInteger}, + {"Boolean", &ChannelTypeBool}, + {"String", &ChannelTypeString}, + {NULL, &ChannelTypeUnknown}, }; static ScalarParameterInput * SSDReadScalarParameter(SSDParameter * parameter) { @@ -676,7 +677,7 @@ static ScalarParameterInput * SSDReadScalarParameter(SSDParameter * parameter) { } } - if (input->type == CHANNEL_UNKNOWN) { + if (!ChannelTypeIsValid(input->type)) { retVal = xml_error_unsupported_node(connectorTypeNode); goto cleanup; } @@ -705,11 +706,11 @@ static ScalarParameterInput * SSDReadScalarParameter(SSDParameter * parameter) { return input; } -static MapStringInt _typeMappingArray[] = { - {"Real", CHANNEL_DOUBLE}, - {"Integer", CHANNEL_INTEGER}, - {"Boolean", CHANNEL_BOOL}, - {NULL, 0}, +static MapStringChannelType _typeMappingArray[] = { + {"Real", &ChannelTypeDouble}, + {"Integer", &ChannelTypeInteger}, + {"Boolean", &ChannelTypeBool}, + {NULL, &ChannelTypeUnknown}, }; static ArrayParameterDimensionInput * SSDReadArrayParameterDimension(xmlNodePtr node) { @@ -746,10 +747,10 @@ static ArrayParameterDimensionInput * SSDReadArrayParameterDimension(xmlNodePtr return input; } -static McxStatus AllocateMemory(SSDParameter * parameter, ChannelType type, size_t numValues, void ** dest) { +static McxStatus AllocateMemory(SSDParameter * parameter, ChannelType * type, size_t numValues, void ** dest) { size_t size = 0; - switch (type) { + switch (type->con) { case CHANNEL_DOUBLE: size = sizeof(double); break; @@ -825,7 +826,7 @@ static McxStatus SSDReadDimensionlessArrayParameter(SSDParameter * parameter, Ar continue; } - switch (input->type) { + switch (input->type->con) { case CHANNEL_DOUBLE: retVal = xml_attr_double(paramTypeNode, "value", (double*)input->values + paramValue->idx1, SSD_MANDATORY); break; @@ -911,7 +912,7 @@ static McxStatus SSDReadDimensionArrayParameter(SSDParameter * parameter, ArrayP index = (paramValue->idx1 - input->dims[0]->start) * (input->dims[1]->end - input->dims[1]->start + 1) + paramValue->idx2 - input->dims[1]->start; } - switch (input->type) { + switch (input->type->con) { case CHANNEL_DOUBLE: retVal = xml_attr_double(paramTypeNode, "value", (double*)input->values + index, SSD_MANDATORY); break; @@ -1025,7 +1026,7 @@ static ArrayParameterInput * SSDReadArrayParameter(SSDParameter * parameter) { } } - if (input->type == CHANNEL_UNKNOWN) { + if (!ChannelTypeIsValid(input->type)) { retVal = xml_error_unsupported_node(connectorTypeNode); goto cleanup; } diff --git a/src/reader/ssp/Ports.c b/src/reader/ssp/Ports.c index 8c03a63..93c4b08 100644 --- a/src/reader/ssp/Ports.c +++ b/src/reader/ssp/Ports.c @@ -17,6 +17,7 @@ #include "reader/model/ports/ScalarPortInput.h" #include "reader/model/ports/VectorPortInput.h" +#include "core/channels/ChannelValue.h" #include "util/string.h" @@ -24,20 +25,20 @@ extern "C" { #endif /* __cplusplus */ -static MapStringInt _typeMapping[] = { - {"Real", CHANNEL_DOUBLE}, - {"Integer", CHANNEL_INTEGER}, - {"Boolean", CHANNEL_BOOL}, - {"String", CHANNEL_STRING}, - {"Binary", CHANNEL_BINARY}, - {NULL, 0}, +static MapStringChannelType _typeMapping[] = { + {"Real", &ChannelTypeDouble}, + {"Integer", &ChannelTypeInteger}, + {"Boolean", &ChannelTypeBool}, + {"String", &ChannelTypeString}, + {"Binary", &ChannelTypeBinary}, + {NULL, &ChannelTypeUnknown}, }; -static MapStringInt _vectorTypeMapping[] = { - {"RealVector", CHANNEL_DOUBLE}, - {"IntegerVector", CHANNEL_INTEGER}, - {"BooleanVector", CHANNEL_BOOL}, - {NULL, 0}, +static MapStringChannelType _vectorTypeMapping[] = { + {"RealVector", &ChannelTypeDouble}, + {"IntegerVector", &ChannelTypeInteger}, + {"BooleanVector", &ChannelTypeBool}, + {NULL, &ChannelTypeUnknown}, }; @@ -140,7 +141,7 @@ VectorPortInput * SSDReadComponentVectorPort(xmlNodePtr connectorNode, const cha break; } } - if (vectorPortInput->type == CHANNEL_UNKNOWN) { + if (!ChannelTypeIsValid(vectorPortInput->type)) { retVal = xml_error_unsupported_node(typeNode); goto cleanup; } @@ -172,7 +173,7 @@ VectorPortInput * SSDReadComponentVectorPort(xmlNodePtr connectorNode, const cha xmlNodePtr vectorNode = NULL; size_t num = 0; - switch (vectorPortInput->type) { + switch (vectorPortInput->type->con) { case CHANNEL_DOUBLE: vectorNode = xml_child(portNode, "RealVector"); break; @@ -238,18 +239,9 @@ VectorPortInput * SSDReadComponentVectorPort(xmlNodePtr connectorNode, const cha goto cleanup; } - { - size_t n = 0; - retVal = xml_attr_bool_vec(vectorNode, "writeResults", &n, &vectorPortInput->writeResults, SSD_OPTIONAL); - if (RETURN_ERROR == retVal) { - goto cleanup; - } else if (RETURN_OK == retVal) { - if (n != num) { - mcx_log(LOG_ERROR, "xml_attr_vec_len: Expected length (%d) does not match actual length (%d)", num, n); - retVal = RETURN_ERROR; - goto cleanup; - } - } + retVal = xml_opt_attr_bool(vectorNode, "writeResults", &vectorPortInput->writeResults); + if (RETURN_ERROR == retVal) { + goto cleanup; } } } @@ -354,7 +346,7 @@ ScalarPortInput * SSDReadComponentScalarPort(xmlNodePtr connectorNode, const cha break; } } - if (scalarPortInput->type == CHANNEL_UNKNOWN) { + if (!ChannelTypeIsValid(scalarPortInput->type)) { retVal = xml_error_unsupported_node(typeNode); goto cleanup; } @@ -386,7 +378,7 @@ ScalarPortInput * SSDReadComponentScalarPort(xmlNodePtr connectorNode, const cha xmlNodePtr scalarNode = NULL; size_t num = 0; - switch (scalarPortInput->type) { + switch (scalarPortInput->type->con) { case CHANNEL_DOUBLE: scalarNode = xml_child(portNode, "Real"); break; diff --git a/src/reader/ssp/SpecificData.c b/src/reader/ssp/SpecificData.c index 1af4a25..b151743 100644 --- a/src/reader/ssp/SpecificData.c +++ b/src/reader/ssp/SpecificData.c @@ -17,6 +17,7 @@ #include "reader/model/components/specific_data/VectorIntegratorInput.h" #include "reader/model/components/specific_data/ConstantInput.h" +#include "core/channels/ChannelValue.h" #ifdef __cplusplus extern "C" { @@ -150,12 +151,12 @@ McxStatus SSDReadVectorIntegratorData(xmlNodePtr componentNode, xmlNodePtr speci return RETURN_OK; } -static MapStringInt _scalarConstantTypeMapping[] = { - {"Real", CHANNEL_DOUBLE}, - {"Integer", CHANNEL_INTEGER}, - {"String", CHANNEL_STRING}, - {"Boolean", CHANNEL_BOOL}, - {NULL, CHANNEL_UNKNOWN} +static MapStringChannelType _scalarConstantTypeMapping[] = { + {"Real", &ChannelTypeDouble}, + {"Integer", &ChannelTypeInteger}, + {"String", &ChannelTypeString}, + {"Boolean", &ChannelTypeBool}, + {NULL, &ChannelTypeUnknown} }; static ScalarConstantValueInput * SSDReadScalarConstantValue(xmlNodePtr node) { @@ -181,7 +182,7 @@ static ScalarConstantValueInput * SSDReadScalarConstantValue(xmlNodePtr node) { } } - if (input->type == CHANNEL_UNKNOWN) { + if (!ChannelTypeIsValid(input->type)) { retVal = xml_error_unsupported_node(node); goto cleanup; } @@ -201,10 +202,10 @@ static ScalarConstantValueInput * SSDReadScalarConstantValue(xmlNodePtr node) { return input; } -static MapStringInt _vectorConstantTypeMapping[] = { - {"RealVector", CHANNEL_DOUBLE}, - {"IntegerVector", CHANNEL_INTEGER}, - {NULL, CHANNEL_UNKNOWN} +static MapStringChannelType _vectorConstantTypeMapping[] = { + {"RealVector", &ChannelTypeDouble}, + {"IntegerVector", &ChannelTypeInteger}, + {NULL, &ChannelTypeUnknown} }; static ArrayConstantValueInput * SSDReadArrayConstantValue(xmlNodePtr node) { @@ -230,13 +231,13 @@ static ArrayConstantValueInput * SSDReadArrayConstantValue(xmlNodePtr node) { } } - if (input->type == CHANNEL_UNKNOWN) { + if (!ChannelTypeIsValid(input->type)) { retVal = xml_error_unsupported_node(node); goto cleanup; } } - switch (input->type) { + switch (input->type->con) { case CHANNEL_DOUBLE: retVal = xml_attr_double_vec(node, "value", &input->numValues, (double**)&input->values, SSD_MANDATORY); break; diff --git a/src/reader/ssp/Util.c b/src/reader/ssp/Util.c index 2c8e76c..7b883da 100644 --- a/src/reader/ssp/Util.c +++ b/src/reader/ssp/Util.c @@ -746,8 +746,8 @@ McxStatus xml_attr_enum_weak_ptr(xmlNodePtr node, return ret_status; } -McxStatus xml_attr_channel_value_data(xmlNodePtr node, const char * attribute_name, ChannelType type, ChannelValueData * dest, SSDParamMode mode) { - switch (type) { +McxStatus xml_attr_channel_value_data(xmlNodePtr node, const char * attribute_name, ChannelType * type, ChannelValueData * dest, SSDParamMode mode) { + switch (type->con) { case CHANNEL_DOUBLE: return xml_attr_double(node, attribute_name, &dest->d, mode); case CHANNEL_INTEGER: @@ -1160,7 +1160,7 @@ McxStatus xml_opt_attr_enum(xmlNodePtr node, const char * attribute_name, MapStr return RETURN_OK; } -McxStatus xml_opt_attr_channel_value_data(xmlNodePtr node, const char * attribute_name, ChannelType type, OPTIONAL_VALUE(ChannelValueData) * dest) { +McxStatus xml_opt_attr_channel_value_data(xmlNodePtr node, const char * attribute_name, ChannelType * type, OPTIONAL_VALUE(ChannelValueData) * dest) { McxStatus ret_val = xml_attr_channel_value_data(node, attribute_name, type, &dest->value, SSD_OPTIONAL); if (ret_val == RETURN_ERROR) { @@ -1172,7 +1172,7 @@ McxStatus xml_opt_attr_channel_value_data(xmlNodePtr node, const char * attribut return RETURN_OK; } -McxStatus xml_attr_vec_len(xmlNodePtr node, const char * attribute_name, ChannelType type, size_t expectedLen, void ** dest, SSDParamMode mode) { +McxStatus xml_attr_vec_len(xmlNodePtr node, const char * attribute_name, ChannelType * type, size_t expectedLen, void ** dest, SSDParamMode mode) { McxStatus ret_status = RETURN_OK; int ret_val = 0; xmlChar * buffer = NULL; @@ -1209,7 +1209,7 @@ McxStatus xml_attr_vec_len(xmlNodePtr node, const char * attribute_name, Channel goto cleanup; } - switch (type) { + switch (type->con) { case CHANNEL_DOUBLE: ret_status = xml_attr_double_vec(node, attribute_name, &len, (double **) &values, mode); break; diff --git a/src/reader/ssp/Util.h b/src/reader/ssp/Util.h index 524fe31..8c1af9f 100644 --- a/src/reader/ssp/Util.h +++ b/src/reader/ssp/Util.h @@ -69,20 +69,20 @@ McxStatus xml_attr_string(xmlNodePtr node, const char * attribute_name, char ** McxStatus xml_attr_path(xmlNodePtr node, const char * attribute_name, char ** dest, SSDParamMode mode); McxStatus xml_attr_enum(xmlNodePtr node, const char * attribute_name, MapStringInt * mapping, int * dest, SSDParamMode mode); McxStatus xml_attr_enum_weak_ptr(xmlNodePtr node, const char * attribute_name, void * map[], size_t entry_size, void ** value, SSDParamMode mode); -McxStatus xml_attr_channel_value_data(xmlNodePtr node, const char * attribute_name, ChannelType type, ChannelValueData * dest, SSDParamMode mode); +McxStatus xml_attr_channel_value_data(xmlNodePtr node, const char * attribute_name, ChannelType * type, ChannelValueData * dest, SSDParamMode mode); McxStatus xml_opt_attr_double(xmlNodePtr node, const char * attribute_name, OPTIONAL_VALUE(double) * dest); McxStatus xml_opt_attr_int(xmlNodePtr node, const char * attribute_name, OPTIONAL_VALUE(int) * dest); McxStatus xml_opt_attr_size_t(xmlNodePtr node, const char * attribute_name, OPTIONAL_VALUE(size_t) * dest); McxStatus xml_opt_attr_bool(xmlNodePtr node, const char * attribute_name, OPTIONAL_VALUE(int) * dest); McxStatus xml_opt_attr_enum(xmlNodePtr node, const char * attribute_name, MapStringInt * mapping, OPTIONAL_VALUE(int) * dest); -McxStatus xml_opt_attr_channel_value_data(xmlNodePtr node, const char * attribute_name, ChannelType type, OPTIONAL_VALUE(ChannelValueData) * dest); +McxStatus xml_opt_attr_channel_value_data(xmlNodePtr node, const char * attribute_name, ChannelType * type, OPTIONAL_VALUE(ChannelValueData) * dest); McxStatus xml_attr_double_vec(xmlNodePtr node, const char * attribute_name, size_t * len, double ** dest, SSDParamMode mode); McxStatus xml_attr_bool_vec(xmlNodePtr node, const char * attribute_name, size_t * len, int ** dest, SSDParamMode mode); McxStatus xml_attr_int_vec(xmlNodePtr node, const char * attribute_name, size_t * len, int ** dest, SSDParamMode mode); -McxStatus xml_attr_vec_len(xmlNodePtr node, const char * attribute_name, ChannelType type, size_t expectedLen, void ** dest, SSDParamMode mode); +McxStatus xml_attr_vec_len(xmlNodePtr node, const char * attribute_name, ChannelType * type, size_t expectedLen, void ** dest, SSDParamMode mode); /*****************************************************************************/ /* Errors */ diff --git a/src/steptypes/StepType.c b/src/steptypes/StepType.c index d675c97..0a1a39d 100644 --- a/src/steptypes/StepType.c +++ b/src/steptypes/StepType.c @@ -26,6 +26,39 @@ extern "C" { // ---------------------------------------------------------------------- // Step Type: Common +void StepTypeSynchronizationSetup(StepTypeSynchronization * stepTypes, Component * comp, double syncStepSize) { + double syncToCoupl = 0.0; + double couplToSync = 0.0; + + if (!comp->HasOwnTime(comp)) { + return; + } + + if (comp->GetTimeStep(comp) <= 0.0) { + return; + } + + // If the coupling and synchronization step sizes are multiples of one another + // it is enough to use half the size of the smaller step for absolute time comparisons. + syncToCoupl = syncStepSize / comp->GetTimeStep(comp); + couplToSync = comp->GetTimeStep(comp) / syncStepSize; + + if (double_eq(syncToCoupl, round(syncToCoupl))) { + stepTypes->stepSizesAreMultiples = TRUE; + stepTypes->eps = comp->GetTimeStep(comp) / 2.0; + } + + if (double_eq(couplToSync, round(couplToSync))) { + stepTypes->stepSizesAreMultiples = TRUE; + stepTypes->eps = syncStepSize / 2.0; + } +} + +void StepTypeSynchronizationInit(StepTypeSynchronization * stepTypes) { + stepTypes->stepSizesAreMultiples = FALSE; + stepTypes->eps = -1.0; +} + McxStatus ComponentDoCommunicationStep(Component * comp, size_t group, StepTypeParams * params) { McxStatus retVal = RETURN_OK; double time = params->time; @@ -49,7 +82,9 @@ McxStatus ComponentDoCommunicationStep(Component * comp, size_t group, StepTypeP while ( comp->GetFinishState(comp) != COMP_IS_FINISHED && - double_lt(comp->GetTime(comp), stepEndTime) + comp->syncHints.stepSizesAreMultiples ? + double_cmp_eps_abs(comp->GetTime(comp), stepEndTime, comp->syncHints.eps) == CMP_LT : + double_lt(comp->GetTime(comp), stepEndTime) ) { if (comp->HasOwnTime(comp)) { interval.startTime = comp->GetTime(comp); diff --git a/src/steptypes/StepType.h b/src/steptypes/StepType.h index b5eac5d..b0676d0 100644 --- a/src/steptypes/StepType.h +++ b/src/steptypes/StepType.h @@ -24,6 +24,18 @@ typedef struct SubModel SubModel; typedef struct CompAndGroup CompAndGroup; +typedef struct StepTypeSynchronization { + // flag whether the coupling and synchronization step size are multiples of one another + int stepSizesAreMultiples; + + // epsilon used for time comparisons + double eps; +} StepTypeSynchronization; + +void StepTypeSynchronizationSetup(StepTypeSynchronization * stepTypes, Component * comp, double syncStepSize); +void StepTypeSynchronizationInit(StepTypeSynchronization * stepTypes); + + typedef enum StepTypeType { STEP_TYPE_UNDEFINED = -1, /* "parallel_singlethreaded" */ STEP_TYPE_SEQUENTIAL = 1, /* "sequential" */ diff --git a/src/steptypes/StepTypeParallelMT.c b/src/steptypes/StepTypeParallelMT.c index 23e5bdd..ee43557 100644 --- a/src/steptypes/StepTypeParallelMT.c +++ b/src/steptypes/StepTypeParallelMT.c @@ -79,6 +79,8 @@ static void DoStepThreadArgSetup(DoStepThreadArg * arg, Component * comp, size_t arg->comp = comp; arg->group = group; arg->params = params; + + StepTypeSynchronizationSetup(&comp->syncHints, comp, params->timeStepSize); } static void DoStepThreadArgSetCounter(DoStepThreadArg * arg, DoStepThreadCounter * counter) { @@ -103,11 +105,13 @@ static DoStepThreadArg * DoStepThreadArgCreate(DoStepThreadArg * arg) { arg->SetCounter = DoStepThreadArgSetCounter; arg->StartThread = DoStepThreadArgStartThread; + arg->reachedSyncTime = 0.0; arg->comp = NULL; arg->group = 0; arg->params = NULL; arg->status = RETURN_OK; arg->finished = FALSE; + status = mcx_event_create(&arg->startDoStepEvent); if (status) { mcx_log(LOG_ERROR, "Simulation: Failed to create DoStep start event"); diff --git a/src/steptypes/StepTypeParallelMT.h b/src/steptypes/StepTypeParallelMT.h index deb3906..625f834 100644 --- a/src/steptypes/StepTypeParallelMT.h +++ b/src/steptypes/StepTypeParallelMT.h @@ -71,6 +71,9 @@ typedef struct DoStepThreadArg { // last status value McxStatus status; + // last reached synchronization point + double reachedSyncTime; + // flag if thread should stop int finished; diff --git a/src/storage/ChannelStorage.c b/src/storage/ChannelStorage.c index 7cf8f14..65ca204 100644 --- a/src/storage/ChannelStorage.c +++ b/src/storage/ChannelStorage.c @@ -83,7 +83,7 @@ static McxStatus ChannelStorageSetup(ChannelStorage * channelStore, int fullStor goto cleanup; } - retVal = ChannelInfoSetup(&timeInfo, "Time", "", GetTimeUnitString(), CHANNEL_DOUBLE, ""); + retVal = ChannelInfoSetup(&timeInfo, "Time", "Time", "", GetTimeUnitString(), &ChannelTypeDouble, ""); if (RETURN_ERROR == retVal) { mcx_log(LOG_ERROR, "Results: Setup port storage: Could not set up time port data"); goto cleanup; @@ -153,10 +153,12 @@ static McxStatus ChannelStorageSetValueFromReferenceAt(ChannelStorage * channelS if (row >= channelStore->numValues) { ChannelInfo * info = &channel->info; - ChannelValueInit(&channelStore->values[row * colNum + col], info->type); + ChannelValueInit(&channelStore->values[row * colNum + col], ChannelTypeClone(info->type)); } - ChannelValueSetFromReference(&channelStore->values[row * colNum + col], reference); + if (RETURN_OK != ChannelValueSetFromReference(&channelStore->values[row * colNum + col], reference)) { + return RETURN_ERROR; + } return RETURN_OK; } @@ -304,15 +306,12 @@ static void ChannelStorageDestructor(ChannelStorage * channelStore) { if (channels) { size_t i = 0; - if (channels->Size(channels) > 0) { - Channel * channel = (Channel *) channels->At(channels, 0); - ChannelInfo * timeInfo = &channel->info; - object_destroy(timeInfo); - } + for (i = 0; i < channels->Size(channels); i++) { Channel * channel = (Channel *) channels->At(channels, i); object_destroy(channel); } + object_destroy(channels); } } @@ -349,6 +348,32 @@ static ChannelStorage * ChannelStorageCreate(ChannelStorage * channelStore) { return channelStore; } +char ** ExpandedChannelNames(const char * name, size_t start, size_t end) { + char ** names = NULL; + size_t i = 0; + + names = (char **) mcx_malloc(sizeof(char *) * (end - start + 1 + 1)); + if (!names) { + return NULL; + } + + for (i = start; i <= end; i++) { + names[i-start] = CreateIndexedName(name, i); + } + names[i-start] = NULL; + + return names; +} + +void FreeExpandedChannelNames(char ** names) { + size_t i = 0; + while (names[i]) { + mcx_free(names[i]); + ++i; + } + mcx_free(names); +} + OBJECT_CLASS(ChannelStorage, Object); #ifdef __cplusplus diff --git a/src/storage/ChannelStorage.h b/src/storage/ChannelStorage.h index 481700a..3ed9e08 100644 --- a/src/storage/ChannelStorage.h +++ b/src/storage/ChannelStorage.h @@ -61,6 +61,9 @@ typedef struct ChannelStorage { size_t storeCallNum; } ChannelStorage; +char ** ExpandedChannelNames(const char * name, size_t start, size_t end); +void FreeExpandedChannelNames(char ** names); + #ifdef __cplusplus } /* closing brace for extern "C" */ #endif /* __cplusplus */ diff --git a/src/storage/ResultsStorage.c b/src/storage/ResultsStorage.c index 2b9858e..2af07bd 100644 --- a/src/storage/ResultsStorage.c +++ b/src/storage/ResultsStorage.c @@ -161,6 +161,25 @@ static McxStatus StorageStoreModelLocal(ResultsStorage * storage, SubModel * sub return RETURN_OK; } +static McxStatus StorageStoreModelRTFactor(ResultsStorage * storage, SubModel * subModel, double time, StoreLevel level) { + size_t i = 0; + + McxStatus retVal = RETURN_OK; + + // Get values from Components + for (i = 0; i < storage->numComponents; i++) { + ComponentStorage * compStore = storage->componentStorage[i]; + if (subModel->IsElement(subModel, compStore->comp)) { + retVal = compStore->StoreChannels(compStore, CHANNEL_STORE_RTFACTOR, time, level); + if (RETURN_OK != retVal) { + return RETURN_ERROR; + } + } + } + + return RETURN_OK; +} + static McxStatus StorageFinishModel(ResultsStorage * storage, SubModel * subModel) { size_t i = 0; @@ -525,6 +544,7 @@ static ResultsStorage * ResultsStorageCreate(ResultsStorage * storage) { storage->StoreModel = StorageStoreModel; storage->StoreModelOut = StorageStoreModelOut; storage->StoreModelLocal = StorageStoreModelLocal; + storage->StoreModelRTFactor = StorageStoreModelRTFactor; storage->FinishModel = StorageFinishModel; storage->Finished = StorageFinished; diff --git a/src/storage/ResultsStorage.h b/src/storage/ResultsStorage.h index 47ff582..4665128 100644 --- a/src/storage/ResultsStorage.h +++ b/src/storage/ResultsStorage.h @@ -93,6 +93,7 @@ struct ResultsStorage { fResultsStorageStoreModel StoreModel; fResultsStorageStoreModel StoreModelOut; fResultsStorageStoreModel StoreModelLocal; + fResultsStorageStoreModel StoreModelRTFactor; fResultsStorageFinishModel FinishModel; fResultsStorageRegisterComponent RegisterComponent; diff --git a/src/storage/StorageBackendCsv.c b/src/storage/StorageBackendCsv.c index 61eaf26..67e216b 100644 --- a/src/storage/StorageBackendCsv.c +++ b/src/storage/StorageBackendCsv.c @@ -16,6 +16,8 @@ #include "storage/StorageBackendCsv.h" #include "storage/StorageBackendText_impl.h" #include "storage/PPD.h" +#include "storage/ChannelStorage.h" +#include "core/connections/ConnectionInfoFactory.h" #include "util/string.h" #include "util/os.h" @@ -185,19 +187,21 @@ static char * QuoteString(const char * _str) { static McxStatus SetupComponentFilesCsv(StorageBackend * backend) { StorageBackendText * textBackend = (StorageBackendText *) backend; ResultsStorage * storage = backend->storage; - char * buffer = NULL; size_t compIdx = 0; + size_t numCompsOld = textBackend->numComponents; - if (textBackend->numComponents > 0) { - mcx_log(LOG_ERROR, "Results: Re-setting up backend"); + MCX_DEBUG_LOG("Setting up component CSV files (%zu -> %zu)", numCompsOld, storage->numComponents); + textBackend->numComponents = storage->numComponents; + textBackend->comps = (TextComponent *) mcx_realloc(textBackend->comps, textBackend->numComponents * sizeof(TextComponent)); + if (textBackend->numComponents && !textBackend->comps) { + mcx_log(LOG_ERROR, "SetupComponentFilesCsv: TextComponents: Not enough memory"); return RETURN_ERROR; } - /* calloc is used for implicit initialization of textBackend->comps */ - textBackend->numComponents = storage->numComponents; - textBackend->comps = (TextComponent *) mcx_calloc(textBackend->numComponents, sizeof(TextComponent)); + // initialize new TextComponents to 0 + memset(textBackend->comps + numCompsOld, 0, (textBackend->numComponents - numCompsOld) * sizeof(TextComponent)); - for (compIdx = 0; compIdx < textBackend->numComponents; compIdx++) { + for (compIdx = numCompsOld; compIdx < textBackend->numComponents; compIdx++) { ComponentStorage * compStore = storage->componentStorage[compIdx]; Component * comp = (Component *) compStore->comp; TextComponent * textComponent = &(textBackend->comps[compIdx]); @@ -216,6 +220,7 @@ static McxStatus SetupComponentFilesCsv(StorageBackend * backend) { ChannelStorage * chStore = compStore->channels[j]; size_t chNum = chStore->GetChannelNum(chStore); size_t chIdx = 0; + char * buffer = NULL; const size_t nameLen = strlen(localName) + strlen(ChannelStoreSuffix[j]) + 6; /* do not create a file if channel store is not enabled */ @@ -252,35 +257,83 @@ static McxStatus SetupComponentFilesCsv(StorageBackend * backend) { for (chIdx = 0; chIdx < chNum; chIdx++) { ChannelInfo * info = chStore->GetChannelInfo(chStore, chIdx); const char * channelName = ChannelInfoGetName(info); - char * quotedChannelName = QuoteString(channelName); - const char * sep = textBackend->separator; - - if (chIdx == 0) { - sep = ""; - } - if(quotedChannelName){ - mcx_os_fprintf(textFile->fp, "%s\"%s\"", sep, quotedChannelName); - mcx_free(quotedChannelName); + ChannelType * type = info->type; + + if (ChannelTypeIsArray(type)) { + if (type->ty.a.numDims > 1) { + // multi-dim not yet supported + continue; + } + ChannelDimension * dimension = info->dimension; + size_t array_start = dimension->startIdxs[0]; + size_t array_end = dimension->endIdxs[0]; + + size_t array_idx = 0; + + char ** names = ExpandedChannelNames(channelName, array_start, array_end); + const char * sep = textBackend->separator; + while (names[array_idx]) { + char * quotedChannelName = QuoteString(names[array_idx]); + + if(quotedChannelName){ + mcx_os_fprintf(textFile->fp, "%s\"%s\"", sep, quotedChannelName); + mcx_free(quotedChannelName); + } else { + mcx_os_fprintf(textFile->fp, "%s", sep); + } + ++array_idx; + } + FreeExpandedChannelNames(names); } else { - mcx_os_fprintf(textFile->fp, "%s", sep); + char * quotedChannelName = QuoteString(channelName); + const char * sep = textBackend->separator; + + if (chIdx == 0) { + sep = ""; + } + if(quotedChannelName){ + mcx_os_fprintf(textFile->fp, "%s\"%s\"", sep, quotedChannelName); + mcx_free(quotedChannelName); + } else { + mcx_os_fprintf(textFile->fp, "%s", sep); + } } - } mcx_os_fprintf(textFile->fp, "\n"); for (chIdx = 0; chIdx < chNum; chIdx++) { ChannelInfo * info = chStore->GetChannelInfo(chStore, chIdx); + ChannelType * type = info->type; const char * channelUnit = info->unitString; - const char * sep = textBackend->separator; - if (chIdx == 0) { - sep = ""; + + if (ChannelTypeIsArray(type)) { + if (type->ty.a.numDims > 1) { + // multi-dim not yet supported + continue; + } + ChannelDimension * dimension = info->dimension; + size_t array_start = dimension->startIdxs[0]; + size_t array_end = dimension->endIdxs[0]; + + size_t array_idx = 0; + + const char * sep = textBackend->separator; + for (array_idx = array_start; array_idx <= array_end; array_idx++) { + mcx_os_fprintf(textFile->fp, "%s%s", sep, channelUnit); + } + } else { + const char * sep = textBackend->separator; + if (chIdx == 0) { + sep = ""; + } + mcx_os_fprintf(textFile->fp, "%s%s", sep, channelUnit); } - mcx_os_fprintf(textFile->fp, "%s%s", sep, channelUnit); } mcx_os_fprintf(textFile->fp, "\n"); } mcx_free(localName); } + return RETURN_OK; } diff --git a/src/storage/StorageBackendText.c b/src/storage/StorageBackendText.c index 56c38b0..b14f4c6 100644 --- a/src/storage/StorageBackendText.c +++ b/src/storage/StorageBackendText.c @@ -215,7 +215,10 @@ static McxStatus WriteRow(FILE * file, ChannelValue * values, size_t numChannels if (channel == 0) { // leave out separator at the beginning sep = ""; } - switch (ChannelValueType(&val)) { + + // TODO: This should not mention CHANNEL_* anymore + + switch (ChannelValueType(&val)->con) { case CHANNEL_DOUBLE: case CHANNEL_INTEGER: case CHANNEL_BOOL: @@ -245,6 +248,12 @@ static McxStatus WriteRow(FILE * file, ChannelValue * values, size_t numChannels } break; } + case CHANNEL_ARRAY: { + char * str = ChannelValueToString(&val); + mcx_os_fprintf(file, "%s%s", sep, str); + mcx_free(str); + break; + } default: mcx_os_fprintf(file, "%s\"\"", sep); break; diff --git a/src/util/common/compare.c b/src/util/common/compare.c index ebc5a50..ea21285 100644 --- a/src/util/common/compare.c +++ b/src/util/common/compare.c @@ -60,6 +60,12 @@ int double_almost_equal(double a, double b, double eps) { } } +static int double_almost_equal_abs(double a, double b, double eps) { + double diff = (a - b); + diff = (diff < 0) ? -diff : diff; + return diff < eps; +} + cmp double_cmp_eps(double a, double b, double eps) { if (double_almost_equal(a, b, eps)) { return CMP_EQ; @@ -73,6 +79,19 @@ cmp double_cmp_eps(double a, double b, double eps) { } } +cmp double_cmp_eps_abs(double a, double b, double eps) { + if (double_almost_equal_abs(a, b, eps)) { + return CMP_EQ; + } else if (a < b) { + return CMP_LT; + } else if (a > b) { + return CMP_GT; + } else { + // error + return CMP_IN; + } +} + int double_eq(double a, double b) { return (double_cmp(a,b) == CMP_EQ); } @@ -95,6 +114,10 @@ int double_geq(double a, double b) { || double_cmp(a,b) == CMP_GT); } +int double_leq_eps_abs(double a, double b, double eps) { + return (double_cmp_eps_abs(a, b, eps) == CMP_EQ || double_cmp_eps_abs(a, b, eps) == CMP_LT); +} + #ifdef __cplusplus } /* closing brace for extern "C" */ #endif /* __cplusplus */ \ No newline at end of file diff --git a/src/util/common/stdlib.c b/src/util/common/stdlib.c index cfa25b8..3109219 100644 --- a/src/util/common/stdlib.c +++ b/src/util/common/stdlib.c @@ -20,6 +20,17 @@ extern "C" { #endif /* __cplusplus */ +void * mcx_copy(void * object, size_t size) { + void * copy = mcx_malloc(size); + if (!copy) { + return NULL; + } + + memcpy(copy, object, size); + + return copy; +} + size_t mcx_filter(void * dst, void * base, size_t nmemb, size_t size, int (*pred)(const void *, void *), void * arg) { size_t i; size_t j = 0; diff --git a/src/util/compare.h b/src/util/compare.h index 1a81b67..ae3e084 100644 --- a/src/util/compare.h +++ b/src/util/compare.h @@ -46,6 +46,14 @@ cmp double_cmp(double a, double b); */ cmp double_cmp_eps(double a, double b, double eps); +/** + * Returns CMP_LT, CMP_EQ or CMP_GT if a < b, a == b, a > b within eps + * and CMP_IN if a and b are not comparable. + * + * The comparison is done in an absolute manner. + */ +cmp double_cmp_eps_abs(double a, double b, double eps); + /** * Returns if a == b within an epsilon */ @@ -71,6 +79,11 @@ int double_leq(double a, double b); */ int double_geq(double a, double b); +/** + * Returns if a <= b modulo eps + */ +int double_leq_eps_abs(double a, double b, double eps); + #ifdef __cplusplus } /* closing brace for extern "C" */ diff --git a/src/util/stdlib.h b/src/util/stdlib.h index 52332c2..2dd3470 100644 --- a/src/util/stdlib.h +++ b/src/util/stdlib.h @@ -18,6 +18,8 @@ extern "C" { #endif /* __cplusplus */ +void * mcx_copy(void * object, size_t size); + /** * Sorts the given array in-place using quicksort. *