/******************************************************************************** * Copyright (c) 2025-2026 ZF Friedrichshafen AG * * This program and the accompanying materials are made available under the * terms of the Apache License Version 2.0 which is available at * https://www.apache.org/licenses/LICENSE-2.0 * * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ /** * @file model.cpp * @date 2025-04-16 09:03:47 * This file defines the data link object between CAN and the V-API devices. * This file was generated by the DBC utility from: * datalink_demo_example.dbc * DBC file version: 1.0.0.1 */ #include #include #include #include #include #include "signal_identifier.h" #include #include // we need to activate the counter steering service through the complex service #include "countersteering.h" sdv::core::CSignal g_signalSpeed; sdv::core::CSignal g_signalSteeringWheel; sdv::core::CSignal g_signalRearAngle; sdv::core::CSignal g_signalIsActiveCounter; // in case the simulation timer should be used sdv::core::ITimerSimulationStep* g_pTimerSimulationStep; std::unique_ptr g_appcontrol; bool InitializeAppControl(const std::string& resource, const std::string& configFileName) { auto bResult = g_appcontrol->AddModuleSearchDir( resource ); bResult &= g_appcontrol->Startup(""); g_appcontrol->SetConfigMode(); bResult &= g_appcontrol->AddConfigSearchDir( resource ); if (!configFileName.empty()) { bResult &= g_appcontrol->LoadConfig(configFileName.c_str()) == sdv::core::EConfigProcessResult::successful; } return bResult; } sdv::core::EConfigProcessResult RegisterAllSignals() { std::string msg = "register all signals: "; sdv::core::CDispatchService dispatch; g_signalSpeed = dispatch.RegisterRxSignal("CAN_Input.Speed"); g_signalSteeringWheel = dispatch.RegisterRxSignal("CAN_Input.SteeringWheel"); g_signalRearAngle = dispatch.RegisterTxSignal("CAN_Output.RearAngle",0); g_signalIsActiveCounter = dispatch.RegisterTxSignal("CAN_Output.IsActiveCounter",0); if (g_signalSpeed && g_signalSteeringWheel && g_signalRearAngle && g_signalIsActiveCounter) { return sdv::core::EConfigProcessResult::successful; } return sdv::core::EConfigProcessResult::failed; } bool ResetAllSignals() { sdv::core::CDispatchService dispatch; if (g_signalSpeed) { g_signalSpeed.Reset(); } if (g_signalSteeringWheel) { g_signalSteeringWheel.Reset(); } if (g_signalRearAngle) { g_signalRearAngle.Reset(); } if (g_signalIsActiveCounter) { g_signalIsActiveCounter.Reset(); } SDV_LOG_INFO("Reset signals"); return true; } bool CreateCoreServiceTomlFile(const std::string& resources) { std::ofstream tomlFile("sdv_core_reloc.toml"); if (tomlFile.is_open()) { tomlFile << "# Location of the SDV binaries and configuration files\ndirectory = \""; tomlFile << resources; tomlFile << "\"\n"; tomlFile.close(); return true; } return false; } bool OpenAPILoad(const std::string& resources) { bool success = CreateCoreServiceTomlFile(resources); g_appcontrol = std::make_unique (); // // TODO: Dispatch service must be loaded first, adjust the correct toml file // success &= InitializeAppControl(resources, "data_dispatch_config_file.toml"); if (!success) { SDV_LOG_ERROR("Failed InitializeAppControl"); return false; } success &= RegisterAllSignals() == sdv::core::EConfigProcessResult::successful; if (!success) { SDV_LOG_ERROR("Signals could not be registered"); } // // // TODO: Load all configurations files // // // // Get the simulation task timer service if the simulation timer should be used success &= g_appcontrol->LoadConfig("simulation_task_timer_config_file.toml") == sdv::core::EConfigProcessResult::successful; g_pTimerSimulationStep = sdv::core::GetObject("SimulationTaskTimerService"); if (!g_pTimerSimulationStep) { SDV_LOG_WARNING("Simulation timer step not available, use normal task timer "); success &= g_appcontrol->LoadConfig("fmu_task_timer_example.toml") == sdv::core::EConfigProcessResult::successful; } success &= g_appcontrol->LoadConfig("fmu_vehicle_devices_basic_services.toml") == sdv::core::EConfigProcessResult::successful; success &= g_appcontrol->LoadConfig("fmu_complex_service.toml") == sdv::core::EConfigProcessResult::successful; g_appcontrol->SetRunningMode(); // we need to activate the counter steering service through the complex service auto pCounterSteeringSvc = sdv::core::GetObject("Counter Steering Example Service").GetInterface(); if (pCounterSteeringSvc) { pCounterSteeringSvc->ActivateService(true); } return success; } void OpenAPIShutdown() { ResetAllSignals(); } #ifdef __cplusplus extern "C" { #endif #include // for DBL_EPSILON #include // for fabs() #include "config.h" #include "model.h" Status cleanup(ModelInstance*) { SDV_LOG_INFO("Shutting down..."); OpenAPIShutdown(); return OK; } bool setStartValues(ModelInstance* comp) { std::string path(comp->resourceLocation); std::string resourcePath = path.substr(8); std::replace(resourcePath.begin(), resourcePath.end(), '\\', '/'); if (!OpenAPILoad(resourcePath)) { logError(comp, "Could not initialze Core Instance: %s", resourcePath.c_str()); comp->terminateSimulation = true; return false; } // TODO: move this to initialize()? comp->nextEventTime = 0; comp->nextEventTimeDefined = true; return true; } //void reset(ModelInstance* comp) //{ // OpenAPIShutdown(); // OpenAPILoad((std::string(comp->resourceLocation) + "repository_service.sdv").c_str()); //} Status calculateValues(ModelInstance* comp) { UNUSED(comp); // nothing to do return OK; } Status getFloat64(ModelInstance* comp, [[maybe_unused]] ValueReference vr, [[maybe_unused]] double values[], [[maybe_unused]] size_t nValues, [[maybe_unused]] size_t* index) { ASSERT_NVALUES(1); switch (vr) { case vr_Speed: values[(*index)++] = M(Speed); break; case vr_SteeringWheel: values[(*index)++] = M(SteeringWheel); break; case vr_RearAngle: values[(*index)++] = M(RearAngle); break; default: logError(comp, "Get Float64 is not allowed for value reference u.", vr); return Error; } return OK; } Status getInt32(ModelInstance* comp, [[maybe_unused]] ValueReference vr, [[maybe_unused]] int32_t values[], [[maybe_unused]] size_t nValues, [[maybe_unused]] size_t* index) { ASSERT_NVALUES(1); switch (vr) { case vr_IsActiveCounter: values[(*index)++] = M(IsActiveCounter); break; default: logError(comp, "Get Int32 is not allowed for value reference u.", vr); return Error; } return OK; } Status setFloat64(ModelInstance* comp, [[maybe_unused]] ValueReference vr, [[maybe_unused]] const double values[], [[maybe_unused]] size_t nValues, [[maybe_unused]] size_t* index) { ASSERT_NVALUES(1); switch (vr) { case vr_Speed: M(Speed) = values[(*index)++]; break; case vr_SteeringWheel: M(SteeringWheel) = values[(*index)++]; break; case vr_RearAngle: M(RearAngle) = values[(*index)++]; break; default: logError(comp, "Set Float64 is not allowed for value reference u.", vr); return Error; } return OK; } Status setInt32(ModelInstance* comp, [[maybe_unused]] ValueReference vr, [[maybe_unused]] const int32_t values[], [[maybe_unused]] size_t nValues, [[maybe_unused]] size_t* index) { ASSERT_NVALUES(1); switch (vr) { case vr_IsActiveCounter: M(IsActiveCounter) = values[(*index)++]; break; default: logError(comp, "Set Int32 is not allowed for value reference u.", vr); return Error; } return OK; } void eventUpdate(ModelInstance* comp) { if (g_pTimerSimulationStep) // in case the simulation timer was used, maybe the step size has to be adjusted { g_pTimerSimulationStep->SimulationStep(1000); } g_signalSpeed.Write( M(Speed)); g_signalSteeringWheel.Write( M(SteeringWheel)); M(RearAngle) = g_signalRearAngle.Read().get(); M(IsActiveCounter) = g_signalIsActiveCounter.Read().get(); double epsilon = (1.0 + fabs(comp->time)) * DBL_EPSILON; if (comp->nextEventTimeDefined && comp->time + epsilon >= comp->nextEventTime) { comp->nextEventTime += FIXED_SOLVER_STEP; } comp->valuesOfContinuousStatesChanged = false; comp->nominalsOfContinuousStatesChanged = false; comp->terminateSimulation = false; comp->nextEventTimeDefined = true; } #ifdef __cplusplus } // end of extern "C" #endif