diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index 85f84ec..8b30052 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -3,6 +3,7 @@ add_compile_options(-Wall -Wextra -Werror=switch) add_compile_options(-Wno-unused-function) ev_add_cpp_module(CbChargeSOMDriver) +ev_add_cpp_module(CbCPXDriver) ev_add_cpp_module(CbTarragonDIs) ev_add_cpp_module(CbTarragonDriver) ev_add_cpp_module(CbTarragonPlugLock) diff --git a/modules/CbCPXDriver/CMakeLists.txt b/modules/CbCPXDriver/CMakeLists.txt new file mode 100644 index 0000000..ee2ee1e --- /dev/null +++ b/modules/CbCPXDriver/CMakeLists.txt @@ -0,0 +1,53 @@ +# +# AUTO GENERATED - MARKED REGIONS WILL BE KEPT +# template version 3 +# + +# module setup: +# - ${MODULE_NAME}: module name +ev_setup_cpp_module() + +# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1 +# insert your custom targets and additional config variables here + +find_package(PalSigslot REQUIRED) + +add_subdirectory("cpx") + +set(MODULE_DESCRIPTION "chargebyte's CPX EVerest module") +set(MODULE_VERSION ${PROJECT_VERSION}) + +# make CMake project variables usable in source code +configure_file(configuration.h.in configuration.h @ONLY) + +# create a VERSION file +configure_file(VERSION.in VERSION @ONLY) + +target_link_libraries(${MODULE_NAME} + PRIVATE + Pal::Sigslot + nlohmann_json::nlohmann_json + cpx + utils::cp +) +target_include_directories(${MODULE_NAME} + PRIVATE + ${CPX_INCLUDE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/cpx +) +# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1 + +target_sources(${MODULE_NAME} + PRIVATE + "evse_board_support/evse_board_supportImpl.cpp" + "temperatures/cb_temperaturesImpl.cpp" +) + +# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1 +# insert other things like install cmds etc here + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/VERSION" + DESTINATION "${EVEREST_MODULE_INSTALL_PREFIX}/${MODULE_NAME}" +) +# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1 diff --git a/modules/CbCPXDriver/CbCPXDriver.cpp b/modules/CbCPXDriver/CbCPXDriver.cpp new file mode 100644 index 0000000..d8e8820 --- /dev/null +++ b/modules/CbCPXDriver/CbCPXDriver.cpp @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest +#include "CbCPXDriver.hpp" +#include "configuration.h" + +using namespace std::chrono_literals; + +namespace module { + +void CbCPXDriver::init() { + types::evse_board_support::Connector_type connector_type = + types::evse_board_support::string_to_connector_type(this->config.connector_type); + const bool is_pluggable = connector_type == types::evse_board_support::Connector_type::IEC62196Type2Socket; + + // instantiate CPX controller + this->controller = + std::make_unique(this->config.device_id, this->config.can_interface, this->config.can_bitrate); + this->controller->init(is_pluggable); + + // initialize the interfaces now + invoke_init(*this->p_evse_board_support); + invoke_init(*this->p_temperatures); + + EVLOG_info << MODULE_DESCRIPTION << " (version: " << MODULE_VERSION << ")"; +} + +void CbCPXDriver::ready() { + invoke_ready(*this->p_evse_board_support); + invoke_ready(*this->p_temperatures); +} + +} // namespace module diff --git a/modules/CbCPXDriver/CbCPXDriver.hpp b/modules/CbCPXDriver/CbCPXDriver.hpp new file mode 100644 index 0000000..98974af --- /dev/null +++ b/modules/CbCPXDriver/CbCPXDriver.hpp @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest +#ifndef CB_CPXDRIVER_HPP +#define CB_CPXDRIVER_HPP + +// +// AUTO GENERATED - MARKED REGIONS WILL BE KEPT +// template version 2 +// + +#include "ld-ev.hpp" + +// headers for provided interface implementations +#include +#include + +// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1 +// insert your custom include headers here + +#include "cpx/CbCPX.hpp" +// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1 + +namespace module { + +struct Conf { + std::string connector_type; + double min_current_A; + double max_current_A; + std::string can_interface; + int can_bitrate; + std::string pt1000_1_identification; + std::string pt1000_2_identification; + std::string pt1000_3_identification; + std::string pt1000_4_identification; + int device_id; +}; + +class CbCPXDriver : public Everest::ModuleBase { +public: + CbCPXDriver() = delete; + CbCPXDriver(const ModuleInfo& info, std::unique_ptr p_evse_board_support, + std::unique_ptr p_temperatures, Conf& config) : + ModuleBase(info), + p_evse_board_support(std::move(p_evse_board_support)), + p_temperatures(std::move(p_temperatures)), + config(config) {}; + + const std::unique_ptr p_evse_board_support; + const std::unique_ptr p_temperatures; + const Conf& config; + + // ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1 + // insert your public definitions here + + /// @brief Helper to signal thread termination wish + std::atomic_bool termination_requested {false}; + + /// @brief CPX CAN Interface + std::unique_ptr controller; + // ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1 + +protected: + // ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1 + // insert your protected definitions here + // ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1 + +private: + friend class LdEverest; + void init(); + void ready(); + + // ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1 + // insert your private definitions here + // ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1 +}; + +// ev@087e516b-124c-48df-94fb-109508c7cda9:v1 +// insert other definitions here +// ev@087e516b-124c-48df-94fb-109508c7cda9:v1 + +} // namespace module + +#endif // CB_CPXDRIVER_HPP diff --git a/modules/CbCPXDriver/VERSION.in b/modules/CbCPXDriver/VERSION.in new file mode 100644 index 0000000..b28e33a --- /dev/null +++ b/modules/CbCPXDriver/VERSION.in @@ -0,0 +1,2 @@ +@MODULE_DESCRIPTION@ +Version: @MODULE_VERSION@ diff --git a/modules/CbCPXDriver/configuration.h.in b/modules/CbCPXDriver/configuration.h.in new file mode 100644 index 0000000..3caed4e --- /dev/null +++ b/modules/CbCPXDriver/configuration.h.in @@ -0,0 +1,5 @@ +#pragma once + +#define MODULE_NAME "@MODULE_NAME@" +#define MODULE_DESCRIPTION "@MODULE_DESCRIPTION@" +#define MODULE_VERSION "@MODULE_VERSION@" diff --git a/modules/CbCPXDriver/cpx/CMakeLists.txt b/modules/CbCPXDriver/cpx/CMakeLists.txt new file mode 100644 index 0000000..21dfd33 --- /dev/null +++ b/modules/CbCPXDriver/cpx/CMakeLists.txt @@ -0,0 +1,34 @@ +set(CPX_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" PARENT_SCOPE) + +add_library(cpx STATIC) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(LIBSOCKETCAN REQUIRED libsocketcan) + +target_sources(cpx + PRIVATE + "CbCPX.cpp" + "can_interface/can.c" +) + +get_target_property(GENERATED_INCLUDE_DIR generate_cpp_files EVEREST_GENERATED_INCLUDE_DIR) + +target_include_directories(cpx + PRIVATE + ${GENERATED_INCLUDE_DIR} + ${CMAKE_SOURCE_DIR}/../everest-framework/include + ${CMAKE_SOURCE_DIR}/build/_deps/nlohmann_json_schema_validator-src/src + ${LIBSOCKETCAN_INCLUDE_DIRS} +) + +target_include_directories(cpx PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + +add_dependencies(cpx generate_cpp_files) + +target_link_libraries(cpx + PRIVATE + Pal::Sigslot + nlohmann_json::nlohmann_json + everest::log + ${LIBSOCKETCAN_LIBRARIES} +) diff --git a/modules/CbCPXDriver/cpx/CbCPX.cpp b/modules/CbCPXDriver/cpx/CbCPX.cpp new file mode 100644 index 0000000..740913e --- /dev/null +++ b/modules/CbCPXDriver/cpx/CbCPX.cpp @@ -0,0 +1,1140 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright chargebyte GmbH and Contributors to EVerest +#include "CbCPX.hpp" +#include + +using namespace std::chrono_literals; + +CbCPX::CbCPX(int device_id, const std::string& can_interface, int can_bitrate) { + // init EVerest-config parameters + this->config.device_id = device_id; + this->config.can_interface = can_interface; + this->config.can_bitrate = can_bitrate; + + // we have to convert can_interface of type std::string to const char * + const char* can_interface_cstr = this->config.can_interface.c_str(); + + // open a CAN BCM RX socket, bound to the desired interface + this->can_bcm_rx_fd = socket(PF_CAN, SOCK_DGRAM, CAN_BCM); + if (this->can_bcm_rx_fd == -1) + throw std::system_error(errno, std::generic_category(), "socket(PF_CAN, CAN_BCM) failed"); + + strncpy(this->ifr.ifr_name, can_interface_cstr, sizeof(this->ifr.ifr_name)); + if (ioctl(this->can_bcm_rx_fd, SIOCGIFINDEX, &this->ifr)) + throw std::system_error(errno, std::generic_category(), + "Couldn't determine interface number of " + this->config.can_interface); + + this->addr.can_family = AF_CAN; + this->addr.can_ifindex = this->ifr.ifr_ifindex; + + if (connect(this->can_bcm_rx_fd, reinterpret_cast(&this->addr), sizeof(this->addr))) + throw std::system_error(errno, std::generic_category(), + "Couldn't connect Rx CAN BCM socket on " + this->config.can_interface); + + // open a CAN BCM RX socket, bound to the desired interface + this->can_bcm_tx_fd = socket(PF_CAN, SOCK_DGRAM, CAN_BCM); + if (this->can_bcm_tx_fd == -1) + throw std::system_error(errno, std::generic_category(), "socket(PF_CAN, CAN_BCM) failed"); + + strncpy(this->ifr.ifr_name, can_interface_cstr, sizeof(this->ifr.ifr_name)); + if (ioctl(this->can_bcm_tx_fd, SIOCGIFINDEX, &this->ifr)) + throw std::system_error(errno, std::generic_category(), + "Couldn't determine interface number of " + this->config.can_interface); + + if (connect(this->can_bcm_tx_fd, reinterpret_cast(&this->addr), sizeof(this->addr))) + throw std::system_error(errno, std::generic_category(), + "Couldn't connect Tx CAN BCM socket on " + this->config.can_interface); + + // open a CAN RAW socket, bound to the desired interface + this->can_raw_fd = socket(PF_CAN, SOCK_RAW, CAN_RAW); + if (this->can_raw_fd == -1) + throw std::system_error(errno, std::generic_category(), "socket(PF_CAN, CAN_RAW) failed"); + + // set interface name + std::strncpy(this->ifr.ifr_name, can_interface_cstr, IFNAMSIZ - 1); + if (ioctl(this->can_raw_fd, SIOCGIFINDEX, &this->ifr) < 0) { + throw std::runtime_error("Failed to get interface index on " + this->config.can_interface); + } + + // set CAN filters for RAW socket + struct can_filter filters[2]; + filters[0].can_id = this->get_can_id(this->config.device_id, CAN_FIRMWARE_VERSION_FRAME_ID); + filters[0].can_mask = CAN_EFF_FLAG | CAN_EFF_MASK; + filters[1].can_id = this->get_can_id(this->config.device_id, CAN_GIT_HASH_FRAME_ID); + filters[1].can_mask = CAN_EFF_FLAG | CAN_EFF_MASK; + + if (setsockopt(this->can_raw_fd, SOL_CAN_RAW, CAN_RAW_FILTER, &filters, sizeof(filters)) < 0) { + throw std::system_error(errno, std::generic_category(), "setsockopt(CAN_RAW_FILTER) failed"); + } + + // bind socket + if (bind(this->can_raw_fd, reinterpret_cast(&this->addr), sizeof(this->addr))) + throw std::system_error(errno, std::generic_category(), "Couldn't bind CAN RAW socket!"); + + // setup BCM rx messages + this->can_bcm_rx_init(); + + // launch notify thread + this->notify_thread = std::thread(&CbCPX::notify_worker, this); + + // launch CAN BCM rx thread + this->can_bcm_rx_thread = std::thread(&CbCPX::can_bcm_rx_worker, this); + + // launch CAN RAW rx thread + this->can_raw_rx_thread = std::thread(&CbCPX::can_raw_rx_worker, this); +} + +CbCPX::~CbCPX() { + this->terminate(); +} + +void CbCPX::init(bool is_pluggable) { + // remember this setting + this->is_pluggable = is_pluggable; +} + +void CbCPX::terminate() { + this->evse_enabled = false; + this->tx_cc_enabled = false; + this->rx_bcm_enabled = false; + this->rx_raw_enabled = false; + + this->termination_requested = true; + this->notify_worker_cv.notify_all(); + + this->duty_cycle_check_termination_requested = true; + this->duty_cycle_check_cv.notify_all(); + + this->timeout_watchdog_termination_requested = true; + this->timeout_watchdog_cv.notify_all(); + + // close CAN BCM RX socket + if (this->can_bcm_rx_fd >= 0) { + close(this->can_bcm_rx_fd); + this->can_bcm_rx_fd = -1; + } + + // close CAN BCM TX socket + if (this->can_bcm_tx_fd >= 0) { + close(this->can_bcm_tx_fd); + this->can_bcm_tx_fd = -1; + } + + // close CAN RAW socket + if (this->can_raw_fd >= 0) { + close(this->can_raw_fd); + this->can_raw_fd = -1; + } + + if (this->notify_thread.joinable()) { + this->notify_thread.join(); + } + + if (this->can_bcm_rx_thread.joinable()) { + this->can_bcm_rx_thread.join(); + } + + if (this->can_raw_rx_thread.joinable()) { + this->can_raw_rx_thread.join(); + } + + if (this->duty_cycle_check_thread.joinable()) { + this->duty_cycle_check_thread.join(); + } + + if (this->timeout_watchdog_thread.joinable()) { + this->timeout_watchdog_thread.join(); + } +} + +void CbCPX::ensure_can_bitrate() { + struct can_bittiming bt {}; + if (can_get_bittiming(this->config.can_interface.c_str(), &bt) != 0) { + throw std::runtime_error("can_get_bittiming failed: " + std::string(std::strerror(errno))); + } + + if (static_cast(bt.bitrate) == this->config.can_bitrate) { + return; // already configured + } + + if (can_do_stop(this->config.can_interface.c_str()) != 0) { + throw std::runtime_error("can_do_stop failed: " + std::string(std::strerror(errno))); + } + if (can_set_bitrate(this->config.can_interface.c_str(), this->config.can_bitrate) != 0) { + throw std::runtime_error("can_set_bitrate failed: " + std::string(std::strerror(errno))); + } + if (can_do_start(this->config.can_interface.c_str()) != 0) { + throw std::runtime_error("can_do_start failed: " + std::string(std::strerror(errno))); + } +} + +void CbCPX::get_firmware_and_git_hash() { + // save current firmware information + std::unique_lock fv_lock(this->fv_mutex); + std::unique_lock gh_lock(this->gh_mutex); + + const struct can_firmware_version_t current_firmware_version_info = this->com_data.firmware_version; + // git hash info is not yet being used + // const struct can_git_hash_t current_git_hash_info = this->com_data.git_hash; + + struct can_frame frame; + memset(&frame, 0, sizeof(frame)); + uint8_t payload[8]; + + this->print_can_id_info = true; + + // frame information valid for firmware version and git hash + frame.can_id = this->get_can_id(this->config.device_id, CAN_INQUIRY_PACKET_FRAME_ID); + frame.can_dlc = static_cast(CAN_INQUIRY_PACKET_LENGTH); + + // query firmware version + this->com_data.inquiry_packet.packet_id = CAN_INQUIRY_PACKET_PACKET_ID_FIRMWARE_VERSION_CHOICE; + can_inquiry_packet_pack(payload, &this->com_data.inquiry_packet, CAN_INQUIRY_PACKET_LENGTH); + memcpy(frame.data, payload, CAN_INQUIRY_PACKET_LENGTH); + + fv_lock.unlock(); + + ssize_t bytes_sent = write(this->can_raw_fd, &frame, static_cast(sizeof(frame))); + if (bytes_sent != static_cast(sizeof(frame))) { + throw std::runtime_error("Could not request firmware version!"); + } + + // we should have a response within 1 second + std::this_thread::sleep_for(1s); + + // query git hash + this->com_data.inquiry_packet.packet_id = CAN_INQUIRY_PACKET_PACKET_ID_GIT_HASH_CHOICE; + can_inquiry_packet_pack(payload, &this->com_data.inquiry_packet, CAN_INQUIRY_PACKET_LENGTH); + memcpy(frame.data, payload, CAN_INQUIRY_PACKET_LENGTH); + + gh_lock.unlock(); + + bytes_sent = write(this->can_raw_fd, &frame, static_cast(sizeof(frame))); + if (bytes_sent != static_cast(sizeof(frame))) { + throw std::runtime_error("Could not request git hash!"); + } + + // we should have a response within 1 second + std::this_thread::sleep_for(1s); + + this->print_can_id_info = false; + + // pack firmware info + fv_lock.lock(); + gh_lock.lock(); + + // check if firmware info was really received on RAW thread + if (current_firmware_version_info.major_version == this->com_data.firmware_version.major_version && + current_firmware_version_info.minor_version == this->com_data.firmware_version.minor_version && + current_firmware_version_info.build_version == this->com_data.firmware_version.build_version && + current_firmware_version_info.platform_type == this->com_data.firmware_version.platform_type && + current_firmware_version_info.application_type == this->com_data.firmware_version.application_type + // ignore Git Hash for now because CPX is not sending it in current firmware implementation + // (current_git_hash_info.hash_signal == com_data.git_hash.hash_signal) + ) { + EVLOG_error << "Could not determine CPX firmware information."; + } + + this->fw_info = + std::to_string(can_firmware_version_major_version_decode(this->com_data.firmware_version.major_version)) + + std::to_string(can_firmware_version_minor_version_decode(this->com_data.firmware_version.minor_version)) + + std::to_string(can_firmware_version_build_version_decode(this->com_data.firmware_version.build_version)) + + " (g" + std::to_string(can_git_hash_hash_signal_decode(this->com_data.git_hash.hash_signal)) + ", " + + std::to_string(can_firmware_version_platform_type_decode(this->com_data.firmware_version.platform_type)) + + std::to_string(can_firmware_version_application_type_decode(this->com_data.firmware_version.application_type)); + + fv_lock.unlock(); + gh_lock.unlock(); + + // signal new value + if (!this->has_cpx_connected_once) { + EVLOG_info << "Firmware Version and Git Hash received!"; + } + this->on_fw_info(this->fw_info); +} + +void CbCPX::enable() { + // start sending of periodic Charge Control frames + this->tx_cc_enabled = true; + + // start reacting on received CAN BCM messages + this->rx_bcm_enabled = true; + + // start reacting on received CAN RAW messages + this->rx_raw_enabled = true; +} + +canid_t CbCPX::get_can_id(int cpx_id, int message_id) { + canid_t base_id; + + if (cpx_id <= 0) { + base_id = static_cast(message_id); + } else { + // move cpx_id into upper part and message_id into lower part + base_id = ((static_cast(cpx_id) & 0x1FFFFF) << 8) | // upper bits + (static_cast(message_id) & 0xFF); // lower bits + } + + // make sure CAN-ID fits into reserved 29 bits + base_id &= CAN_EFF_MASK; + + // set extended-frame-flag + return base_id | CAN_EFF_FLAG; +} + +void CbCPX::can_bcm_rx_init() { + // create msg-buffer + alignas(std::max(alignof(bcm_msg_head), alignof(can_frame))) std::array buf {}; + auto* hdr = reinterpret_cast(buf.data()); + auto* frame = reinterpret_cast(buf.data() + sizeof(bcm_msg_head)); + + // write CAN-header information for Charge State and PT1000 State + hdr->opcode = RX_SETUP; + hdr->flags = RX_FILTER_ID | SETTIMER; + hdr->nframes = 1; + hdr->ival1.tv_sec = 0; + hdr->ival1.tv_usec = 120000; + hdr->ival2.tv_sec = 0; + hdr->ival2.tv_usec = 120000; + + // write CAN-header for Charge State + hdr->can_id = this->get_can_id(this->config.device_id, CAN_CHARGE_STATE1_FRAME_ID); + + // write CAN-frame for Charge State + frame->can_id = this->get_can_id(this->config.device_id, CAN_CHARGE_STATE1_FRAME_ID); + frame->can_dlc = static_cast(CAN_CHARGE_STATE1_LENGTH); + memset(frame->data, 0x00, CAN_CHARGE_STATE1_LENGTH); + + // init Charge State data for further use + memset(this->charge_state_data.data(), 0x00, CAN_CHARGE_STATE1_LENGTH); + this->charge_state_id = this->get_can_id(this->config.device_id, CAN_CHARGE_STATE1_FRAME_ID); + + // init CAN-RX-Filter for Charge State by writing to CAN-Socket + if (write(this->can_bcm_rx_fd, buf.data(), buf.size()) < 0) { + throw std::system_error(errno, std::generic_category(), "Charge State RX setup failed!"); + } + + // write CAN-header for PT1000 State + hdr->can_id = this->get_can_id(this->config.device_id, CAN_PT1000_STATE_FRAME_ID); + + // write CAN-frame for PT1000 State + frame->can_id = this->get_can_id(this->config.device_id, CAN_PT1000_STATE_FRAME_ID); + frame->can_dlc = static_cast(CAN_PT1000_STATE_LENGTH); + memset(frame->data, 0x00, CAN_PT1000_STATE_LENGTH); + + // init PT1000 State data for further use + memset(this->pt1000_state_data.data(), 0x00, CAN_PT1000_STATE_LENGTH); + this->pt1000_state_id = get_can_id(this->config.device_id, CAN_PT1000_STATE_FRAME_ID); + + // init CAN-RX-Filter for PT1000 State by writing to CAN-Socket + if (write(this->can_bcm_rx_fd, buf.data(), buf.size()) < 0) { + throw std::system_error(errno, std::generic_category(), "PT1000 State RX setup failed!"); + } +} + +void CbCPX::charge_control_update() { + // create buffer for delete msg + alignas(std::max(alignof(bcm_msg_head), alignof(can_frame))) std::array buf_delete {}; + auto* hdr_delete = reinterpret_cast(buf_delete.data()); + + // create buffer for setup msg + alignas(std::max(alignof(bcm_msg_head), alignof(can_frame))) std::array buf_setup {}; + auto* hdr_setup = reinterpret_cast(buf_setup.data()); + auto* frame_setup = reinterpret_cast(buf_setup.data() + sizeof(bcm_msg_head)); + + // only update if sending of Charge Control msg is enabled + if (this->tx_cc_enabled) { + // only delete previous msg if initialization of Charge Control was done + if (this->charge_control_initialized) { + memset(buf_delete.data(), 0, buf_delete.size()); + hdr_delete->opcode = TX_DELETE; + hdr_delete->can_id = this->get_can_id(this->config.device_id, CAN_CHARGE_CONTROL1_FRAME_ID); + hdr_delete->nframes = 0; + + // now delete auto sending of Charge Control msg by sending delete msg to socket + if (write(this->can_bcm_tx_fd, buf_delete.data(), buf_delete.size()) < 0) { + throw std::system_error(errno, std::generic_category(), "Charge Control delete failed!"); + } + } + + // define payload variable to pack msg later + uint8_t payload[8]; + + // write CAN-header to setup new Charge Control msg + hdr_setup->opcode = TX_SETUP; + hdr_setup->can_id = this->get_can_id(this->config.device_id, CAN_CHARGE_CONTROL1_FRAME_ID); + hdr_setup->flags = SETTIMER | STARTTIMER; + hdr_setup->nframes = 1; + hdr_setup->count = 0; + hdr_setup->ival1.tv_sec = 0; + hdr_setup->ival1.tv_usec = 0; + hdr_setup->ival2.tv_sec = 0; + hdr_setup->ival2.tv_usec = 100000; + + // take Charge Control mutex to write data + std::unique_lock cc_lock(this->cc_mutex); + + // pack Charge Control payload + can_charge_control1_pack(payload, &this->com_data.charge_control, CAN_CHARGE_CONTROL1_LENGTH); + + // write CAN-frame of new Charge Control msg + frame_setup->can_id = this->get_can_id(this->config.device_id, CAN_CHARGE_CONTROL1_FRAME_ID); + frame_setup->len = static_cast(CAN_CHARGE_CONTROL1_LENGTH); + memcpy(frame_setup->data, payload, CAN_CHARGE_CONTROL1_LENGTH); + + cc_lock.unlock(); + + // setup new Charge Control msg by sending to CAN-socket + if (write(this->can_bcm_tx_fd, buf_setup.data(), buf_setup.size()) < 0) { + throw std::system_error(errno, std::generic_category(), "Charge Control init failed!"); + } + + this->charge_control_initialized = true; + } else { + throw std::system_error(errno, std::generic_category(), "Enable sending of Charge Control before updating it!"); + } +} + +void CbCPX::set_duty_cycle(unsigned int duty_cycle) { + std::unique_lock cc_lock(this->cc_mutex); + + this->com_data.charge_control.cc_target_duty_cycle = static_cast(duty_cycle); + this->com_data.charge_control.cc_pwm_active = static_cast(1); + + cc_lock.unlock(); + + this->charge_control_update(); + + // only check if CPX is not timed out + // warning about CPX timeout is provided on noticing it + if (!this->bcm_rx_timeout && this->has_cpx_connected_once) { + this->launch_duty_cycle_check(duty_cycle); + } +} + +unsigned int CbCPX::get_duty_cycle() { + return this->get_cs_current_duty_cycle(); +} + +int CbCPX::switch_state(bool on) { + std::unique_lock cc_lock(this->cc_mutex); + + this->com_data.charge_control.cc_contactor1_state = static_cast(on); + this->com_data.charge_control.cc_contactor2_state = static_cast(on); + + cc_lock.unlock(); + + this->charge_control_update(); + + // if CPX timedout ignore that contactors are not switching + if (this->bcm_rx_timeout) { + return 0; + } + + std::unique_lock contactor_cv_lock(this->contactor_cv_mutex); + contactor_change_cv.wait_for(contactor_cv_lock, 1s, [this] { return this->contactor_change.load(); }); + contactor_cv_lock.unlock(); + + // // we should see the changes take effect after 1s (FIXME) + // std::this_thread::sleep_for(1s); + + if (this->get_cs_contactor_state(1) != static_cast(on)) { + return 1; + } else if (this->get_cs_contactor_state(2) != static_cast(on)) { + return 2; + } + + return 0; +} + +bool CbCPX::get_contactor_state_no_lock() { + unsigned int i; + bool at_least_one_is_configured = false; + bool target_state = false; + bool actual_state = false; + + for (i = 1; i <= CB_PROTO_MAX_CONTACTORS; ++i) { + uint8_t cs_contactor_state_i = this->get_cs_contactor_state(i); + + if (cs_contactor_state_i == CAN_CHARGE_STATE1_CS_CONTACTOR1_STATE_OPEN_CHOICE || + cs_contactor_state_i == CAN_CHARGE_STATE1_CS_CONTACTOR1_STATE_CLOSE_CHOICE) { + at_least_one_is_configured = true; + + // don't overwrite, but merge the state + actual_state |= static_cast(cs_contactor_state_i); + } + + // fallback in the same loop in case no contactor is actually in use + // don't overwrite, but merge the state + target_state |= static_cast(this->get_cc_contactor_state(i)); + } + + if (at_least_one_is_configured) + return actual_state; + else + return target_state; +} + +bool CbCPX::get_contactor_state() { + return this->get_contactor_state_no_lock(); +} + +unsigned int CbCPX::get_temperature_channels() const { + return CB_PROTO_MAX_PT1000S; +} + +bool CbCPX::is_temperature_enabled(unsigned int channel) { + return this->get_pt1000_is_active(channel); +} + +types::board_support_common::Ampacity CbCPX::pp_state_to_ampacity(uint8_t pp_state) { + switch (pp_state) { + case CAN_CHARGE_STATE1_CS_CURRENT_PP_STATE_NO_CABLE_DETECTED_CHOICE: + return types::board_support_common::Ampacity::None; + + case CAN_CHARGE_STATE1_CS_CURRENT_PP_STATE_13_A_CHOICE: + return types::board_support_common::Ampacity::A_13; + + case CAN_CHARGE_STATE1_CS_CURRENT_PP_STATE_20_A_CHOICE: + return types::board_support_common::Ampacity::A_20; + + case CAN_CHARGE_STATE1_CS_CURRENT_PP_STATE_32_A_CHOICE: + return types::board_support_common::Ampacity::A_32; + + case CAN_CHARGE_STATE1_CS_CURRENT_PP_STATE_63_70_A_CHOICE: + return types::board_support_common::Ampacity::A_63_3ph_70_1ph; + + default: + EVLOG_error << "PP voltage out of range; no ampacity mapping available."; + return types::board_support_common::Ampacity::None; + } +} + +types::board_support_common::Ampacity CbCPX::get_ampacity() { + return this->pp_state_to_ampacity(this->get_cs_current_pp_state()); +} + +uint16_t CbCPX::get_cs_current_duty_cycle() { + std::unique_lock cs_lock(this->cs_mutex); + return this->com_data.charge_state.cs_current_duty_cycle; +} + +uint8_t CbCPX::get_cs_pwm_active() { + std::unique_lock cs_lock(this->cs_mutex); + return this->com_data.charge_state.cs_pwm_active; +} + +uint8_t CbCPX::get_cs_current_cp_state() { + std::unique_lock cs_lock(this->cs_mutex); + return this->com_data.charge_state.cs_current_cp_state; +} + +uint8_t CbCPX::get_cs_short_circuit() { + std::unique_lock cs_lock(this->cs_mutex); + return this->com_data.charge_state.cs_cp_short_circuit; +} + +uint8_t CbCPX::get_cs_diode_fault() { + std::unique_lock cs_lock(this->cs_mutex); + return this->com_data.charge_state.cs_diode_fault; +} + +uint8_t CbCPX::get_cs_current_pp_state() { + std::unique_lock cs_lock(this->cs_mutex); + return this->com_data.charge_state.cs_current_pp_state; +} + +uint8_t CbCPX::get_cs_contactor_state(int contactor) { + std::unique_lock cs_lock(this->cs_mutex); + switch (contactor) { + case 1: + return this->com_data.charge_state.cs_contactor1_state; + + case 2: + return this->com_data.charge_state.cs_contactor2_state; + + default: + throw std::system_error(errno, std::generic_category(), "Selected contactor out of range!"); + } +} + +uint8_t CbCPX::get_cc_contactor_state(int contactor) { + std::unique_lock cc_lock(this->cc_mutex); + switch (contactor) { + case 1: + return this->com_data.charge_control.cc_contactor1_state; + + case 2: + return this->com_data.charge_control.cc_contactor2_state; + + default: + throw std::system_error(errno, std::generic_category(), "Selected contactor out of range!"); + } +} + +bool CbCPX::is_cs_contactor_error(int contactor) { + std::unique_lock cs_lock(this->cs_mutex); + switch (contactor) { + case 1: + return static_cast(this->com_data.charge_state.cs_contactor1_error); + + case 2: + return static_cast(this->com_data.charge_state.cs_contactor2_error); + + case 0: + return static_cast(this->com_data.charge_state.cs_contactor1_error || + this->com_data.charge_state.cs_contactor2_error); + + default: + throw std::system_error(errno, std::generic_category(), "Selected contactor out of range!"); + } +} + +uint8_t CbCPX::get_cs_hv_ready() { + std::unique_lock cs_lock(this->cs_mutex); + return this->com_data.charge_state.cs_hv_ready; +} + +bool CbCPX::is_cs_estop_charging_abort(int estop) { + std::unique_lock cs_lock(this->cs_mutex); + switch (estop) { + case 1: + return static_cast(this->com_data.charge_state.cs_estop1_charging_abort == 1); + + case 2: + return static_cast(this->com_data.charge_state.cs_estop2_charging_abort == 1); + + case 3: + return static_cast(this->com_data.charge_state.cs_estop3_charging_abort == 1); + + case 0: + return static_cast(this->com_data.charge_state.cs_estop1_charging_abort == 1 || + this->com_data.charge_state.cs_estop2_charging_abort == 1 || + this->com_data.charge_state.cs_estop3_charging_abort == 1); + + default: + throw std::system_error(errno, std::generic_category(), "Selected estop out of range!"); + } +} + +bool CbCPX::get_pt1000_is_active(int channel) { + std::unique_lock pt_lock(this->pt_mutex); + switch (channel) { + case 1: + return static_cast(this->com_data.pt1000_state.pt1_temperature != + CAN_PT1000_STATE_PT1_TEMPERATURE_TEMP_SENSOR_NOT_USED_CHOICE); + + case 2: + return static_cast(this->com_data.pt1000_state.pt2_temperature != + CAN_PT1000_STATE_PT2_TEMPERATURE_TEMP_SENSOR_NOT_USED_CHOICE); + + case 3: + return static_cast(this->com_data.pt1000_state.pt3_temperature != + CAN_PT1000_STATE_PT3_TEMPERATURE_TEMP_SENSOR_NOT_USED_CHOICE); + + case 4: + return static_cast(this->com_data.pt1000_state.pt4_temperature != + CAN_PT1000_STATE_PT4_TEMPERATURE_TEMP_SENSOR_NOT_USED_CHOICE); + + default: + throw std::system_error(errno, std::generic_category(), "Selected PT1000 channel out of range!"); + } +} + +bool CbCPX::is_temperature_valid(unsigned int channel) { + std::unique_lock pt_lock(this->pt_mutex); + switch (channel) { + case 1: + return static_cast( + !(this->com_data.pt1000_state.pt1_selftest_failed && this->com_data.pt1000_state.pt1_charging_stopped)); + + case 2: + return static_cast( + !(this->com_data.pt1000_state.pt2_selftest_failed && this->com_data.pt1000_state.pt2_charging_stopped)); + + case 3: + return static_cast( + !(this->com_data.pt1000_state.pt3_selftest_failed && this->com_data.pt1000_state.pt3_charging_stopped)); + + case 4: + return static_cast( + !(this->com_data.pt1000_state.pt4_selftest_failed && this->com_data.pt1000_state.pt4_charging_stopped)); + + default: + throw std::system_error(errno, std::generic_category(), "Selected PT1000 channel out of range!"); + } +} + +bool CbCPX::is_pt_selftest_failed(unsigned int channel) { + std::unique_lock pt_lock(this->pt_mutex); + switch (channel) { + case 1: + return static_cast(this->com_data.pt1000_state.pt1_selftest_failed); + + case 2: + return static_cast(this->com_data.pt1000_state.pt2_selftest_failed); + + case 3: + return static_cast(this->com_data.pt1000_state.pt3_selftest_failed); + + case 4: + return static_cast(this->com_data.pt1000_state.pt4_selftest_failed); + + case 0: + return static_cast( + this->com_data.pt1000_state.pt1_selftest_failed || this->com_data.pt1000_state.pt2_selftest_failed || + this->com_data.pt1000_state.pt3_selftest_failed || this->com_data.pt1000_state.pt4_selftest_failed); + + default: + throw std::system_error(errno, std::generic_category(), "Selected PT1000 channel out of range!"); + } +} + +bool CbCPX::is_pt_charging_stopped(unsigned int channel) { + std::unique_lock pt_lock(this->pt_mutex); + switch (channel) { + case 1: + return static_cast(this->com_data.pt1000_state.pt1_charging_stopped); + + case 2: + return static_cast(this->com_data.pt1000_state.pt2_charging_stopped); + + case 3: + return static_cast(this->com_data.pt1000_state.pt3_charging_stopped); + + case 4: + return static_cast(this->com_data.pt1000_state.pt4_charging_stopped); + + case 0: + return static_cast( + this->com_data.pt1000_state.pt1_charging_stopped || this->com_data.pt1000_state.pt2_charging_stopped || + this->com_data.pt1000_state.pt3_charging_stopped || this->com_data.pt1000_state.pt4_charging_stopped); + + default: + throw std::system_error(errno, std::generic_category(), "Selected PT1000 channel out of range!"); + } +} + +float CbCPX::get_temperature(unsigned int channel) { + std::unique_lock pt_lock(this->pt_mutex); + switch (channel) { + case 1: + return static_cast(this->com_data.pt1000_state.pt1_temperature); + + case 2: + return static_cast(this->com_data.pt1000_state.pt2_temperature); + + case 3: + return static_cast(this->com_data.pt1000_state.pt3_temperature); + + case 4: + return static_cast(this->com_data.pt1000_state.pt4_temperature); + + default: + throw std::system_error(errno, std::generic_category(), "Selected PT1000 channel out of range!"); + } +} + +bool CbCPX::is_emergency() { + bool pp_error = false; + + if (this->is_pluggable) { + // any state above this is considered an error for now + pp_error = this->get_cs_current_pp_state() > CAN_CHARGE_STATE1_CS_CURRENT_PP_STATE_63_70_A_CHOICE; + } + + return pp_error || (this->get_cs_current_cp_state() == CAN_CHARGE_STATE1_CS_CURRENT_CP_STATE_INVALID_CHOICE) || + (this->get_cs_short_circuit() != 0) || (this->get_cs_diode_fault() != 0) || this->is_cs_contactor_error() || + this->is_cs_estop_charging_abort() || this->is_pt_selftest_failed() || this->is_pt_charging_stopped(); +} + +void CbCPX::read_charge_state(uint8_t* data) { + std::unique_lock cs_lock(this->cs_mutex); + + can_charge_state1_unpack(&this->com_data.charge_state, data, CAN_CHARGE_STATE1_LENGTH); + + cs_lock.unlock(); +} + +void CbCPX::read_pt1000_state(uint8_t* data) { + std::unique_lock pt_lock(this->pt_mutex); + + can_pt1000_state_unpack(&this->com_data.pt1000_state, data, CAN_PT1000_STATE_LENGTH); + + // multiply temperatures with correct scale + this->com_data.pt1000_state.pt1_temperature *= static_cast(CB_PROTO_TEMP_SCALE); + this->com_data.pt1000_state.pt2_temperature *= static_cast(CB_PROTO_TEMP_SCALE); + this->com_data.pt1000_state.pt3_temperature *= static_cast(CB_PROTO_TEMP_SCALE); + this->com_data.pt1000_state.pt4_temperature *= static_cast(CB_PROTO_TEMP_SCALE); + + pt_lock.unlock(); +} + +void CbCPX::read_fw_version(uint8_t* data) { + std::unique_lock fv_lock(this->fv_mutex); + can_firmware_version_unpack(&this->com_data.firmware_version, data, CAN_FIRMWARE_VERSION_LENGTH); +} + +void CbCPX::read_git_hash(uint8_t* data) { + std::unique_lock gh_lock(this->gh_mutex); + can_git_hash_unpack(&this->com_data.git_hash, data, CAN_GIT_HASH_LENGTH); +} + +bool CbCPX::is_any_notify_flag_set() { + return notify_flags.any(); +} + +bool CbCPX::is_contactor_error(int contactor) { + uint8_t cs_contactor_state = this->get_cs_contactor_state(contactor); + + return (cs_contactor_state == CAN_CHARGE_STATE1_CS_CONTACTOR1_STATE_OPEN_CHOICE || + cs_contactor_state == CAN_CHARGE_STATE1_CS_CONTACTOR1_STATE_CLOSE_CHOICE) && + this->is_cs_contactor_error(contactor); +} + +void CbCPX::launch_duty_cycle_check(unsigned int expected_duty_cycle) { + // stop previous thread if running + if (this->duty_cycle_check_thread.joinable()) { + this->duty_cycle_check_termination_requested = true; + this->duty_cycle_check_cv.notify_all(); + this->duty_cycle_check_thread.join(); + } + + this->duty_cycle_check_termination_requested = false; + + this->duty_cycle_check_thread = std::thread([this, expected_duty_cycle]() { + std::unique_lock lock(this->duty_cycle_check_mutex); + while (!this->duty_cycle_check_termination_requested) { + const bool notified = this->duty_cycle_check_cv.wait_for( + lock, 1s, [this] { return this->duty_cycle_check_termination_requested.load(); }); + + if (this->duty_cycle_check_termination_requested || notified) + break; + + const double duty_cycle = expected_duty_cycle / 10.0; + std::ostringstream oss; + oss << std::fixed << std::setprecision(1) << duty_cycle; + + if (expected_duty_cycle == this->get_cs_current_duty_cycle()) { + EVLOG_debug << "[suppressed] Safety Controller did accept the new duty cycle of " << oss.str() << "%"; + } else { + EVLOG_warning << "Safety Controller did not accept the new duty cycle of " << oss.str() << "%"; + } + + break; + } + }); +} + +void CbCPX::handle_timeout_watchdog(int flag) { + // disable thread in case CPX reports back + if (flag == 0) { + this->timeout_watchdog_termination_requested = true; + this->timeout_watchdog_cv.notify_all(); + if (this->timeout_watchdog_thread.joinable()) { + this->timeout_watchdog_thread.join(); + } + this->timeout_watchdog_termination_requested = false; + this->bcm_rx_timeout = false; + EVLOG_info << "Receiving on BCM socket after timeout (CPX-ID: " << std::to_string(this->config.device_id) + << ")."; + this->on_cpx_timeout(false); + return; + } + + if (this->timeout_watchdog_thread.joinable()) { + this->timeout_watchdog_termination_requested = true; + this->timeout_watchdog_cv.notify_all(); + this->timeout_watchdog_thread.join(); + } + + this->timeout_watchdog_termination_requested = false; + + this->timeout_watchdog_thread = std::thread([this] { + std::unique_lock lock(this->timeout_watchdog_mutex); + while (!this->timeout_watchdog_termination_requested) { + bool notified = + this->timeout_watchdog_cv.wait_for(lock, std::chrono::seconds(this->cpx_timeout_seconds), [this] { + return this->timeout_watchdog_termination_requested.load(); + }); + + if (this->timeout_watchdog_termination_requested || notified) + break; + + if (this->bcm_rx_timeout) { + EVLOG_warning << "Failed to receive on BCM socket - CPX-ID: " << std::to_string(this->config.device_id); + this->on_cpx_timeout(true); + break; + } + } + }); +} + +void CbCPX::notify_worker() { + uint8_t previous_cp_state = static_cast(CAN_CHARGE_STATE1_CS_CURRENT_CP_STATE_INVALID_CHOICE + 1); + + uint8_t current_cp_state; + uint8_t current_pp_state; + + while (!this->termination_requested) { + // react to changes relevant for notification + std::unique_lock lock(this->notify_mutex); + + this->notify_worker_cv.wait(lock, + [&] { return this->termination_requested || this->is_any_notify_flag_set(); }); + + const bool pp_changed = notify_exchange(NotifyFlag::PpChanged, false); + const bool cp_changed = notify_exchange(NotifyFlag::CpChanged, false); + const bool estop_1_changed = notify_exchange(NotifyFlag::Estop1Changed, false); + const bool estop_2_changed = notify_exchange(NotifyFlag::Estop2Changed, false); + const bool estop_3_changed = notify_exchange(NotifyFlag::Estop3Changed, false); + const bool cp_error = notify_exchange(NotifyFlag::CpError, false); + const bool contactor_1_error = notify_exchange(NotifyFlag::Contactor1Error, false); + const bool contactor_2_error = notify_exchange(NotifyFlag::Contactor2Error, false); + lock.unlock(); + + // check for PP changes + if (pp_changed) { + current_pp_state = this->get_cs_current_pp_state(); + if (this->is_pluggable) { + EVLOG_debug << "on_pp_change(" << this->pp_state_to_ampacity(current_pp_state) << ")"; + this->on_pp_change(current_pp_state); + } else { + EVLOG_debug << "on_pp_change(" << this->pp_state_to_ampacity(current_pp_state) + << ") [suppressed, fixed cable]"; + } + } + + // check for CP changes + if (cp_changed) { + current_cp_state = this->get_cs_current_cp_state(); + if (previous_cp_state != CAN_CHARGE_STATE1_CS_CURRENT_CP_STATE_UNKNOWN_CHOICE) { + EVLOG_debug << "on_cp_change(" << static_cast(current_cp_state) + << ")"; + this->on_cp_change(current_cp_state); + } else { + EVLOG_debug << "on_cp_change(" << static_cast(current_cp_state) + << ") [suppressed]"; + } + previous_cp_state = current_cp_state; + } + + // check for ESTOPs + if (estop_1_changed) { + this->on_estop(1u, this->is_cs_estop_charging_abort(1)); + } + + if (estop_2_changed) { + this->on_estop(2u, this->is_cs_estop_charging_abort(2)); + } + + if (estop_3_changed) { + this->on_estop(3u, this->is_cs_estop_charging_abort(3)); + } + + // check for contactor errors + if (contactor_1_error) { + const std::string name = "Contactor 1"; + this->on_contactor_error(name, static_cast(this->get_cc_contactor_state(1)), + static_cast(this->get_cs_contactor_state(1)) + ? types::cb_board_support::ContactorState::Closed + : types::cb_board_support::ContactorState::Open); + } + + if (contactor_2_error) { + const std::string name = "Contactor 2"; + this->on_contactor_error(name, static_cast(this->get_cc_contactor_state(2)), + static_cast(this->get_cs_contactor_state(2)) + ? types::cb_board_support::ContactorState::Closed + : types::cb_board_support::ContactorState::Open); + } + + // check for CP related errors + if (cp_error) { + this->on_cp_error(); + } + } + + EVLOG_info << "Notify Thread terminated"; +} + +void CbCPX::can_bcm_rx_worker() { + // create buffer to handle CAN-RX-msg + alignas(std::max(alignof(bcm_msg_head), alignof(can_frame))) std::array buf {}; + auto* hdr = reinterpret_cast(buf.data()); + auto* frame = reinterpret_cast(buf.data() + sizeof(bcm_msg_head)); + + // Snapshot of CAN-derived states used to decide which notify flags must fire. + struct NotifyState { + uint8_t pp_state {0}; + uint8_t cp_state {0}; + bool estop[CB_PROTO_MAX_ESTOPS] {false, false, false}; + unsigned int cp_errors {0}; + bool contactor_errors[CB_PROTO_MAX_CONTACTORS] {false, false}; + bool contactor_states[CB_PROTO_MAX_CONTACTORS] {false, false}; + }; + + // Capture the current notification state so we can compare before/after decoding a frame. + auto capture_notify_state = [this]() { + NotifyState state; + state.pp_state = this->get_cs_current_pp_state(); + state.cp_state = this->get_cs_current_cp_state(); + for (unsigned int i = 0; i < CB_PROTO_MAX_ESTOPS; ++i) { + state.estop[i] = this->is_cs_estop_charging_abort(i + 1); + } + state.cp_errors = (static_cast(this->get_cs_diode_fault()) << 1) | + static_cast(this->get_cs_short_circuit()); + for (unsigned int i = 0; i < CB_PROTO_MAX_CONTACTORS; ++i) { + state.contactor_errors[i] = this->is_contactor_error(i + 1); + state.contactor_states[i] = static_cast(this->get_cs_contactor_state(i + 1)); + } + return state; + }; + + EVLOG_info << "CAN BCM Rx Thread started"; + + while (!this->termination_requested) { + if (this->rx_bcm_enabled) { + const ssize_t rv = read(this->can_bcm_rx_fd, buf.data(), buf.size()); + + if (rv < 0) { + EVLOG_warning << "Read from Rx BCM socket failed."; + } + if (rv == 0) + continue; // this should usually not happen + // we should receive either a header plus frame, or only the header in case of RX_TIMEOUT + if (rv < static_cast(sizeof(bcm_msg_head) + hdr->nframes * sizeof(can_frame))) { + EVLOG_warning << "Short CAN message read on Rx BCM socket."; + } + + // check for timeout or received data + if (hdr->opcode == RX_TIMEOUT) { + this->bcm_rx_timeout = true; + this->handle_timeout_watchdog(1); + + } else if (hdr->opcode == RX_CHANGED) { + // compare current and received data by CAN-ID + if (hdr->can_id == this->charge_state_id && + memcmp(frame->data, this->charge_state_data.data(), 8) != 0) { + // remember new Charge State data as current + std::memcpy(this->charge_state_data.data(), frame->data, CAN_MAX_DLEN); + + // remember current notify state + const auto previous_notify_state = capture_notify_state(); + + this->read_charge_state(frame->data); + + // remember new notify state + const auto new_notify_state = capture_notify_state(); + + bool notify_required = false; + + auto mark_change = [&](bool changed, NotifyFlag flag) { + if (changed) { + this->notify_set(flag, true); + notify_required = true; + } + }; + + { + std::lock_guard lock(this->notify_mutex); + + mark_change(previous_notify_state.pp_state != new_notify_state.pp_state, NotifyFlag::PpChanged); + mark_change(previous_notify_state.cp_state != new_notify_state.cp_state, NotifyFlag::CpChanged); + mark_change(previous_notify_state.cp_errors != new_notify_state.cp_errors, NotifyFlag::CpError); + mark_change(previous_notify_state.contactor_errors[0] != new_notify_state.contactor_errors[0], + NotifyFlag::Contactor1Error); + mark_change(previous_notify_state.contactor_errors[1] != new_notify_state.contactor_errors[1], + NotifyFlag::Contactor2Error); + mark_change(previous_notify_state.estop[0] != new_notify_state.estop[0], + NotifyFlag::Estop1Changed); + mark_change(previous_notify_state.estop[1] != new_notify_state.estop[1], + NotifyFlag::Estop2Changed); + mark_change(previous_notify_state.estop[2] != new_notify_state.estop[2], + NotifyFlag::Estop3Changed); + } + + if (notify_required) { + this->notify_worker_cv.notify_one(); + } + + // check contactor states seperately because it is only used within this module and no flag to be + // read from the outside is set + std::lock_guard contactor_cv_lock(this->contactor_cv_mutex); + this->contactor_change = false; + if (previous_notify_state.contactor_states[0] != new_notify_state.contactor_states[0] || + previous_notify_state.contactor_states[1] != new_notify_state.contactor_states[1]) { + this->contactor_change = true; + this->contactor_change_cv.notify_one(); + } + + } else if (hdr->can_id == this->pt1000_state_id && + memcmp(frame->data, this->pt1000_state_data.data(), 8) != 0) { + // remember new frame as current + memcpy(this->pt1000_state_data.data(), frame->data, CAN_MAX_DLEN); + + // make new received data available + this->read_pt1000_state(frame->data); + this->temperature_data_is_valid = true; + + } else if (hdr->can_id != this->charge_state_id && hdr->can_id != this->pt1000_state_id) { + EVLOG_info << "[RECV] CAN ID: 0x" << std::hex << hdr->can_id; + EVLOG_info << "[Charge State] CAN ID: 0x" << std::hex << this->charge_state_id; + EVLOG_info << "[PT1000 State] CAN ID: 0x" << std::hex << this->pt1000_state_id; + EVLOG_warning << "CAN BCM RX: Unknown CAN-ID received"; + } + + if (hdr->can_id == this->charge_state_id || hdr->can_id == this->pt1000_state_id) { + // do stuff in case CPX was not yet connected + if (!this->has_cpx_connected_once) { + // request firmware version and git hash on first connection + this->get_firmware_and_git_hash(); + + // check if CPX accepted duty cycle + this->launch_duty_cycle_check(this->com_data.charge_control.cc_target_duty_cycle); + + this->has_cpx_connected_once = true; + } + + // if expected CAN-ID was received and timeout active + // reset timeout flag and report back + if (this->bcm_rx_timeout == true) { + this->handle_timeout_watchdog(0); + } + } + } else { + EVLOG_warning << "Unexpected BCM opcode received: " << std::to_string(hdr->opcode); + } + } + } + + EVLOG_info << "CAN BCM Rx Thread stopped"; +} + +void CbCPX::can_raw_rx_worker() { + struct can_frame frame; + + EVLOG_info << "CAN RAW Rx Thread started"; + + while (!this->termination_requested) { + if (this->rx_raw_enabled) { + // read blocks until new frame is available + const ssize_t rv = read(this->can_raw_fd, &frame, sizeof(frame)); + if (rv < 0) { + EVLOG_warning << "Read from RAW socket failed."; + } + if (rv == 0 || rv < static_cast(sizeof(struct can_frame))) + continue; // this should usually not happen + + if (frame.can_id == this->get_can_id(this->config.device_id, CAN_FIRMWARE_VERSION_FRAME_ID)) { + this->read_fw_version(frame.data); + } + + if (frame.can_id == this->get_can_id(this->config.device_id, CAN_GIT_HASH_FRAME_ID)) { + this->read_git_hash(frame.data); + } + } + } + + EVLOG_info << "CAN RAW Rx Thread stopped"; +} diff --git a/modules/CbCPXDriver/cpx/CbCPX.hpp b/modules/CbCPXDriver/cpx/CbCPX.hpp new file mode 100644 index 0000000..8ab1260 --- /dev/null +++ b/modules/CbCPXDriver/cpx/CbCPX.hpp @@ -0,0 +1,452 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright chargebyte GmbH and Contributors to EVerest +#pragma once +#include +#include +#include +#include +#include "can_interface/can.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace std::chrono_literals; + +// maximum supported contactors +inline constexpr std::size_t CB_PROTO_MAX_CONTACTORS = 2; + +// maximum supported estops +inline constexpr std::size_t CB_PROTO_MAX_ESTOPS = 3; + +// maximum count of PT1000 channels +inline constexpr std::size_t CB_PROTO_MAX_PT1000S = 4; + +// scale of measured temperatures +inline constexpr double CB_PROTO_TEMP_SCALE = 0.1; + +typedef struct { + struct can_charge_state1_t charge_state; + struct can_charge_control1_t charge_control; + struct can_pt1000_state_t pt1000_state; + struct can_firmware_version_t firmware_version; + struct can_git_hash_t git_hash; + struct can_inquiry_packet_t inquiry_packet; +} com_data_t; + +struct Conf { + std::string can_interface; + int can_bitrate; + int device_id; +}; + +enum class NotifyFlag : size_t { + PpChanged, + CpChanged, + CpError, + Contactor1Error, + Contactor2Error, + Estop1Changed, + Estop2Changed, + Estop3Changed, + Count +}; + +/// +/// A class for abstracting the CAN communication with the CPX's safety controller +/// +class CbCPX { + +public: + /// @brief Default constructor. + /// @param device_id Offset in CAN-ID used to identify hardware plattform + /// @param can_interface Name of the CAN-interface used for communication + /// @param can_bitrate Bitrate of the CAN-controller of the CPX + CbCPX(int device_id, const std::string& can_interface, int can_bitrate); + + /// @brief Destructor. + ~CbCPX(); + + /// @brief Open the given UART and establish initial communication with safety controller. + /// @param is_pluggable Tells whether the safety processor needs to observe the proximity pilot. + void init(bool is_pluggable); + + /// @brief Releases the reset of the safety controller and establish communication, + /// i.e. if not yet done, retrieve firmware version etc. + void enable(); + + /// @brief Signal used to inform when firmware information string was assembled + /// The parameter contains the new content + sigslot::signal on_fw_info; + + /// @brief Helper to indicate termination wish + void terminate(); + + /// @brief Helper to map the internal PP enum to the EVerest type system. + /// A 'std::runtime_error` is raised in case the mappig fails, e.g. + /// when Type 1 related states are found. + /// @return A cable current rating using `types::board_support_common::Ampacity` + types::board_support_common::Ampacity pp_state_to_ampacity(uint8_t pp_state); + + /// @brief Reads the current (cached) cable rating from the safety controller. + /// It uses `pp_state_to_ampacity`, in other words it raises an exception + /// in case the value cannot be mapped. + /// @return A cable current rating using `types::board_support_common::Ampacity` + types::board_support_common::Ampacity get_ampacity(); + + /// @brief Update Charge Control message in CAN BCM. + void charge_control_update(); + + /// @brief Return whether the safety controller detected an emergency state. + bool is_emergency(); + + /// @brief Set a new duty cycle. + /// @param duty_cycle The desired duty cycle in percent [0.1 %]. + void set_duty_cycle(unsigned int duty_cycle); + + /// @brief Set contactor state. + /// @param on Desired contactor state. Use true to close and false to open the contactor. + /// @return 0 if CPX switched to desired state, 1 or 2 depending on which contactor did not switch + int switch_state(bool on); + + /// @brief Function to call all Charge State getter-functions + /// @param data Received CAN data + void read_charge_state(uint8_t* data); + + /// @brief Function to call all PT1000 State getter-functions + /// @param data Received CAN data + void read_pt1000_state(uint8_t* data); + + /// @brief Function to read Firmware Version info + /// @param data Received CAN data + void read_fw_version(uint8_t* data); + + /// @brief Function to read Git Hash info + /// @param data Received CAN data + void read_git_hash(uint8_t* data); + + /// @brief Return the current contactor state (even when no contactor is configured) + /// @return true if contactor is closed, false otherwise + bool get_contactor_state(); + + /// @brief Get the current/actual duty cycle in [0.1 %]. + unsigned int get_duty_cycle(); + + /// @brief Signal used to inform about PP state changes. + /// The parameter contains the new PP state. + sigslot::signal on_pp_change; + + /// @brief Signal used to inform about CP state changes. + /// The parameter contains the new CP state. + sigslot::signal on_cp_change; + + /// @brief Signal used to inform about various errors, e.g. CP Short Circuits, + /// Diode Faults and contactor errors. + /// Callee is expected to check in detail. + sigslot::signal<> on_cp_error; + + /// @brief Signal used to inform about errors during contactor switching. + sigslot::signal on_contactor_error; + + /// @brief Signal used to inform about a charging stop caused by ESTOP signal. + /// The first parameter is the number of the ESTOP signal which changed, + /// the second parameter tells whether the signal is active. + sigslot::signal on_estop; + + /// @brief Remember whether the PT1000 State frame was received at least once. + bool temperature_data_is_valid {false}; + + /// @brief Retrieves the number of supported temperature channels. + /// @return The count of supported channels. + unsigned int get_temperature_channels() const; + + /// @brief Returns whether the given temperature channel is enabled or not. + /// @return True if the channel is enabled, false otherwise. + bool is_temperature_enabled(unsigned int channel); + + /// @brief Returns whether the given temperature channel passed the internal selftest. + /// @return True if the channel return valid measurements, false otherwise. + bool is_temperature_valid(unsigned int channel); + + /// @brief Returns whether the given temperature channel passed the internal selftest. + /// @return True if the channel return valid measurements, false otherwise. + bool is_pt_selftest_failed(unsigned int channel = 0); + + /// @brief Returns whether the given temperature channel passed the internal selftest. + /// @return True if the channel return valid measurements, false otherwise. + bool is_pt_charging_stopped(unsigned int channel = 0); + + /// @brief Retrieves the current temperature measured on a given channel. + /// @param channel The channel number + /// @return The temperature in °C + float get_temperature(unsigned int channel); + + /// @brief Helper to signal thread termination wish + std::atomic_bool termination_requested {false}; + + /// @brief Retrieve the diode fault flag reported by the charge state frame. + /// @return Non-zero if a diode fault was detected. + uint8_t get_cs_diode_fault(); + + /// @brief Retrieve the CP short circuit flag reported by the charge state frame. + /// @return Non-zero if a short circuit was detected. + uint8_t get_cs_short_circuit(); + + /// @brief Signal used to inform about CPX timeout. + /// The parameter contains the ID of timed out CPX. + sigslot::signal on_cpx_timeout; + +private: + /// @brief Reference to the CPX-config provided by manifest.yaml + // const module::Conf& config; + Conf config {"can0", 500000, 0}; + + /// @brief Flag to control sending of Charge Control messages + std::atomic_bool tx_cc_enabled {false}; + + /// @brief Flag to tell we are expecting incoming BCM messages + std::atomic_bool rx_bcm_enabled {false}; + + /// @brief Flag to tell we are expecting incoming RAW messages + std::atomic_bool rx_raw_enabled {false}; + + /// @brief Remember whether the system is with fixed cable or not. + bool is_pluggable {false}; + + /// @brief Struct of CAN Socket Address + struct sockaddr_can addr; + + /// @brief Interface request structure + struct ifreq ifr; + + /// @brief File descriptor of the (opened) CAN RAW interface + int can_raw_fd {-1}; + + /// @brief File descriptor of the (opened) CAN BCM RX interface + int can_bcm_rx_fd {-1}; + + /// @brief File descriptor of the (opened) CAN BCM TX interface + int can_bcm_tx_fd {-1}; + + /// @brief Holds the assembled firmware information string. + std::string fw_info; + + /// @brief Thread for receiving CAN BCM messages + std::thread can_bcm_rx_thread; + + /// @brief Thread for receiving CAN RAW messages + std::thread can_raw_rx_thread; + + /// @brief Thread for notifications upon changes + std::thread notify_thread; + + /// @brief Thread used to check duty cycle acceptance + std::thread duty_cycle_check_thread; + + /// @brief Helper function to check duty cycle acceptance + void launch_duty_cycle_check(unsigned int expected_duty_cycle); + + /// @brief Condition variable to activate duty cycle check action + std::condition_variable duty_cycle_check_cv; + + /// @brief Mutex to protect duty cycle check action + std::mutex duty_cycle_check_mutex; + + /// @brief Flag to abort duty cycle check thread + std::atomic_bool duty_cycle_check_termination_requested {false}; + + /// @brief Thread used to monitor CPX timeout + std::thread timeout_watchdog_thread; + + /// @brief Helper function to handle CPX timeout + void handle_timeout_watchdog(int flag); + + /// @brief Condition variable to activate timeout watchdog action + std::condition_variable timeout_watchdog_cv; + + /// @brief Mutex to protect timeout watchdog action + std::mutex timeout_watchdog_mutex; + + /// @brief Allowed timeout in seconds until CPX is considered lost + const int cpx_timeout_seconds {15}; + + /// @brief Flag to abort timeout watchdog thread + std::atomic_bool timeout_watchdog_termination_requested {false}; + + /// @brief Register BCM rx messages + void can_bcm_rx_init(); + + /// @brief CAN BCM receive thread worker + void can_bcm_rx_worker(); + + /// @brief CAN RAW receive thread worker + void can_raw_rx_worker(); + + /// @brief Notify thread worker + void notify_worker(); + + /// @brief struct holding all available messages + com_data_t com_data {}; + + /// @brief Prevents parallel access to charge control info + std::mutex cc_mutex; + + /// @brief Prevents parallel access to charge state info + std::mutex cs_mutex; + + /// @brief Prevents parallel access to pt1000 info + std::mutex pt_mutex; + + /// @brief Prevents parallel access to firmware version info + std::mutex fv_mutex; + + /// @brief Prevents parallel access to git hash info + std::mutex gh_mutex; + + /// @brief Prevents parallel access to inquiry packet info + std::mutex ip_mutex; + + /// @brief Prevents parallel access to notifications + std::mutex notify_mutex; + + /// @brief Prevents parallel access to BCM socket + std::mutex bcm_mutex; + + /// @brief Size of CAN-msg + static constexpr std::size_t can_msg_size = sizeof(bcm_msg_head) + sizeof(can_frame); + + /// @brief Save last new received Charge State data + std::array charge_state_data {}; + + /// @brief Save last new received Charge State data + std::array pt1000_state_data {}; + + /// @brief Save Charge State msg ID + canid_t charge_state_id; + + /// @brief Save Charge State msg ID + canid_t pt1000_state_id; + + /// @brief Condition variable used to wait for Charge State message + std::condition_variable rx_cs_cv; + + /// @brief Condition variable used to wait for PT1000 State message + std::condition_variable rx_pt_cv; + + /// @brief Condition variable used to wait for Firmware Version message + std::condition_variable rx_fv_cv; + + /// @brief Condition variable used to wait for Git Hash message + std::condition_variable rx_gh_cv; + + /// @brief Condition variable used to wait for changes relevant for notifications + std::condition_variable notify_cv; + + /// @brief Remember whether EvseManager enabled this port. + std::atomic_bool evse_enabled {false}; + + /// @brief Remember if Charge State updates were received. + std::atomic_bool cs_updated {false}; + + /// @brief Return the duty cycle reported by the latest charge state frame (unit: 0.1 %). + uint16_t get_cs_current_duty_cycle(); + + /// @brief Return whether PWM output is currently enabled according to the charge state frame. + uint8_t get_cs_pwm_active(); + + /// @brief Return the current CP state encoded in the charge state frame. + uint8_t get_cs_current_cp_state(); + + /// @brief Return the current PP state encoded in the charge state frame. + uint8_t get_cs_current_pp_state(); + + /// @brief Fetch the contactor feedback state from the charge state frame. + /// @param contactor Contactor index starting at 1. + uint8_t get_cs_contactor_state(int contactor); + + /// @brief Fetch the requested contactor state from the charge control frame. + /// @param contactor Contactor index starting at 1. + uint8_t get_cc_contactor_state(int contactor); + + /// @brief Check whether the charge state frame reports a contactor error. + /// @param contactor Contactor index (1/2) or 0 for aggregated state. + bool is_cs_contactor_error(int contactor = 0); + + /// @brief Return the HV-ready flag contained in the charge state frame. + uint8_t get_cs_hv_ready(); + + /// @brief Check whether an ESTOP input triggered a charging abort. + /// @param estop ESTOP index (1–3) or 0 to query all inputs. + bool is_cs_estop_charging_abort(int estop = 0); + + /// @brief Report whether the given PT1000 channel is active according to the PT1000 state frame. + /// @param channel Sensor index starting at 1. + bool get_pt1000_is_active(int channel); + + /// @brief Internal helper to determine the current contactor state. + bool get_contactor_state_no_lock(); + + /// @brief Compose a 29-bit CAN identifier from the configured CPX-ID and a message ID. + /// @param cpx_id Logical CPX identifier (0 keeps the raw message ID). + /// @param msg_id Base message identifier to embed in the lower bits. + canid_t get_can_id(int cpx_id, int msg_id); + + /// @brief Enables verbose CAN-ID logging while requesting firmware information. + bool print_can_id_info {false}; + + /// @brief Remember if CPX CAN-message timed out + std::atomic bcm_rx_timeout {false}; + + /// @brief Helper to request and save firmware version and git hash + void get_firmware_and_git_hash(); + + /// @brief Remember if CPX was ever connected after EVerest start up + std::atomic has_cpx_connected_once {false}; + + /// @brief Remember if Charge Control was initialized + std::atomic charge_control_initialized {false}; + + /// @brief bitset to signal notify thread about changes + std::bitset(NotifyFlag::Count)> notify_flags; + + /// @brief Helper to convert notify_flag name to bitposition + static constexpr size_t nf(NotifyFlag f) { + return static_cast(f); + } + + /// @brief Helper to set value of notify_flag bit + void notify_set(NotifyFlag f, bool value) { + notify_flags.set(nf(f), value); + } + + /// @brief Helper to get old notify_flag value and set new one + bool notify_exchange(NotifyFlag f, bool value) { + const bool old = notify_flags.test(nf(f)); + notify_set(f, value); + return old; + } + + /// @brief Condition variable to activate notify_worker action + std::condition_variable notify_worker_cv; + + /// @brief Condition variable to signal contactor state change + std::condition_variable contactor_change_cv; + + /// @brief Remember if any contactor state was changed + std::atomic contactor_change {false}; + + /// @brief Mutex to protect contactor condition variable + std::mutex contactor_cv_mutex; + + /// @brief Helper function to check if any notify_worker flag is set + bool is_any_notify_flag_set(); + + /// @brief Helper to check for contactor errors + bool is_contactor_error(int contactor); + + /// @brief Set CAN bitrate of the CAN-controller running the EVerest stack to match the CPX's + void ensure_can_bitrate(); +}; diff --git a/modules/CbCPXDriver/cpx/can_interface/can.c b/modules/CbCPXDriver/cpx/can_interface/can.c new file mode 100644 index 0000000..eddd408 --- /dev/null +++ b/modules/CbCPXDriver/cpx/can_interface/can.c @@ -0,0 +1,2718 @@ +/** + * @file can.c + * + * @brief This source file was generated by cantools version 40.2.2 Wed Jun 18 15:48:26 2025. + * + * @copyright Copyright (c) 2018-2019 Erik Moqvist + * + * @par License + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "can.h" + +static inline uint8_t pack_left_shift_u8( + uint8_t value, + uint8_t shift, + uint8_t mask) +{ + return (uint8_t)((uint8_t)(value << shift) & mask); +} + +static inline uint8_t pack_left_shift_u16( + uint16_t value, + uint8_t shift, + uint8_t mask) +{ + return (uint8_t)((uint8_t)(value << shift) & mask); +} + +static inline uint8_t pack_left_shift_u64( + uint64_t value, + uint8_t shift, + uint8_t mask) +{ + return (uint8_t)((uint8_t)(value << shift) & mask); +} + +static inline uint8_t pack_right_shift_u16( + uint16_t value, + uint8_t shift, + uint8_t mask) +{ + return (uint8_t)((uint8_t)(value >> shift) & mask); +} + +static inline uint8_t pack_right_shift_u64( + uint64_t value, + uint8_t shift, + uint8_t mask) +{ + return (uint8_t)((uint8_t)(value >> shift) & mask); +} + +static inline uint16_t unpack_left_shift_u16( + uint8_t value, + uint8_t shift, + uint8_t mask) +{ + return (uint16_t)((uint16_t)(value & mask) << shift); +} + +static inline uint64_t unpack_left_shift_u64( + uint8_t value, + uint8_t shift, + uint8_t mask) +{ + return (uint64_t)((uint64_t)(value & mask) << shift); +} + +static inline uint8_t unpack_right_shift_u8( + uint8_t value, + uint8_t shift, + uint8_t mask) +{ + return (uint8_t)((uint8_t)(value & mask) >> shift); +} + +static inline uint16_t unpack_right_shift_u16( + uint8_t value, + uint8_t shift, + uint8_t mask) +{ + return (uint16_t)((uint16_t)(value & mask) >> shift); +} + +static inline uint64_t unpack_right_shift_u64( + uint8_t value, + uint8_t shift, + uint8_t mask) +{ + return (uint64_t)((uint64_t)(value & mask) >> shift); +} + +int can_inquiry_packet_pack( + uint8_t *dst_p, + const struct can_inquiry_packet_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 8); + + dst_p[0] |= pack_left_shift_u8(src_p->packet_id, 0u, 0xffu); + + return (8); +} + +int can_inquiry_packet_unpack( + struct can_inquiry_packet_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + dst_p->packet_id = unpack_right_shift_u8(src_p[0], 0u, 0xffu); + + return (0); +} + +int can_inquiry_packet_init(struct can_inquiry_packet_t *msg_p) +{ + if (msg_p == NULL) return -1; + + memset(msg_p, 0, sizeof(struct can_inquiry_packet_t)); + + return 0; +} + +uint8_t can_inquiry_packet_packet_id_encode(double value) +{ + return (uint8_t)(value); +} + +double can_inquiry_packet_packet_id_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_inquiry_packet_packet_id_is_in_range(uint8_t value) +{ + (void)value; + + return (true); +} + +int can_digital_output_pack( + uint8_t *dst_p, + const struct can_digital_output_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 8); + + dst_p[0] |= pack_left_shift_u8(src_p->do_safety_motor_out1, 6u, 0x40u); + dst_p[0] |= pack_left_shift_u8(src_p->do_safety_motor_out2, 5u, 0x20u); + dst_p[0] |= pack_left_shift_u8(src_p->do_cp_invert, 3u, 0x08u); + dst_p[1] |= pack_left_shift_u8(src_p->do_pp_sae_iec, 7u, 0x80u); + dst_p[1] |= pack_left_shift_u8(src_p->do_safety_hvsw1_hs, 6u, 0x40u); + dst_p[1] |= pack_left_shift_u8(src_p->do_safety_hvsw2_hs, 5u, 0x20u); + dst_p[1] |= pack_left_shift_u8(src_p->do_safety_gpio_exp, 4u, 0x10u); + dst_p[1] |= pack_left_shift_u8(src_p->do_safety_imd_od_en, 3u, 0x08u); + dst_p[1] |= pack_left_shift_u8(src_p->do_pt1000_ctrlen4, 2u, 0x04u); + dst_p[1] |= pack_left_shift_u8(src_p->do_safety_hvsw3_precharge, 1u, 0x02u); + dst_p[2] |= pack_left_shift_u8(src_p->do_pt1000_ctrlen1, 5u, 0x20u); + dst_p[2] |= pack_left_shift_u8(src_p->do_pt1000_ctrlen2, 4u, 0x10u); + dst_p[2] |= pack_left_shift_u8(src_p->do_pt1000_ctrlen3, 3u, 0x08u); + dst_p[2] |= pack_left_shift_u8(src_p->do_safety_cp_state_c, 2u, 0x04u); + dst_p[3] |= pack_left_shift_u8(src_p->do_cp_dutycycle, 0u, 0xffu); + + return (8); +} + +int can_digital_output_unpack( + struct can_digital_output_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + dst_p->do_safety_motor_out1 = unpack_right_shift_u8(src_p[0], 6u, 0x40u); + dst_p->do_safety_motor_out2 = unpack_right_shift_u8(src_p[0], 5u, 0x20u); + dst_p->do_cp_invert = unpack_right_shift_u8(src_p[0], 3u, 0x08u); + dst_p->do_pp_sae_iec = unpack_right_shift_u8(src_p[1], 7u, 0x80u); + dst_p->do_safety_hvsw1_hs = unpack_right_shift_u8(src_p[1], 6u, 0x40u); + dst_p->do_safety_hvsw2_hs = unpack_right_shift_u8(src_p[1], 5u, 0x20u); + dst_p->do_safety_gpio_exp = unpack_right_shift_u8(src_p[1], 4u, 0x10u); + dst_p->do_safety_imd_od_en = unpack_right_shift_u8(src_p[1], 3u, 0x08u); + dst_p->do_pt1000_ctrlen4 = unpack_right_shift_u8(src_p[1], 2u, 0x04u); + dst_p->do_safety_hvsw3_precharge = unpack_right_shift_u8(src_p[1], 1u, 0x02u); + dst_p->do_pt1000_ctrlen1 = unpack_right_shift_u8(src_p[2], 5u, 0x20u); + dst_p->do_pt1000_ctrlen2 = unpack_right_shift_u8(src_p[2], 4u, 0x10u); + dst_p->do_pt1000_ctrlen3 = unpack_right_shift_u8(src_p[2], 3u, 0x08u); + dst_p->do_safety_cp_state_c = unpack_right_shift_u8(src_p[2], 2u, 0x04u); + dst_p->do_cp_dutycycle = unpack_right_shift_u8(src_p[3], 0u, 0xffu); + + return (0); +} + +int can_digital_output_init(struct can_digital_output_t *msg_p) +{ + if (msg_p == NULL) return -1; + + memset(msg_p, 0, sizeof(struct can_digital_output_t)); + + return 0; +} + +uint8_t can_digital_output_do_safety_motor_out1_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_output_do_safety_motor_out1_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_output_do_safety_motor_out1_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_output_do_safety_motor_out2_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_output_do_safety_motor_out2_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_output_do_safety_motor_out2_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_output_do_cp_invert_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_output_do_cp_invert_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_output_do_cp_invert_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_output_do_pp_sae_iec_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_output_do_pp_sae_iec_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_output_do_pp_sae_iec_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_output_do_safety_hvsw1_hs_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_output_do_safety_hvsw1_hs_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_output_do_safety_hvsw1_hs_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_output_do_safety_hvsw2_hs_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_output_do_safety_hvsw2_hs_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_output_do_safety_hvsw2_hs_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_output_do_safety_gpio_exp_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_output_do_safety_gpio_exp_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_output_do_safety_gpio_exp_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_output_do_safety_imd_od_en_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_output_do_safety_imd_od_en_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_output_do_safety_imd_od_en_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_output_do_pt1000_ctrlen4_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_output_do_pt1000_ctrlen4_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_output_do_pt1000_ctrlen4_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_output_do_safety_hvsw3_precharge_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_output_do_safety_hvsw3_precharge_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_output_do_safety_hvsw3_precharge_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_output_do_pt1000_ctrlen1_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_output_do_pt1000_ctrlen1_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_output_do_pt1000_ctrlen1_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_output_do_pt1000_ctrlen2_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_output_do_pt1000_ctrlen2_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_output_do_pt1000_ctrlen2_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_output_do_pt1000_ctrlen3_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_output_do_pt1000_ctrlen3_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_output_do_pt1000_ctrlen3_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_output_do_safety_cp_state_c_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_output_do_safety_cp_state_c_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_output_do_safety_cp_state_c_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_output_do_cp_dutycycle_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_output_do_cp_dutycycle_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_output_do_cp_dutycycle_is_in_range(uint8_t value) +{ + (void)value; + + return (true); +} + +int can_diagnostic_measurements2_pack( + uint8_t *dst_p, + const struct can_diagnostic_measurements2_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 8); + + dst_p[0] |= pack_right_shift_u16(src_p->dm_ce_adc_val, 8u, 0xffu); + dst_p[1] |= pack_left_shift_u16(src_p->dm_ce_adc_val, 0u, 0xffu); + dst_p[2] |= pack_right_shift_u16(src_p->dm_id_adc_val, 8u, 0xffu); + dst_p[3] |= pack_left_shift_u16(src_p->dm_id_adc_val, 0u, 0xffu); + + return (8); +} + +int can_diagnostic_measurements2_unpack( + struct can_diagnostic_measurements2_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + dst_p->dm_ce_adc_val = unpack_left_shift_u16(src_p[0], 8u, 0xffu); + dst_p->dm_ce_adc_val |= unpack_right_shift_u16(src_p[1], 0u, 0xffu); + dst_p->dm_id_adc_val = unpack_left_shift_u16(src_p[2], 8u, 0xffu); + dst_p->dm_id_adc_val |= unpack_right_shift_u16(src_p[3], 0u, 0xffu); + + return (0); +} + +int can_diagnostic_measurements2_init(struct can_diagnostic_measurements2_t *msg_p) +{ + if (msg_p == NULL) return -1; + + memset(msg_p, 0, sizeof(struct can_diagnostic_measurements2_t)); + + return 0; +} + +uint16_t can_diagnostic_measurements2_dm_ce_adc_val_encode(double value) +{ + return (uint16_t)(value); +} + +double can_diagnostic_measurements2_dm_ce_adc_val_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_diagnostic_measurements2_dm_ce_adc_val_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +uint16_t can_diagnostic_measurements2_dm_id_adc_val_encode(double value) +{ + return (uint16_t)(value); +} + +double can_diagnostic_measurements2_dm_id_adc_val_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_diagnostic_measurements2_dm_id_adc_val_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +int can_analog_input04_pack( + uint8_t *dst_p, + const struct can_analog_input04_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 8); + + dst_p[0] |= pack_right_shift_u16(src_p->ai_pt1000_cfb3_4, 8u, 0xffu); + dst_p[1] |= pack_left_shift_u16(src_p->ai_pt1000_cfb3_4, 0u, 0xffu); + dst_p[2] |= pack_right_shift_u16(src_p->ai_int_temp, 8u, 0xffu); + dst_p[3] |= pack_left_shift_u16(src_p->ai_int_temp, 0u, 0xffu); + dst_p[4] |= pack_right_shift_u16(src_p->ai_int_refvolt, 8u, 0xffu); + dst_p[5] |= pack_left_shift_u16(src_p->ai_int_refvolt, 0u, 0xffu); + + return (8); +} + +int can_analog_input04_unpack( + struct can_analog_input04_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + dst_p->ai_pt1000_cfb3_4 = unpack_left_shift_u16(src_p[0], 8u, 0xffu); + dst_p->ai_pt1000_cfb3_4 |= unpack_right_shift_u16(src_p[1], 0u, 0xffu); + dst_p->ai_int_temp = unpack_left_shift_u16(src_p[2], 8u, 0xffu); + dst_p->ai_int_temp |= unpack_right_shift_u16(src_p[3], 0u, 0xffu); + dst_p->ai_int_refvolt = unpack_left_shift_u16(src_p[4], 8u, 0xffu); + dst_p->ai_int_refvolt |= unpack_right_shift_u16(src_p[5], 0u, 0xffu); + + return (0); +} + +int can_analog_input04_init(struct can_analog_input04_t *msg_p) +{ + if (msg_p == NULL) return -1; + + memset(msg_p, 0, sizeof(struct can_analog_input04_t)); + + return 0; +} + +uint16_t can_analog_input04_ai_pt1000_cfb3_4_encode(double value) +{ + return (uint16_t)(value); +} + +double can_analog_input04_ai_pt1000_cfb3_4_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_analog_input04_ai_pt1000_cfb3_4_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +uint16_t can_analog_input04_ai_int_temp_encode(double value) +{ + return (uint16_t)(value); +} + +double can_analog_input04_ai_int_temp_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_analog_input04_ai_int_temp_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +uint16_t can_analog_input04_ai_int_refvolt_encode(double value) +{ + return (uint16_t)(value); +} + +double can_analog_input04_ai_int_refvolt_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_analog_input04_ai_int_refvolt_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +int can_analog_input03_pack( + uint8_t *dst_p, + const struct can_analog_input03_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 8); + + dst_p[0] |= pack_right_shift_u16(src_p->ai_safety_precharge_cfb, 8u, 0xffu); + dst_p[1] |= pack_left_shift_u16(src_p->ai_safety_precharge_cfb, 0u, 0xffu); + dst_p[2] |= pack_right_shift_u16(src_p->ai_safety_hs1_cfb, 8u, 0xffu); + dst_p[3] |= pack_left_shift_u16(src_p->ai_safety_hs1_cfb, 0u, 0xffu); + dst_p[4] |= pack_right_shift_u16(src_p->ai_safety_hs2_cfb, 8u, 0xffu); + dst_p[5] |= pack_left_shift_u16(src_p->ai_safety_hs2_cfb, 0u, 0xffu); + dst_p[6] |= pack_right_shift_u16(src_p->ai_pt1000_cfb1_2, 8u, 0xffu); + dst_p[7] |= pack_left_shift_u16(src_p->ai_pt1000_cfb1_2, 0u, 0xffu); + + return (8); +} + +int can_analog_input03_unpack( + struct can_analog_input03_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + dst_p->ai_safety_precharge_cfb = unpack_left_shift_u16(src_p[0], 8u, 0xffu); + dst_p->ai_safety_precharge_cfb |= unpack_right_shift_u16(src_p[1], 0u, 0xffu); + dst_p->ai_safety_hs1_cfb = unpack_left_shift_u16(src_p[2], 8u, 0xffu); + dst_p->ai_safety_hs1_cfb |= unpack_right_shift_u16(src_p[3], 0u, 0xffu); + dst_p->ai_safety_hs2_cfb = unpack_left_shift_u16(src_p[4], 8u, 0xffu); + dst_p->ai_safety_hs2_cfb |= unpack_right_shift_u16(src_p[5], 0u, 0xffu); + dst_p->ai_pt1000_cfb1_2 = unpack_left_shift_u16(src_p[6], 8u, 0xffu); + dst_p->ai_pt1000_cfb1_2 |= unpack_right_shift_u16(src_p[7], 0u, 0xffu); + + return (0); +} + +int can_analog_input03_init(struct can_analog_input03_t *msg_p) +{ + if (msg_p == NULL) return -1; + + memset(msg_p, 0, sizeof(struct can_analog_input03_t)); + + return 0; +} + +uint16_t can_analog_input03_ai_safety_precharge_cfb_encode(double value) +{ + return (uint16_t)(value); +} + +double can_analog_input03_ai_safety_precharge_cfb_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_analog_input03_ai_safety_precharge_cfb_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +uint16_t can_analog_input03_ai_safety_hs1_cfb_encode(double value) +{ + return (uint16_t)(value); +} + +double can_analog_input03_ai_safety_hs1_cfb_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_analog_input03_ai_safety_hs1_cfb_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +uint16_t can_analog_input03_ai_safety_hs2_cfb_encode(double value) +{ + return (uint16_t)(value); +} + +double can_analog_input03_ai_safety_hs2_cfb_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_analog_input03_ai_safety_hs2_cfb_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +uint16_t can_analog_input03_ai_pt1000_cfb1_2_encode(double value) +{ + return (uint16_t)(value); +} + +double can_analog_input03_ai_pt1000_cfb1_2_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_analog_input03_ai_pt1000_cfb1_2_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +int can_analog_input02_pack( + uint8_t *dst_p, + const struct can_analog_input02_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 8); + + dst_p[0] |= pack_right_shift_u16(src_p->ai_cp_buffered_neg_adc, 8u, 0xffu); + dst_p[1] |= pack_left_shift_u16(src_p->ai_cp_buffered_neg_adc, 0u, 0xffu); + dst_p[2] |= pack_right_shift_u16(src_p->ai_cp_buffered_pos_adc, 8u, 0xffu); + dst_p[3] |= pack_left_shift_u16(src_p->ai_cp_buffered_pos_adc, 0u, 0xffu); + dst_p[4] |= pack_right_shift_u16(src_p->ai_pp_value_adc, 8u, 0xffu); + dst_p[5] |= pack_left_shift_u16(src_p->ai_pp_value_adc, 0u, 0xffu); + dst_p[6] |= pack_right_shift_u16(src_p->ai_safety_u_in_adc, 8u, 0xffu); + dst_p[7] |= pack_left_shift_u16(src_p->ai_safety_u_in_adc, 0u, 0xffu); + + return (8); +} + +int can_analog_input02_unpack( + struct can_analog_input02_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + dst_p->ai_cp_buffered_neg_adc = unpack_left_shift_u16(src_p[0], 8u, 0xffu); + dst_p->ai_cp_buffered_neg_adc |= unpack_right_shift_u16(src_p[1], 0u, 0xffu); + dst_p->ai_cp_buffered_pos_adc = unpack_left_shift_u16(src_p[2], 8u, 0xffu); + dst_p->ai_cp_buffered_pos_adc |= unpack_right_shift_u16(src_p[3], 0u, 0xffu); + dst_p->ai_pp_value_adc = unpack_left_shift_u16(src_p[4], 8u, 0xffu); + dst_p->ai_pp_value_adc |= unpack_right_shift_u16(src_p[5], 0u, 0xffu); + dst_p->ai_safety_u_in_adc = unpack_left_shift_u16(src_p[6], 8u, 0xffu); + dst_p->ai_safety_u_in_adc |= unpack_right_shift_u16(src_p[7], 0u, 0xffu); + + return (0); +} + +int can_analog_input02_init(struct can_analog_input02_t *msg_p) +{ + if (msg_p == NULL) return -1; + + memset(msg_p, 0, sizeof(struct can_analog_input02_t)); + + return 0; +} + +uint16_t can_analog_input02_ai_cp_buffered_neg_adc_encode(double value) +{ + return (uint16_t)(value); +} + +double can_analog_input02_ai_cp_buffered_neg_adc_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_analog_input02_ai_cp_buffered_neg_adc_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +uint16_t can_analog_input02_ai_cp_buffered_pos_adc_encode(double value) +{ + return (uint16_t)(value); +} + +double can_analog_input02_ai_cp_buffered_pos_adc_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_analog_input02_ai_cp_buffered_pos_adc_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +uint16_t can_analog_input02_ai_pp_value_adc_encode(double value) +{ + return (uint16_t)(value); +} + +double can_analog_input02_ai_pp_value_adc_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_analog_input02_ai_pp_value_adc_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +uint16_t can_analog_input02_ai_safety_u_in_adc_encode(double value) +{ + return (uint16_t)(value); +} + +double can_analog_input02_ai_safety_u_in_adc_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_analog_input02_ai_safety_u_in_adc_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +int can_analog_input01_pack( + uint8_t *dst_p, + const struct can_analog_input01_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 8); + + dst_p[0] |= pack_right_shift_u16(src_p->ai_pt1000_lead1_1, 8u, 0xffu); + dst_p[1] |= pack_left_shift_u16(src_p->ai_pt1000_lead1_1, 0u, 0xffu); + dst_p[2] |= pack_right_shift_u16(src_p->ai_pt1000_lead1_2, 8u, 0xffu); + dst_p[3] |= pack_left_shift_u16(src_p->ai_pt1000_lead1_2, 0u, 0xffu); + dst_p[4] |= pack_right_shift_u16(src_p->ai_pt1000_lead1_3, 8u, 0xffu); + dst_p[5] |= pack_left_shift_u16(src_p->ai_pt1000_lead1_3, 0u, 0xffu); + dst_p[6] |= pack_right_shift_u16(src_p->ai_pt1000_lead1_4, 8u, 0xffu); + dst_p[7] |= pack_left_shift_u16(src_p->ai_pt1000_lead1_4, 0u, 0xffu); + + return (8); +} + +int can_analog_input01_unpack( + struct can_analog_input01_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + dst_p->ai_pt1000_lead1_1 = unpack_left_shift_u16(src_p[0], 8u, 0xffu); + dst_p->ai_pt1000_lead1_1 |= unpack_right_shift_u16(src_p[1], 0u, 0xffu); + dst_p->ai_pt1000_lead1_2 = unpack_left_shift_u16(src_p[2], 8u, 0xffu); + dst_p->ai_pt1000_lead1_2 |= unpack_right_shift_u16(src_p[3], 0u, 0xffu); + dst_p->ai_pt1000_lead1_3 = unpack_left_shift_u16(src_p[4], 8u, 0xffu); + dst_p->ai_pt1000_lead1_3 |= unpack_right_shift_u16(src_p[5], 0u, 0xffu); + dst_p->ai_pt1000_lead1_4 = unpack_left_shift_u16(src_p[6], 8u, 0xffu); + dst_p->ai_pt1000_lead1_4 |= unpack_right_shift_u16(src_p[7], 0u, 0xffu); + + return (0); +} + +int can_analog_input01_init(struct can_analog_input01_t *msg_p) +{ + if (msg_p == NULL) return -1; + + memset(msg_p, 0, sizeof(struct can_analog_input01_t)); + + return 0; +} + +uint16_t can_analog_input01_ai_pt1000_lead1_1_encode(double value) +{ + return (uint16_t)(value); +} + +double can_analog_input01_ai_pt1000_lead1_1_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_analog_input01_ai_pt1000_lead1_1_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +uint16_t can_analog_input01_ai_pt1000_lead1_2_encode(double value) +{ + return (uint16_t)(value); +} + +double can_analog_input01_ai_pt1000_lead1_2_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_analog_input01_ai_pt1000_lead1_2_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +uint16_t can_analog_input01_ai_pt1000_lead1_3_encode(double value) +{ + return (uint16_t)(value); +} + +double can_analog_input01_ai_pt1000_lead1_3_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_analog_input01_ai_pt1000_lead1_3_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +uint16_t can_analog_input01_ai_pt1000_lead1_4_encode(double value) +{ + return (uint16_t)(value); +} + +double can_analog_input01_ai_pt1000_lead1_4_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_analog_input01_ai_pt1000_lead1_4_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +int can_charge_control2_pack( + uint8_t *dst_p, + const struct can_charge_control2_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 8); + + dst_p[0] |= pack_left_shift_u8(src_p->cc_ccs_ready, 4u, 0xf0u); + dst_p[0] |= pack_left_shift_u8(src_p->cc_controller_reset, 2u, 0x0cu); + + return (8); +} + +int can_charge_control2_unpack( + struct can_charge_control2_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + dst_p->cc_ccs_ready = unpack_right_shift_u8(src_p[0], 4u, 0xf0u); + dst_p->cc_controller_reset = unpack_right_shift_u8(src_p[0], 2u, 0x0cu); + + return (0); +} + +int can_charge_control2_init(struct can_charge_control2_t *msg_p) +{ + if (msg_p == NULL) return -1; + + memset(msg_p, 0, sizeof(struct can_charge_control2_t)); + + return 0; +} + +uint8_t can_charge_control2_cc_ccs_ready_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_control2_cc_ccs_ready_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_control2_cc_ccs_ready_is_in_range(uint8_t value) +{ + return (value <= 15u); +} + +uint8_t can_charge_control2_cc_controller_reset_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_control2_cc_controller_reset_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_control2_cc_controller_reset_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +int can_charge_state2_pack( + uint8_t *dst_p, + const struct can_charge_state2_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 8); + + dst_p[0] |= pack_left_shift_u8(src_p->cs_ce_state, 4u, 0xf0u); + dst_p[0] |= pack_left_shift_u8(src_p->cs_id_state, 0u, 0x0fu); + dst_p[1] |= pack_left_shift_u8(src_p->cs_e_stop_reason, 0u, 0xffu); + + return (8); +} + +int can_charge_state2_unpack( + struct can_charge_state2_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + dst_p->cs_ce_state = unpack_right_shift_u8(src_p[0], 4u, 0xf0u); + dst_p->cs_id_state = unpack_right_shift_u8(src_p[0], 0u, 0x0fu); + dst_p->cs_e_stop_reason = unpack_right_shift_u8(src_p[1], 0u, 0xffu); + + return (0); +} + +int can_charge_state2_init(struct can_charge_state2_t *msg_p) +{ + if (msg_p == NULL) return -1; + + memset(msg_p, 0, sizeof(struct can_charge_state2_t)); + + return 0; +} + +uint8_t can_charge_state2_cs_ce_state_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_state2_cs_ce_state_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_state2_cs_ce_state_is_in_range(uint8_t value) +{ + return (value <= 15u); +} + +uint8_t can_charge_state2_cs_id_state_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_state2_cs_id_state_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_state2_cs_id_state_is_in_range(uint8_t value) +{ + return (value <= 15u); +} + +uint8_t can_charge_state2_cs_e_stop_reason_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_state2_cs_e_stop_reason_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_state2_cs_e_stop_reason_is_in_range(uint8_t value) +{ + (void)value; + + return (true); +} + +int can_charge_state1_pack( + uint8_t *dst_p, + const struct can_charge_state1_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 8); + + dst_p[0] |= pack_left_shift_u8(src_p->cs_pwm_active, 7u, 0x80u); + dst_p[0] |= pack_right_shift_u16(src_p->cs_current_duty_cycle, 8u, 0x03u); + dst_p[1] |= pack_left_shift_u16(src_p->cs_current_duty_cycle, 0u, 0xffu); + dst_p[2] |= pack_left_shift_u8(src_p->cs_diode_fault, 4u, 0x10u); + dst_p[2] |= pack_left_shift_u8(src_p->cs_cp_short_circuit, 3u, 0x08u); + dst_p[2] |= pack_left_shift_u8(src_p->cs_current_cp_state, 0u, 0x07u); + dst_p[3] |= pack_left_shift_u8(src_p->cs_current_pp_state, 0u, 0x07u); + dst_p[4] |= pack_left_shift_u8(src_p->cs_hv_ready, 6u, 0x40u); + dst_p[4] |= pack_left_shift_u8(src_p->cs_contactor2_error, 5u, 0x20u); + dst_p[4] |= pack_left_shift_u8(src_p->cs_contactor2_state, 3u, 0x18u); + dst_p[4] |= pack_left_shift_u8(src_p->cs_contactor1_error, 2u, 0x04u); + dst_p[4] |= pack_left_shift_u8(src_p->cs_contactor1_state, 0u, 0x03u); + dst_p[5] |= pack_left_shift_u8(src_p->cs_estop3_charging_abort, 4u, 0x30u); + dst_p[5] |= pack_left_shift_u8(src_p->cs_estop2_charging_abort, 2u, 0x0cu); + dst_p[5] |= pack_left_shift_u8(src_p->cs_estop1_charging_abort, 0u, 0x03u); + + return (8); +} + +int can_charge_state1_unpack( + struct can_charge_state1_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + dst_p->cs_pwm_active = unpack_right_shift_u8(src_p[0], 7u, 0x80u); + dst_p->cs_current_duty_cycle = unpack_left_shift_u16(src_p[0], 8u, 0x03u); + dst_p->cs_current_duty_cycle |= unpack_right_shift_u16(src_p[1], 0u, 0xffu); + dst_p->cs_diode_fault = unpack_right_shift_u8(src_p[2], 4u, 0x10u); + dst_p->cs_cp_short_circuit = unpack_right_shift_u8(src_p[2], 3u, 0x08u); + dst_p->cs_current_cp_state = unpack_right_shift_u8(src_p[2], 0u, 0x07u); + dst_p->cs_current_pp_state = unpack_right_shift_u8(src_p[3], 0u, 0x07u); + dst_p->cs_hv_ready = unpack_right_shift_u8(src_p[4], 6u, 0x40u); + dst_p->cs_contactor2_error = unpack_right_shift_u8(src_p[4], 5u, 0x20u); + dst_p->cs_contactor2_state = unpack_right_shift_u8(src_p[4], 3u, 0x18u); + dst_p->cs_contactor1_error = unpack_right_shift_u8(src_p[4], 2u, 0x04u); + dst_p->cs_contactor1_state = unpack_right_shift_u8(src_p[4], 0u, 0x03u); + dst_p->cs_estop3_charging_abort = unpack_right_shift_u8(src_p[5], 4u, 0x30u); + dst_p->cs_estop2_charging_abort = unpack_right_shift_u8(src_p[5], 2u, 0x0cu); + dst_p->cs_estop1_charging_abort = unpack_right_shift_u8(src_p[5], 0u, 0x03u); + + return (0); +} + +int can_charge_state1_init(struct can_charge_state1_t *msg_p) +{ + if (msg_p == NULL) return -1; + + memset(msg_p, 0, sizeof(struct can_charge_state1_t)); + + return 0; +} + +uint8_t can_charge_state1_cs_pwm_active_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_state1_cs_pwm_active_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_state1_cs_pwm_active_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint16_t can_charge_state1_cs_current_duty_cycle_encode(double value) +{ + return (uint16_t)(value / 0.1); +} + +double can_charge_state1_cs_current_duty_cycle_decode(uint16_t value) +{ + return ((double)value * 0.1); +} + +bool can_charge_state1_cs_current_duty_cycle_is_in_range(uint16_t value) +{ + return (value <= 1023u); +} + +uint8_t can_charge_state1_cs_diode_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_state1_cs_diode_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_state1_cs_diode_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_charge_state1_cs_cp_short_circuit_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_state1_cs_cp_short_circuit_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_state1_cs_cp_short_circuit_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_charge_state1_cs_current_cp_state_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_state1_cs_current_cp_state_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_state1_cs_current_cp_state_is_in_range(uint8_t value) +{ + return (value <= 7u); +} + +uint8_t can_charge_state1_cs_current_pp_state_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_state1_cs_current_pp_state_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_state1_cs_current_pp_state_is_in_range(uint8_t value) +{ + return (value <= 7u); +} + +uint8_t can_charge_state1_cs_hv_ready_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_state1_cs_hv_ready_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_state1_cs_hv_ready_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_charge_state1_cs_contactor2_error_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_state1_cs_contactor2_error_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_state1_cs_contactor2_error_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_charge_state1_cs_contactor2_state_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_state1_cs_contactor2_state_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_state1_cs_contactor2_state_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +uint8_t can_charge_state1_cs_contactor1_error_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_state1_cs_contactor1_error_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_state1_cs_contactor1_error_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_charge_state1_cs_contactor1_state_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_state1_cs_contactor1_state_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_state1_cs_contactor1_state_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +uint8_t can_charge_state1_cs_estop3_charging_abort_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_state1_cs_estop3_charging_abort_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_state1_cs_estop3_charging_abort_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +uint8_t can_charge_state1_cs_estop2_charging_abort_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_state1_cs_estop2_charging_abort_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_state1_cs_estop2_charging_abort_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +uint8_t can_charge_state1_cs_estop1_charging_abort_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_state1_cs_estop1_charging_abort_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_state1_cs_estop1_charging_abort_is_in_range(uint8_t value) +{ + return (value <= 3u); +} + +int can_charge_control1_pack( + uint8_t *dst_p, + const struct can_charge_control1_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 8); + + dst_p[0] |= pack_left_shift_u8(src_p->cc_pwm_active, 7u, 0x80u); + dst_p[0] |= pack_right_shift_u16(src_p->cc_target_duty_cycle, 8u, 0x03u); + dst_p[1] |= pack_left_shift_u16(src_p->cc_target_duty_cycle, 0u, 0xffu); + dst_p[2] |= pack_left_shift_u8(src_p->cc_contactor2_state, 1u, 0x02u); + dst_p[2] |= pack_left_shift_u8(src_p->cc_contactor1_state, 0u, 0x01u); + + return (8); +} + +int can_charge_control1_unpack( + struct can_charge_control1_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + dst_p->cc_pwm_active = unpack_right_shift_u8(src_p[0], 7u, 0x80u); + dst_p->cc_target_duty_cycle = unpack_left_shift_u16(src_p[0], 8u, 0x03u); + dst_p->cc_target_duty_cycle |= unpack_right_shift_u16(src_p[1], 0u, 0xffu); + dst_p->cc_contactor2_state = unpack_right_shift_u8(src_p[2], 1u, 0x02u); + dst_p->cc_contactor1_state = unpack_right_shift_u8(src_p[2], 0u, 0x01u); + + return (0); +} + +int can_charge_control1_init(struct can_charge_control1_t *msg_p) +{ + if (msg_p == NULL) return -1; + + memset(msg_p, 0, sizeof(struct can_charge_control1_t)); + + return 0; +} + +uint8_t can_charge_control1_cc_pwm_active_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_control1_cc_pwm_active_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_control1_cc_pwm_active_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint16_t can_charge_control1_cc_target_duty_cycle_encode(double value) +{ + return (uint16_t)(value / 0.1); +} + +double can_charge_control1_cc_target_duty_cycle_decode(uint16_t value) +{ + return ((double)value * 0.1); +} + +bool can_charge_control1_cc_target_duty_cycle_is_in_range(uint16_t value) +{ + return (value <= 1023u); +} + +uint8_t can_charge_control1_cc_contactor2_state_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_control1_cc_contactor2_state_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_control1_cc_contactor2_state_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_charge_control1_cc_contactor1_state_encode(double value) +{ + return (uint8_t)(value); +} + +double can_charge_control1_cc_contactor1_state_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_charge_control1_cc_contactor1_state_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +int can_firmware_version_pack( + uint8_t *dst_p, + const struct can_firmware_version_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 8); + + dst_p[0] |= pack_left_shift_u8(src_p->major_version, 0u, 0xffu); + dst_p[1] |= pack_left_shift_u8(src_p->minor_version, 0u, 0xffu); + dst_p[2] |= pack_left_shift_u8(src_p->build_version, 0u, 0xffu); + dst_p[3] |= pack_left_shift_u8(src_p->platform_type, 0u, 0xffu); + dst_p[4] |= pack_left_shift_u8(src_p->application_type, 0u, 0xffu); + + return (8); +} + +int can_firmware_version_unpack( + struct can_firmware_version_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + dst_p->major_version = unpack_right_shift_u8(src_p[0], 0u, 0xffu); + dst_p->minor_version = unpack_right_shift_u8(src_p[1], 0u, 0xffu); + dst_p->build_version = unpack_right_shift_u8(src_p[2], 0u, 0xffu); + dst_p->platform_type = unpack_right_shift_u8(src_p[3], 0u, 0xffu); + dst_p->application_type = unpack_right_shift_u8(src_p[4], 0u, 0xffu); + + return (0); +} + +int can_firmware_version_init(struct can_firmware_version_t *msg_p) +{ + if (msg_p == NULL) return -1; + + memset(msg_p, 0, sizeof(struct can_firmware_version_t)); + + return 0; +} + +uint8_t can_firmware_version_major_version_encode(double value) +{ + return (uint8_t)(value); +} + +double can_firmware_version_major_version_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_firmware_version_major_version_is_in_range(uint8_t value) +{ + (void)value; + + return (true); +} + +uint8_t can_firmware_version_minor_version_encode(double value) +{ + return (uint8_t)(value); +} + +double can_firmware_version_minor_version_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_firmware_version_minor_version_is_in_range(uint8_t value) +{ + (void)value; + + return (true); +} + +uint8_t can_firmware_version_build_version_encode(double value) +{ + return (uint8_t)(value); +} + +double can_firmware_version_build_version_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_firmware_version_build_version_is_in_range(uint8_t value) +{ + (void)value; + + return (true); +} + +uint8_t can_firmware_version_platform_type_encode(double value) +{ + return (uint8_t)(value); +} + +double can_firmware_version_platform_type_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_firmware_version_platform_type_is_in_range(uint8_t value) +{ + (void)value; + + return (true); +} + +uint8_t can_firmware_version_application_type_encode(double value) +{ + return (uint8_t)(value); +} + +double can_firmware_version_application_type_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_firmware_version_application_type_is_in_range(uint8_t value) +{ + (void)value; + + return (true); +} + +int can_git_hash_pack( + uint8_t *dst_p, + const struct can_git_hash_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 8); + + dst_p[0] |= pack_right_shift_u64(src_p->hash_signal, 56u, 0xffu); + dst_p[1] |= pack_right_shift_u64(src_p->hash_signal, 48u, 0xffu); + dst_p[2] |= pack_right_shift_u64(src_p->hash_signal, 40u, 0xffu); + dst_p[3] |= pack_right_shift_u64(src_p->hash_signal, 32u, 0xffu); + dst_p[4] |= pack_right_shift_u64(src_p->hash_signal, 24u, 0xffu); + dst_p[5] |= pack_right_shift_u64(src_p->hash_signal, 16u, 0xffu); + dst_p[6] |= pack_right_shift_u64(src_p->hash_signal, 8u, 0xffu); + dst_p[7] |= pack_left_shift_u64(src_p->hash_signal, 0u, 0xffu); + + return (8); +} + +int can_git_hash_unpack( + struct can_git_hash_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + dst_p->hash_signal = unpack_left_shift_u64(src_p[0], 56u, 0xffu); + dst_p->hash_signal |= unpack_left_shift_u64(src_p[1], 48u, 0xffu); + dst_p->hash_signal |= unpack_left_shift_u64(src_p[2], 40u, 0xffu); + dst_p->hash_signal |= unpack_left_shift_u64(src_p[3], 32u, 0xffu); + dst_p->hash_signal |= unpack_left_shift_u64(src_p[4], 24u, 0xffu); + dst_p->hash_signal |= unpack_left_shift_u64(src_p[5], 16u, 0xffu); + dst_p->hash_signal |= unpack_left_shift_u64(src_p[6], 8u, 0xffu); + dst_p->hash_signal |= unpack_right_shift_u64(src_p[7], 0u, 0xffu); + + return (0); +} + +int can_git_hash_init(struct can_git_hash_t *msg_p) +{ + if (msg_p == NULL) return -1; + + memset(msg_p, 0, sizeof(struct can_git_hash_t)); + + return 0; +} + +uint64_t can_git_hash_hash_signal_encode(double value) +{ + return (uint64_t)(value); +} + +double can_git_hash_hash_signal_decode(uint64_t value) +{ + return ((double)value); +} + +bool can_git_hash_hash_signal_is_in_range(uint64_t value) +{ + (void)value; + + return (true); +} + +int can_error_message_pack( + uint8_t *dst_p, + const struct can_error_message_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 8); + + dst_p[0] |= pack_left_shift_u8(src_p->error_active, 7u, 0x80u); + dst_p[0] |= pack_right_shift_u16(src_p->error_module, 8u, 0x7fu); + dst_p[1] |= pack_left_shift_u16(src_p->error_module, 0u, 0xffu); + dst_p[2] |= pack_right_shift_u16(src_p->error_reason, 8u, 0xffu); + dst_p[3] |= pack_left_shift_u16(src_p->error_reason, 0u, 0xffu); + dst_p[4] |= pack_right_shift_u16(src_p->error_add_data1, 8u, 0xffu); + dst_p[5] |= pack_left_shift_u16(src_p->error_add_data1, 0u, 0xffu); + dst_p[6] |= pack_right_shift_u16(src_p->error_add_data2, 8u, 0xffu); + dst_p[7] |= pack_left_shift_u16(src_p->error_add_data2, 0u, 0xffu); + + return (8); +} + +int can_error_message_unpack( + struct can_error_message_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + dst_p->error_active = unpack_right_shift_u8(src_p[0], 7u, 0x80u); + dst_p->error_module = unpack_left_shift_u16(src_p[0], 8u, 0x7fu); + dst_p->error_module |= unpack_right_shift_u16(src_p[1], 0u, 0xffu); + dst_p->error_reason = unpack_left_shift_u16(src_p[2], 8u, 0xffu); + dst_p->error_reason |= unpack_right_shift_u16(src_p[3], 0u, 0xffu); + dst_p->error_add_data1 = unpack_left_shift_u16(src_p[4], 8u, 0xffu); + dst_p->error_add_data1 |= unpack_right_shift_u16(src_p[5], 0u, 0xffu); + dst_p->error_add_data2 = unpack_left_shift_u16(src_p[6], 8u, 0xffu); + dst_p->error_add_data2 |= unpack_right_shift_u16(src_p[7], 0u, 0xffu); + + return (0); +} + +int can_error_message_init(struct can_error_message_t *msg_p) +{ + if (msg_p == NULL) return -1; + + memset(msg_p, 0, sizeof(struct can_error_message_t)); + + return 0; +} + +uint8_t can_error_message_error_active_encode(double value) +{ + return (uint8_t)(value); +} + +double can_error_message_error_active_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_error_message_error_active_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint16_t can_error_message_error_module_encode(double value) +{ + return (uint16_t)(value); +} + +double can_error_message_error_module_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_error_message_error_module_is_in_range(uint16_t value) +{ + return (value <= 32767u); +} + +uint16_t can_error_message_error_reason_encode(double value) +{ + return (uint16_t)(value); +} + +double can_error_message_error_reason_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_error_message_error_reason_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +uint16_t can_error_message_error_add_data1_encode(double value) +{ + return (uint16_t)(value); +} + +double can_error_message_error_add_data1_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_error_message_error_add_data1_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +uint16_t can_error_message_error_add_data2_encode(double value) +{ + return (uint16_t)(value); +} + +double can_error_message_error_add_data2_decode(uint16_t value) +{ + return ((double)value); +} + +bool can_error_message_error_add_data2_is_in_range(uint16_t value) +{ + (void)value; + + return (true); +} + +int can_diagnostic_measurements_pack( + uint8_t *dst_p, + const struct can_diagnostic_measurements_t *src_p, + size_t size) +{ + uint16_t dm_cp_voltage_negative_side; + uint16_t dm_cp_voltage_positive_side; + uint16_t dm_pp_voltage; + + if (size < 8u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 8); + + dm_cp_voltage_negative_side = (uint16_t)src_p->dm_cp_voltage_negative_side; + dst_p[0] |= pack_right_shift_u16(dm_cp_voltage_negative_side, 8u, 0xffu); + dst_p[1] |= pack_left_shift_u16(dm_cp_voltage_negative_side, 0u, 0xffu); + dm_cp_voltage_positive_side = (uint16_t)src_p->dm_cp_voltage_positive_side; + dst_p[2] |= pack_right_shift_u16(dm_cp_voltage_positive_side, 8u, 0xffu); + dst_p[3] |= pack_left_shift_u16(dm_cp_voltage_positive_side, 0u, 0xffu); + dm_pp_voltage = (uint16_t)src_p->dm_pp_voltage; + dst_p[4] |= pack_right_shift_u16(dm_pp_voltage, 8u, 0xffu); + dst_p[5] |= pack_left_shift_u16(dm_pp_voltage, 0u, 0xffu); + + return (8); +} + +int can_diagnostic_measurements_unpack( + struct can_diagnostic_measurements_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + uint16_t dm_cp_voltage_negative_side; + uint16_t dm_cp_voltage_positive_side; + uint16_t dm_pp_voltage; + + if (size < 8u) { + return (-EINVAL); + } + + dm_cp_voltage_negative_side = unpack_left_shift_u16(src_p[0], 8u, 0xffu); + dm_cp_voltage_negative_side |= unpack_right_shift_u16(src_p[1], 0u, 0xffu); + dst_p->dm_cp_voltage_negative_side = (int16_t)dm_cp_voltage_negative_side; + dm_cp_voltage_positive_side = unpack_left_shift_u16(src_p[2], 8u, 0xffu); + dm_cp_voltage_positive_side |= unpack_right_shift_u16(src_p[3], 0u, 0xffu); + dst_p->dm_cp_voltage_positive_side = (int16_t)dm_cp_voltage_positive_side; + dm_pp_voltage = unpack_left_shift_u16(src_p[4], 8u, 0xffu); + dm_pp_voltage |= unpack_right_shift_u16(src_p[5], 0u, 0xffu); + dst_p->dm_pp_voltage = (int16_t)dm_pp_voltage; + + return (0); +} + +int can_diagnostic_measurements_init(struct can_diagnostic_measurements_t *msg_p) +{ + if (msg_p == NULL) return -1; + + memset(msg_p, 0, sizeof(struct can_diagnostic_measurements_t)); + + return 0; +} + +int16_t can_diagnostic_measurements_dm_cp_voltage_negative_side_encode(double value) +{ + return (int16_t)(value); +} + +double can_diagnostic_measurements_dm_cp_voltage_negative_side_decode(int16_t value) +{ + return ((double)value); +} + +bool can_diagnostic_measurements_dm_cp_voltage_negative_side_is_in_range(int16_t value) +{ + (void)value; + + return (true); +} + +int16_t can_diagnostic_measurements_dm_cp_voltage_positive_side_encode(double value) +{ + return (int16_t)(value); +} + +double can_diagnostic_measurements_dm_cp_voltage_positive_side_decode(int16_t value) +{ + return ((double)value); +} + +bool can_diagnostic_measurements_dm_cp_voltage_positive_side_is_in_range(int16_t value) +{ + (void)value; + + return (true); +} + +int16_t can_diagnostic_measurements_dm_pp_voltage_encode(double value) +{ + return (int16_t)(value); +} + +double can_diagnostic_measurements_dm_pp_voltage_decode(int16_t value) +{ + return ((double)value); +} + +bool can_diagnostic_measurements_dm_pp_voltage_is_in_range(int16_t value) +{ + (void)value; + + return (true); +} + +int can_pt1000_state_pack( + uint8_t *dst_p, + const struct can_pt1000_state_t *src_p, + size_t size) +{ + uint16_t pt1_temperature; + uint16_t pt2_temperature; + uint16_t pt3_temperature; + uint16_t pt4_temperature; + + if (size < 8u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 8); + + pt1_temperature = (uint16_t)src_p->pt1_temperature; + dst_p[0] |= pack_right_shift_u16(pt1_temperature, 6u, 0xffu); + dst_p[1] |= pack_left_shift_u16(pt1_temperature, 2u, 0xfcu); + dst_p[1] |= pack_left_shift_u8(src_p->pt1_selftest_failed, 1u, 0x02u); + dst_p[1] |= pack_left_shift_u8(src_p->pt1_charging_stopped, 0u, 0x01u); + pt2_temperature = (uint16_t)src_p->pt2_temperature; + dst_p[2] |= pack_right_shift_u16(pt2_temperature, 6u, 0xffu); + dst_p[3] |= pack_left_shift_u16(pt2_temperature, 2u, 0xfcu); + dst_p[3] |= pack_left_shift_u8(src_p->pt2_selftest_failed, 1u, 0x02u); + dst_p[3] |= pack_left_shift_u8(src_p->pt2_charging_stopped, 0u, 0x01u); + pt3_temperature = (uint16_t)src_p->pt3_temperature; + dst_p[4] |= pack_right_shift_u16(pt3_temperature, 6u, 0xffu); + dst_p[5] |= pack_left_shift_u16(pt3_temperature, 2u, 0xfcu); + dst_p[5] |= pack_left_shift_u8(src_p->pt3_selftest_failed, 1u, 0x02u); + dst_p[5] |= pack_left_shift_u8(src_p->pt3_charging_stopped, 0u, 0x01u); + pt4_temperature = (uint16_t)src_p->pt4_temperature; + dst_p[6] |= pack_right_shift_u16(pt4_temperature, 6u, 0xffu); + dst_p[7] |= pack_left_shift_u16(pt4_temperature, 2u, 0xfcu); + dst_p[7] |= pack_left_shift_u8(src_p->pt4_selftest_failed, 1u, 0x02u); + dst_p[7] |= pack_left_shift_u8(src_p->pt4_charging_stopped, 0u, 0x01u); + + return (8); +} + +int can_pt1000_state_unpack( + struct can_pt1000_state_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + uint16_t pt1_temperature; + uint16_t pt2_temperature; + uint16_t pt3_temperature; + uint16_t pt4_temperature; + + if (size < 8u) { + return (-EINVAL); + } + + pt1_temperature = unpack_left_shift_u16(src_p[0], 6u, 0xffu); + pt1_temperature |= unpack_right_shift_u16(src_p[1], 2u, 0xfcu); + + if ((pt1_temperature & (1u << 13)) != 0u) { + pt1_temperature |= 0xc000u; + } + + dst_p->pt1_temperature = (int16_t)pt1_temperature; + dst_p->pt1_selftest_failed = unpack_right_shift_u8(src_p[1], 1u, 0x02u); + dst_p->pt1_charging_stopped = unpack_right_shift_u8(src_p[1], 0u, 0x01u); + pt2_temperature = unpack_left_shift_u16(src_p[2], 6u, 0xffu); + pt2_temperature |= unpack_right_shift_u16(src_p[3], 2u, 0xfcu); + + if ((pt2_temperature & (1u << 13)) != 0u) { + pt2_temperature |= 0xc000u; + } + + dst_p->pt2_temperature = (int16_t)pt2_temperature; + dst_p->pt2_selftest_failed = unpack_right_shift_u8(src_p[3], 1u, 0x02u); + dst_p->pt2_charging_stopped = unpack_right_shift_u8(src_p[3], 0u, 0x01u); + pt3_temperature = unpack_left_shift_u16(src_p[4], 6u, 0xffu); + pt3_temperature |= unpack_right_shift_u16(src_p[5], 2u, 0xfcu); + + if ((pt3_temperature & (1u << 13)) != 0u) { + pt3_temperature |= 0xc000u; + } + + dst_p->pt3_temperature = (int16_t)pt3_temperature; + dst_p->pt3_selftest_failed = unpack_right_shift_u8(src_p[5], 1u, 0x02u); + dst_p->pt3_charging_stopped = unpack_right_shift_u8(src_p[5], 0u, 0x01u); + pt4_temperature = unpack_left_shift_u16(src_p[6], 6u, 0xffu); + pt4_temperature |= unpack_right_shift_u16(src_p[7], 2u, 0xfcu); + + if ((pt4_temperature & (1u << 13)) != 0u) { + pt4_temperature |= 0xc000u; + } + + dst_p->pt4_temperature = (int16_t)pt4_temperature; + dst_p->pt4_selftest_failed = unpack_right_shift_u8(src_p[7], 1u, 0x02u); + dst_p->pt4_charging_stopped = unpack_right_shift_u8(src_p[7], 0u, 0x01u); + + return (0); +} + +int can_pt1000_state_init(struct can_pt1000_state_t *msg_p) +{ + if (msg_p == NULL) return -1; + + memset(msg_p, 0, sizeof(struct can_pt1000_state_t)); + + return 0; +} + +int16_t can_pt1000_state_pt1_temperature_encode(double value) +{ + return (int16_t)(value / 0.1); +} + +double can_pt1000_state_pt1_temperature_decode(int16_t value) +{ + return ((double)value * 0.1); +} + +bool can_pt1000_state_pt1_temperature_is_in_range(int16_t value) +{ + return ((value >= -8192) && (value <= 8191)); +} + +uint8_t can_pt1000_state_pt1_selftest_failed_encode(double value) +{ + return (uint8_t)(value); +} + +double can_pt1000_state_pt1_selftest_failed_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_pt1000_state_pt1_selftest_failed_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_pt1000_state_pt1_charging_stopped_encode(double value) +{ + return (uint8_t)(value); +} + +double can_pt1000_state_pt1_charging_stopped_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_pt1000_state_pt1_charging_stopped_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +int16_t can_pt1000_state_pt2_temperature_encode(double value) +{ + return (int16_t)(value / 0.1); +} + +double can_pt1000_state_pt2_temperature_decode(int16_t value) +{ + return ((double)value * 0.1); +} + +bool can_pt1000_state_pt2_temperature_is_in_range(int16_t value) +{ + return ((value >= -8192) && (value <= 8191)); +} + +uint8_t can_pt1000_state_pt2_selftest_failed_encode(double value) +{ + return (uint8_t)(value); +} + +double can_pt1000_state_pt2_selftest_failed_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_pt1000_state_pt2_selftest_failed_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_pt1000_state_pt2_charging_stopped_encode(double value) +{ + return (uint8_t)(value); +} + +double can_pt1000_state_pt2_charging_stopped_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_pt1000_state_pt2_charging_stopped_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +int16_t can_pt1000_state_pt3_temperature_encode(double value) +{ + return (int16_t)(value / 0.1); +} + +double can_pt1000_state_pt3_temperature_decode(int16_t value) +{ + return ((double)value * 0.1); +} + +bool can_pt1000_state_pt3_temperature_is_in_range(int16_t value) +{ + return ((value >= -8192) && (value <= 8191)); +} + +uint8_t can_pt1000_state_pt3_selftest_failed_encode(double value) +{ + return (uint8_t)(value); +} + +double can_pt1000_state_pt3_selftest_failed_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_pt1000_state_pt3_selftest_failed_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_pt1000_state_pt3_charging_stopped_encode(double value) +{ + return (uint8_t)(value); +} + +double can_pt1000_state_pt3_charging_stopped_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_pt1000_state_pt3_charging_stopped_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +int16_t can_pt1000_state_pt4_temperature_encode(double value) +{ + return (int16_t)(value / 0.1); +} + +double can_pt1000_state_pt4_temperature_decode(int16_t value) +{ + return ((double)value * 0.1); +} + +bool can_pt1000_state_pt4_temperature_is_in_range(int16_t value) +{ + return ((value >= -8192) && (value <= 8191)); +} + +uint8_t can_pt1000_state_pt4_selftest_failed_encode(double value) +{ + return (uint8_t)(value); +} + +double can_pt1000_state_pt4_selftest_failed_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_pt1000_state_pt4_selftest_failed_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_pt1000_state_pt4_charging_stopped_encode(double value) +{ + return (uint8_t)(value); +} + +double can_pt1000_state_pt4_charging_stopped_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_pt1000_state_pt4_charging_stopped_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +int can_digital_input_pack( + uint8_t *dst_p, + const struct can_digital_input_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + memset(&dst_p[0], 0, 8); + + dst_p[0] |= pack_left_shift_u8(src_p->di_safety_k1_in, 7u, 0x80u); + dst_p[0] |= pack_left_shift_u8(src_p->di_safety_motor_out1, 6u, 0x40u); + dst_p[0] |= pack_left_shift_u8(src_p->di_safety_motor_out2, 5u, 0x20u); + dst_p[0] |= pack_left_shift_u8(src_p->di_safety_motor_fault, 4u, 0x10u); + dst_p[0] |= pack_left_shift_u8(src_p->di_cp_invert, 3u, 0x08u); + dst_p[0] |= pack_left_shift_u8(src_p->di_ev_cp_edge, 2u, 0x04u); + dst_p[0] |= pack_left_shift_u8(src_p->di_safety_dev_2, 1u, 0x02u); + dst_p[0] |= pack_left_shift_u8(src_p->di_safety_dev_1, 0u, 0x01u); + dst_p[1] |= pack_left_shift_u8(src_p->di_pp_sae_iec, 7u, 0x80u); + dst_p[1] |= pack_left_shift_u8(src_p->di_safety_hvsw1_hs, 6u, 0x40u); + dst_p[1] |= pack_left_shift_u8(src_p->di_safety_hvsw2_hs, 5u, 0x20u); + dst_p[1] |= pack_left_shift_u8(src_p->di_safety_gpio_exp, 4u, 0x10u); + dst_p[1] |= pack_left_shift_u8(src_p->di_safety_imd_od_en, 3u, 0x08u); + dst_p[1] |= pack_left_shift_u8(src_p->di_pt4_en, 2u, 0x04u); + dst_p[1] |= pack_left_shift_u8(src_p->di_safety_hvsw3_precharge, 1u, 0x02u); + dst_p[1] |= pack_left_shift_u8(src_p->di_safety_k2_in, 0u, 0x01u); + dst_p[2] |= pack_left_shift_u8(src_p->di_pt1_en, 6u, 0x40u); + dst_p[2] |= pack_left_shift_u8(src_p->di_pt2_en, 5u, 0x20u); + dst_p[2] |= pack_left_shift_u8(src_p->di_pt3_en, 4u, 0x10u); + dst_p[2] |= pack_left_shift_u8(src_p->di_safety_cp_state_c, 3u, 0x08u); + dst_p[2] |= pack_left_shift_u8(src_p->di_estop1, 2u, 0x04u); + dst_p[2] |= pack_left_shift_u8(src_p->di_estop2, 1u, 0x02u); + dst_p[2] |= pack_left_shift_u8(src_p->di_estop3, 0u, 0x01u); + + return (8); +} + +int can_digital_input_unpack( + struct can_digital_input_t *dst_p, + const uint8_t *src_p, + size_t size) +{ + if (size < 8u) { + return (-EINVAL); + } + + dst_p->di_safety_k1_in = unpack_right_shift_u8(src_p[0], 7u, 0x80u); + dst_p->di_safety_motor_out1 = unpack_right_shift_u8(src_p[0], 6u, 0x40u); + dst_p->di_safety_motor_out2 = unpack_right_shift_u8(src_p[0], 5u, 0x20u); + dst_p->di_safety_motor_fault = unpack_right_shift_u8(src_p[0], 4u, 0x10u); + dst_p->di_cp_invert = unpack_right_shift_u8(src_p[0], 3u, 0x08u); + dst_p->di_ev_cp_edge = unpack_right_shift_u8(src_p[0], 2u, 0x04u); + dst_p->di_safety_dev_2 = unpack_right_shift_u8(src_p[0], 1u, 0x02u); + dst_p->di_safety_dev_1 = unpack_right_shift_u8(src_p[0], 0u, 0x01u); + dst_p->di_pp_sae_iec = unpack_right_shift_u8(src_p[1], 7u, 0x80u); + dst_p->di_safety_hvsw1_hs = unpack_right_shift_u8(src_p[1], 6u, 0x40u); + dst_p->di_safety_hvsw2_hs = unpack_right_shift_u8(src_p[1], 5u, 0x20u); + dst_p->di_safety_gpio_exp = unpack_right_shift_u8(src_p[1], 4u, 0x10u); + dst_p->di_safety_imd_od_en = unpack_right_shift_u8(src_p[1], 3u, 0x08u); + dst_p->di_pt4_en = unpack_right_shift_u8(src_p[1], 2u, 0x04u); + dst_p->di_safety_hvsw3_precharge = unpack_right_shift_u8(src_p[1], 1u, 0x02u); + dst_p->di_safety_k2_in = unpack_right_shift_u8(src_p[1], 0u, 0x01u); + dst_p->di_pt1_en = unpack_right_shift_u8(src_p[2], 6u, 0x40u); + dst_p->di_pt2_en = unpack_right_shift_u8(src_p[2], 5u, 0x20u); + dst_p->di_pt3_en = unpack_right_shift_u8(src_p[2], 4u, 0x10u); + dst_p->di_safety_cp_state_c = unpack_right_shift_u8(src_p[2], 3u, 0x08u); + dst_p->di_estop1 = unpack_right_shift_u8(src_p[2], 2u, 0x04u); + dst_p->di_estop2 = unpack_right_shift_u8(src_p[2], 1u, 0x02u); + dst_p->di_estop3 = unpack_right_shift_u8(src_p[2], 0u, 0x01u); + + return (0); +} + +int can_digital_input_init(struct can_digital_input_t *msg_p) +{ + if (msg_p == NULL) return -1; + + memset(msg_p, 0, sizeof(struct can_digital_input_t)); + + return 0; +} + +uint8_t can_digital_input_di_safety_k1_in_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_safety_k1_in_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_safety_k1_in_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_safety_motor_out1_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_safety_motor_out1_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_safety_motor_out1_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_safety_motor_out2_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_safety_motor_out2_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_safety_motor_out2_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_safety_motor_fault_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_safety_motor_fault_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_safety_motor_fault_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_cp_invert_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_cp_invert_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_cp_invert_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_ev_cp_edge_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_ev_cp_edge_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_ev_cp_edge_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_safety_dev_2_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_safety_dev_2_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_safety_dev_2_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_safety_dev_1_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_safety_dev_1_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_safety_dev_1_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_pp_sae_iec_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_pp_sae_iec_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_pp_sae_iec_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_safety_hvsw1_hs_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_safety_hvsw1_hs_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_safety_hvsw1_hs_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_safety_hvsw2_hs_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_safety_hvsw2_hs_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_safety_hvsw2_hs_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_safety_gpio_exp_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_safety_gpio_exp_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_safety_gpio_exp_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_safety_imd_od_en_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_safety_imd_od_en_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_safety_imd_od_en_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_pt4_en_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_pt4_en_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_pt4_en_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_safety_hvsw3_precharge_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_safety_hvsw3_precharge_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_safety_hvsw3_precharge_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_safety_k2_in_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_safety_k2_in_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_safety_k2_in_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_pt1_en_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_pt1_en_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_pt1_en_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_pt2_en_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_pt2_en_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_pt2_en_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_pt3_en_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_pt3_en_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_pt3_en_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_safety_cp_state_c_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_safety_cp_state_c_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_safety_cp_state_c_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_estop1_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_estop1_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_estop1_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_estop2_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_estop2_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_estop2_is_in_range(uint8_t value) +{ + return (value <= 1u); +} + +uint8_t can_digital_input_di_estop3_encode(double value) +{ + return (uint8_t)(value); +} + +double can_digital_input_di_estop3_decode(uint8_t value) +{ + return ((double)value); +} + +bool can_digital_input_di_estop3_is_in_range(uint8_t value) +{ + return (value <= 1u); +} diff --git a/modules/CbCPXDriver/cpx/can_interface/can.h b/modules/CbCPXDriver/cpx/can_interface/can.h new file mode 100644 index 0000000..bd0a163 --- /dev/null +++ b/modules/CbCPXDriver/cpx/can_interface/can.h @@ -0,0 +1,4727 @@ +/** + * @file CAN.h + * + * @brief This header file was generated by cantools version 40.2.2 Wed Jun 18 15:48:26 2025. + * + * @copyright Copyright (c) 2018-2019 Erik Moqvist + * + * @par License + * The MIT License (MIT) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CAN_H +#define CAN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#ifndef EINVAL +# define EINVAL 22 +#endif + +/* Frame ids. */ +#define CAN_INQUIRY_PACKET_FRAME_ID (0xffu) +#define CAN_DIGITAL_OUTPUT_FRAME_ID (0x00u) +#define CAN_DIAGNOSTIC_MEASUREMENTS2_FRAME_ID (0x12u) +#define CAN_ANALOG_INPUT04_FRAME_ID (0x05u) +#define CAN_ANALOG_INPUT03_FRAME_ID (0x04u) +#define CAN_ANALOG_INPUT02_FRAME_ID (0x03u) +#define CAN_ANALOG_INPUT01_FRAME_ID (0x02u) +#define CAN_CHARGE_CONTROL2_FRAME_ID (0x11u) +#define CAN_CHARGE_STATE2_FRAME_ID (0x10u) +#define CAN_CHARGE_STATE1_FRAME_ID (0x07u) +#define CAN_CHARGE_CONTROL1_FRAME_ID (0x06u) +#define CAN_FIRMWARE_VERSION_FRAME_ID (0x0au) +#define CAN_GIT_HASH_FRAME_ID (0x0bu) +#define CAN_ERROR_MESSAGE_FRAME_ID (0x0eu) +#define CAN_DIAGNOSTIC_MEASUREMENTS_FRAME_ID (0x09u) +#define CAN_PT1000_STATE_FRAME_ID (0x08u) +#define CAN_DIGITAL_INPUT_FRAME_ID (0x01u) + +/* Frame lengths in bytes. */ +#define CAN_INQUIRY_PACKET_LENGTH (8u) +#define CAN_DIGITAL_OUTPUT_LENGTH (8u) +#define CAN_DIAGNOSTIC_MEASUREMENTS2_LENGTH (8u) +#define CAN_ANALOG_INPUT04_LENGTH (8u) +#define CAN_ANALOG_INPUT03_LENGTH (8u) +#define CAN_ANALOG_INPUT02_LENGTH (8u) +#define CAN_ANALOG_INPUT01_LENGTH (8u) +#define CAN_CHARGE_CONTROL2_LENGTH (8u) +#define CAN_CHARGE_STATE2_LENGTH (8u) +#define CAN_CHARGE_STATE1_LENGTH (8u) +#define CAN_CHARGE_CONTROL1_LENGTH (8u) +#define CAN_FIRMWARE_VERSION_LENGTH (8u) +#define CAN_GIT_HASH_LENGTH (8u) +#define CAN_ERROR_MESSAGE_LENGTH (8u) +#define CAN_DIAGNOSTIC_MEASUREMENTS_LENGTH (8u) +#define CAN_PT1000_STATE_LENGTH (8u) +#define CAN_DIGITAL_INPUT_LENGTH (8u) + +/* Extended or standard frame types. */ +#define CAN_INQUIRY_PACKET_IS_EXTENDED (0) +#define CAN_DIGITAL_OUTPUT_IS_EXTENDED (0) +#define CAN_DIAGNOSTIC_MEASUREMENTS2_IS_EXTENDED (0) +#define CAN_ANALOG_INPUT04_IS_EXTENDED (0) +#define CAN_ANALOG_INPUT03_IS_EXTENDED (0) +#define CAN_ANALOG_INPUT02_IS_EXTENDED (0) +#define CAN_ANALOG_INPUT01_IS_EXTENDED (0) +#define CAN_CHARGE_CONTROL2_IS_EXTENDED (0) +#define CAN_CHARGE_STATE2_IS_EXTENDED (0) +#define CAN_CHARGE_STATE1_IS_EXTENDED (0) +#define CAN_CHARGE_CONTROL1_IS_EXTENDED (0) +#define CAN_FIRMWARE_VERSION_IS_EXTENDED (0) +#define CAN_GIT_HASH_IS_EXTENDED (0) +#define CAN_ERROR_MESSAGE_IS_EXTENDED (0) +#define CAN_DIAGNOSTIC_MEASUREMENTS_IS_EXTENDED (0) +#define CAN_PT1000_STATE_IS_EXTENDED (0) +#define CAN_DIGITAL_INPUT_IS_EXTENDED (0) + +/* Frame cycle times in milliseconds. */ + + +/* Signal choices. */ +#define CAN_INQUIRY_PACKET_PACKET_ID_FIRMWARE_VERSION_CHOICE (10u) +#define CAN_INQUIRY_PACKET_PACKET_ID_GIT_HASH_CHOICE (11u) + +#define CAN_CHARGE_CONTROL2_CC_CCS_READY_CCS_NOT_READY_CHOICE (0u) +#define CAN_CHARGE_CONTROL2_CC_CCS_READY_CCS_READY_CHOICE (1u) +#define CAN_CHARGE_CONTROL2_CC_CCS_READY_CCS_EMERGENCY_STOP_CHOICE (2u) + +#define CAN_CHARGE_STATE2_CS_CE_STATE_A_CHOICE (0u) +#define CAN_CHARGE_STATE2_CS_CE_STATE_B_CHOICE (1u) +#define CAN_CHARGE_STATE2_CS_CE_STATE_C_CHOICE (2u) +#define CAN_CHARGE_STATE2_CS_CE_STATE_D_CHOICE (3u) +#define CAN_CHARGE_STATE2_CS_CE_STATE_E_CHOICE (4u) +#define CAN_CHARGE_STATE2_CS_CE_STATE_EC_CHOICE (5u) +#define CAN_CHARGE_STATE2_CS_CE_STATE_B0_CHOICE (6u) +#define CAN_CHARGE_STATE2_CS_CE_STATE_INVALID_CHOICE (7u) +#define CAN_CHARGE_STATE2_CS_CE_STATE_SNA_CHOICE (15u) + +#define CAN_CHARGE_STATE2_CS_ID_STATE_NOT_CONNECTED_CHOICE (0u) +#define CAN_CHARGE_STATE2_CS_ID_STATE_CONNECTED_CHOICE (1u) +#define CAN_CHARGE_STATE2_CS_ID_STATE_INVALID_CHOICE (14u) +#define CAN_CHARGE_STATE2_CS_ID_STATE_SNA_CHOICE (15u) + +#define CAN_CHARGE_STATE2_CS_E_STOP_REASON_NO_STOP_CHOICE (0u) +#define CAN_CHARGE_STATE2_CS_E_STOP_REASON_EMERGENCY_INPUT_CHOICE (1u) +#define CAN_CHARGE_STATE2_CS_E_STOP_REASON_COM_TIMEOUT_CHOICE (2u) +#define CAN_CHARGE_STATE2_CS_E_STOP_REASON_TEMP1_MALFUNCTION_CHOICE (3u) +#define CAN_CHARGE_STATE2_CS_E_STOP_REASON_TEMP2_MALFUNCTION_CHOICE (4u) +#define CAN_CHARGE_STATE2_CS_E_STOP_REASON_TEMP3_MALFUNCTION_CHOICE (5u) +#define CAN_CHARGE_STATE2_CS_E_STOP_REASON_TEMP4_MALFUNCTION_CHOICE (6u) +#define CAN_CHARGE_STATE2_CS_E_STOP_REASON_TEMP1_OVERTEMP_CHOICE (7u) +#define CAN_CHARGE_STATE2_CS_E_STOP_REASON_TEMP2_OVERTEMP_CHOICE (8u) +#define CAN_CHARGE_STATE2_CS_E_STOP_REASON_TEMP3_OVERTEMP_CHOICE (9u) +#define CAN_CHARGE_STATE2_CS_E_STOP_REASON_TEMP4_OVERTEMP_CHOICE (10u) +#define CAN_CHARGE_STATE2_CS_E_STOP_REASON_ID_MALFUNCTION_CHOICE (11u) +#define CAN_CHARGE_STATE2_CS_E_STOP_REASON_CE_MALFUNCTION_CHOICE (12u) +#define CAN_CHARGE_STATE2_CS_E_STOP_REASON_HV_READY_MALFUNCTION_CHOICE (13u) + +#define CAN_CHARGE_STATE1_CS_CURRENT_CP_STATE_UNKNOWN_CHOICE (0u) +#define CAN_CHARGE_STATE1_CS_CURRENT_CP_STATE_A_CHOICE (1u) +#define CAN_CHARGE_STATE1_CS_CURRENT_CP_STATE_B_CHOICE (2u) +#define CAN_CHARGE_STATE1_CS_CURRENT_CP_STATE_C_CHOICE (3u) +#define CAN_CHARGE_STATE1_CS_CURRENT_CP_STATE_D_CHOICE (4u) +#define CAN_CHARGE_STATE1_CS_CURRENT_CP_STATE_E_CHOICE (5u) +#define CAN_CHARGE_STATE1_CS_CURRENT_CP_STATE_F_CHOICE (6u) +#define CAN_CHARGE_STATE1_CS_CURRENT_CP_STATE_INVALID_CHOICE (7u) + +#define CAN_CHARGE_STATE1_CS_CURRENT_PP_STATE_NO_CABLE_DETECTED_CHOICE (0u) +#define CAN_CHARGE_STATE1_CS_CURRENT_PP_STATE_13_A_CHOICE (1u) +#define CAN_CHARGE_STATE1_CS_CURRENT_PP_STATE_20_A_CHOICE (2u) +#define CAN_CHARGE_STATE1_CS_CURRENT_PP_STATE_32_A_CHOICE (3u) +#define CAN_CHARGE_STATE1_CS_CURRENT_PP_STATE_63_70_A_CHOICE (4u) +#define CAN_CHARGE_STATE1_CS_CURRENT_PP_STATE_TYPE1_CONNECTED_CHOICE (5u) +#define CAN_CHARGE_STATE1_CS_CURRENT_PP_STATE_TYPE1_CONNECTED_BUTTON_PRESSED_CHOICE (6u) +#define CAN_CHARGE_STATE1_CS_CURRENT_PP_STATE_ERROR_CHOICE (7u) + +#define CAN_CHARGE_STATE1_CS_CONTACTOR2_STATE_OPEN_CHOICE (0u) +#define CAN_CHARGE_STATE1_CS_CONTACTOR2_STATE_CLOSE_CHOICE (1u) +#define CAN_CHARGE_STATE1_CS_CONTACTOR2_STATE_NOT_CONFIGURED_CHOICE (3u) + +#define CAN_CHARGE_STATE1_CS_CONTACTOR1_STATE_OPEN_CHOICE (0u) +#define CAN_CHARGE_STATE1_CS_CONTACTOR1_STATE_CLOSE_CHOICE (1u) +#define CAN_CHARGE_STATE1_CS_CONTACTOR1_STATE_NOT_CONFIGURED_CHOICE (3u) + +#define CAN_CHARGE_STATE1_CS_ESTOP3_CHARGING_ABORT_FALSE_CHOICE (0u) +#define CAN_CHARGE_STATE1_CS_ESTOP3_CHARGING_ABORT_TRUE_CHOICE (1u) +#define CAN_CHARGE_STATE1_CS_ESTOP3_CHARGING_ABORT_NOT_CONFIGURED_CHOICE (3u) + +#define CAN_CHARGE_STATE1_CS_ESTOP2_CHARGING_ABORT_FALSE_CHOICE (0u) +#define CAN_CHARGE_STATE1_CS_ESTOP2_CHARGING_ABORT_TRUE_CHOICE (1u) +#define CAN_CHARGE_STATE1_CS_ESTOP2_CHARGING_ABORT_NOT_CONFIGURED_CHOICE (3u) + +#define CAN_CHARGE_STATE1_CS_ESTOP1_CHARGING_ABORT_FALSE_CHOICE (0u) +#define CAN_CHARGE_STATE1_CS_ESTOP1_CHARGING_ABORT_TRUE_CHOICE (1u) +#define CAN_CHARGE_STATE1_CS_ESTOP1_CHARGING_ABORT_NOT_CONFIGURED_CHOICE (3u) + +#define CAN_FIRMWARE_VERSION_PLATFORM_TYPE_CHARGE_SOM_CHOICE (129u) +#define CAN_FIRMWARE_VERSION_PLATFORM_TYPE_CCY_CHOICE (130u) + +#define CAN_FIRMWARE_VERSION_APPLICATION_TYPE_FIRMWARE_CHOICE (3u) +#define CAN_FIRMWARE_VERSION_APPLICATION_TYPE_END__OF__LINE_CHOICE (4u) +#define CAN_FIRMWARE_VERSION_APPLICATION_TYPE_QUALIFICATION_CHOICE (5u) + +#define CAN_PT1000_STATE_PT1_TEMPERATURE_TEMP_SENSOR_NOT_USED_CHOICE (8191) + +#define CAN_PT1000_STATE_PT2_TEMPERATURE_TEMP_SENSOR_NOT_USED_CHOICE (8191) + +#define CAN_PT1000_STATE_PT3_TEMPERATURE_TEMP_SENSOR_NOT_USED_CHOICE (8191) + +#define CAN_PT1000_STATE_PT4_TEMPERATURE_TEMP_SENSOR_NOT_USED_CHOICE (8191) + +/* Frame Names. */ +#define CAN_INQUIRY_PACKET_NAME "InquiryPacket" +#define CAN_DIGITAL_OUTPUT_NAME "DigitalOutput" +#define CAN_DIAGNOSTIC_MEASUREMENTS2_NAME "DiagnosticMeasurements2" +#define CAN_ANALOG_INPUT04_NAME "AnalogInput04" +#define CAN_ANALOG_INPUT03_NAME "AnalogInput03" +#define CAN_ANALOG_INPUT02_NAME "AnalogInput02" +#define CAN_ANALOG_INPUT01_NAME "AnalogInput01" +#define CAN_CHARGE_CONTROL2_NAME "ChargeControl2" +#define CAN_CHARGE_STATE2_NAME "ChargeState2" +#define CAN_CHARGE_STATE1_NAME "ChargeState1" +#define CAN_CHARGE_CONTROL1_NAME "ChargeControl1" +#define CAN_FIRMWARE_VERSION_NAME "FirmwareVersion" +#define CAN_GIT_HASH_NAME "GitHash" +#define CAN_ERROR_MESSAGE_NAME "ErrorMessage" +#define CAN_DIAGNOSTIC_MEASUREMENTS_NAME "DiagnosticMeasurements" +#define CAN_PT1000_STATE_NAME "PT1000State" +#define CAN_DIGITAL_INPUT_NAME "DigitalInput" + +/* Signal Names. */ +#define CAN_INQUIRY_PACKET_PACKET_ID_NAME "PacketId" +#define CAN_DIGITAL_OUTPUT_DO_SAFETY_MOTOR_OUT1_NAME "DO_SAFETY_MOTOR_OUT1" +#define CAN_DIGITAL_OUTPUT_DO_SAFETY_MOTOR_OUT2_NAME "DO_SAFETY_MOTOR_OUT2" +#define CAN_DIGITAL_OUTPUT_DO_CP_INVERT_NAME "DO_CP_INVERT" +#define CAN_DIGITAL_OUTPUT_DO_PP_SAE_IEC_NAME "DO_PP_SAE_IEC" +#define CAN_DIGITAL_OUTPUT_DO_SAFETY_HVSW1_HS_NAME "DO_SAFETY_HVSW1_HS" +#define CAN_DIGITAL_OUTPUT_DO_SAFETY_HVSW2_HS_NAME "DO_SAFETY_HVSW2_HS" +#define CAN_DIGITAL_OUTPUT_DO_SAFETY_GPIO_EXP_NAME "DO_SAFETY_GPIO_EXP" +#define CAN_DIGITAL_OUTPUT_DO_SAFETY_IMD_OD_EN_NAME "DO_SAFETY_IMD_OD_EN" +#define CAN_DIGITAL_OUTPUT_DO_PT1000_CTRLEN4_NAME "DO_PT1000_CTRLEN4" +#define CAN_DIGITAL_OUTPUT_DO_SAFETY_HVSW3_PRECHARGE_NAME "DO_SAFETY_HVSW3_PRECHARGE" +#define CAN_DIGITAL_OUTPUT_DO_PT1000_CTRLEN1_NAME "DO_PT1000_CTRLEN1" +#define CAN_DIGITAL_OUTPUT_DO_PT1000_CTRLEN2_NAME "DO_PT1000_CTRLEN2" +#define CAN_DIGITAL_OUTPUT_DO_PT1000_CTRLEN3_NAME "DO_PT1000_CTRLEN3" +#define CAN_DIGITAL_OUTPUT_DO_SAFETY_CP_STATE_C_NAME "DO_SAFETY_CP_STATE_C" +#define CAN_DIGITAL_OUTPUT_DO_CP_DUTYCYCLE_NAME "DO_CP_Dutycycle" +#define CAN_DIAGNOSTIC_MEASUREMENTS2_DM_CE_ADC_VAL_NAME "DM_CE_ADC_VAL" +#define CAN_DIAGNOSTIC_MEASUREMENTS2_DM_ID_ADC_VAL_NAME "DM_ID_ADC_VAL" +#define CAN_ANALOG_INPUT04_AI_PT1000_CFB3_4_NAME "AI_PT1000_CFB3_4" +#define CAN_ANALOG_INPUT04_AI_INT_TEMP_NAME "AI_INT_TEMP" +#define CAN_ANALOG_INPUT04_AI_INT_REFVOLT_NAME "AI_INT_REFVOLT" +#define CAN_ANALOG_INPUT03_AI_SAFETY_PRECHARGE_CFB_NAME "AI_SAFETY_PRECHARGE_CFB" +#define CAN_ANALOG_INPUT03_AI_SAFETY_HS1_CFB_NAME "AI_SAFETY_HS1_CFB" +#define CAN_ANALOG_INPUT03_AI_SAFETY_HS2_CFB_NAME "AI_SAFETY_HS2_CFB" +#define CAN_ANALOG_INPUT03_AI_PT1000_CFB1_2_NAME "AI_PT1000_CFB1_2" +#define CAN_ANALOG_INPUT02_AI_CP_BUFFERED_NEG_ADC_NAME "AI_CP_BUFFERED_NEG_ADC" +#define CAN_ANALOG_INPUT02_AI_CP_BUFFERED_POS_ADC_NAME "AI_CP_BUFFERED_POS_ADC" +#define CAN_ANALOG_INPUT02_AI_PP_VALUE_ADC_NAME "AI_PP_VALUE_ADC" +#define CAN_ANALOG_INPUT02_AI_SAFETY_U_IN_ADC_NAME "AI_SAFETY_U_IN_ADC" +#define CAN_ANALOG_INPUT01_AI_PT1000_LEAD1_1_NAME "AI_PT1000_LEAD1_1" +#define CAN_ANALOG_INPUT01_AI_PT1000_LEAD1_2_NAME "AI_PT1000_LEAD1_2" +#define CAN_ANALOG_INPUT01_AI_PT1000_LEAD1_3_NAME "AI_PT1000_LEAD1_3" +#define CAN_ANALOG_INPUT01_AI_PT1000_LEAD1_4_NAME "AI_PT1000_LEAD1_4" +#define CAN_CHARGE_CONTROL2_CC_CCS_READY_NAME "CC_CCSReady" +#define CAN_CHARGE_CONTROL2_CC_CONTROLLER_RESET_NAME "CC_ControllerReset" +#define CAN_CHARGE_STATE2_CS_CE_STATE_NAME "CS_CE_State" +#define CAN_CHARGE_STATE2_CS_ID_STATE_NAME "CS_ID_State" +#define CAN_CHARGE_STATE2_CS_E_STOP_REASON_NAME "CS_EStop_Reason" +#define CAN_CHARGE_STATE1_CS_PWM_ACTIVE_NAME "CS_PWM_Active" +#define CAN_CHARGE_STATE1_CS_CURRENT_DUTY_CYCLE_NAME "CS_CurrentDutyCycle" +#define CAN_CHARGE_STATE1_CS_DIODE_FAULT_NAME "CS_DiodeFault" +#define CAN_CHARGE_STATE1_CS_CP_SHORT_CIRCUIT_NAME "CS_CpShortCircuit" +#define CAN_CHARGE_STATE1_CS_CURRENT_CP_STATE_NAME "CS_CurrentCpState" +#define CAN_CHARGE_STATE1_CS_CURRENT_PP_STATE_NAME "CS_CurrentPpState" +#define CAN_CHARGE_STATE1_CS_HV_READY_NAME "CS_HV_Ready" +#define CAN_CHARGE_STATE1_CS_CONTACTOR2_ERROR_NAME "CS_Contactor2Error" +#define CAN_CHARGE_STATE1_CS_CONTACTOR2_STATE_NAME "CS_Contactor2State" +#define CAN_CHARGE_STATE1_CS_CONTACTOR1_ERROR_NAME "CS_Contactor1Error" +#define CAN_CHARGE_STATE1_CS_CONTACTOR1_STATE_NAME "CS_Contactor1State" +#define CAN_CHARGE_STATE1_CS_ESTOP3_CHARGING_ABORT_NAME "CS_Estop3ChargingAbort" +#define CAN_CHARGE_STATE1_CS_ESTOP2_CHARGING_ABORT_NAME "CS_Estop2ChargingAbort" +#define CAN_CHARGE_STATE1_CS_ESTOP1_CHARGING_ABORT_NAME "CS_Estop1ChargingAbort" +#define CAN_CHARGE_CONTROL1_CC_PWM_ACTIVE_NAME "CC_PWM_Active" +#define CAN_CHARGE_CONTROL1_CC_TARGET_DUTY_CYCLE_NAME "CC_TargetDutyCycle" +#define CAN_CHARGE_CONTROL1_CC_CONTACTOR2_STATE_NAME "CC_Contactor2State" +#define CAN_CHARGE_CONTROL1_CC_CONTACTOR1_STATE_NAME "CC_Contactor1State" +#define CAN_FIRMWARE_VERSION_MAJOR_VERSION_NAME "MajorVersion" +#define CAN_FIRMWARE_VERSION_MINOR_VERSION_NAME "MinorVersion" +#define CAN_FIRMWARE_VERSION_BUILD_VERSION_NAME "BuildVersion" +#define CAN_FIRMWARE_VERSION_PLATFORM_TYPE_NAME "PlatformType" +#define CAN_FIRMWARE_VERSION_APPLICATION_TYPE_NAME "ApplicationType" +#define CAN_GIT_HASH_HASH_SIGNAL_NAME "HashSignal" +#define CAN_ERROR_MESSAGE_ERROR_ACTIVE_NAME "ErrorActive" +#define CAN_ERROR_MESSAGE_ERROR_MODULE_NAME "ErrorModule" +#define CAN_ERROR_MESSAGE_ERROR_REASON_NAME "ErrorReason" +#define CAN_ERROR_MESSAGE_ERROR_ADD_DATA1_NAME "ErrorAddData1" +#define CAN_ERROR_MESSAGE_ERROR_ADD_DATA2_NAME "ErrorAddData2" +#define CAN_DIAGNOSTIC_MEASUREMENTS_DM_CP_VOLTAGE_NEGATIVE_SIDE_NAME "DM_CP_VoltageNegativeSide" +#define CAN_DIAGNOSTIC_MEASUREMENTS_DM_CP_VOLTAGE_POSITIVE_SIDE_NAME "DM_CP_VoltagePositiveSide" +#define CAN_DIAGNOSTIC_MEASUREMENTS_DM_PP_VOLTAGE_NAME "DM_PP_Voltage" +#define CAN_PT1000_STATE_PT1_TEMPERATURE_NAME "PT1_Temperature" +#define CAN_PT1000_STATE_PT1_SELFTEST_FAILED_NAME "PT1_SelftestFailed" +#define CAN_PT1000_STATE_PT1_CHARGING_STOPPED_NAME "PT1_ChargingStopped" +#define CAN_PT1000_STATE_PT2_TEMPERATURE_NAME "PT2_Temperature" +#define CAN_PT1000_STATE_PT2_SELFTEST_FAILED_NAME "PT2_SelftestFailed" +#define CAN_PT1000_STATE_PT2_CHARGING_STOPPED_NAME "PT2_ChargingStopped" +#define CAN_PT1000_STATE_PT3_TEMPERATURE_NAME "PT3_Temperature" +#define CAN_PT1000_STATE_PT3_SELFTEST_FAILED_NAME "PT3_SelftestFailed" +#define CAN_PT1000_STATE_PT3_CHARGING_STOPPED_NAME "PT3_ChargingStopped" +#define CAN_PT1000_STATE_PT4_TEMPERATURE_NAME "PT4_Temperature" +#define CAN_PT1000_STATE_PT4_SELFTEST_FAILED_NAME "PT4_SelftestFailed" +#define CAN_PT1000_STATE_PT4_CHARGING_STOPPED_NAME "PT4_ChargingStopped" +#define CAN_DIGITAL_INPUT_DI_SAFETY_K1_IN_NAME "DI_SAFETY_K1_IN" +#define CAN_DIGITAL_INPUT_DI_SAFETY_MOTOR_OUT1_NAME "DI_SAFETY_MOTOR_OUT1" +#define CAN_DIGITAL_INPUT_DI_SAFETY_MOTOR_OUT2_NAME "DI_SAFETY_MOTOR_OUT2" +#define CAN_DIGITAL_INPUT_DI_SAFETY_MOTOR_FAULT_NAME "DI_SAFETY_MOTOR_FAULT" +#define CAN_DIGITAL_INPUT_DI_CP_INVERT_NAME "DI_CP_INVERT" +#define CAN_DIGITAL_INPUT_DI_EV_CP_EDGE_NAME "DI_EV_CP_EDGE" +#define CAN_DIGITAL_INPUT_DI_SAFETY_DEV_2_NAME "DI_SAFETY_DEV_2" +#define CAN_DIGITAL_INPUT_DI_SAFETY_DEV_1_NAME "DI_SAFETY_DEV_1" +#define CAN_DIGITAL_INPUT_DI_PP_SAE_IEC_NAME "DI_PP_SAE_IEC" +#define CAN_DIGITAL_INPUT_DI_SAFETY_HVSW1_HS_NAME "DI_SAFETY_HVSW1_HS" +#define CAN_DIGITAL_INPUT_DI_SAFETY_HVSW2_HS_NAME "DI_SAFETY_HVSW2_HS" +#define CAN_DIGITAL_INPUT_DI_SAFETY_GPIO_EXP_NAME "DI_SAFETY_GPIO_EXP" +#define CAN_DIGITAL_INPUT_DI_SAFETY_IMD_OD_EN_NAME "DI_SAFETY_IMD_OD_EN" +#define CAN_DIGITAL_INPUT_DI_PT4_EN_NAME "DI_PT4_EN" +#define CAN_DIGITAL_INPUT_DI_SAFETY_HVSW3_PRECHARGE_NAME "DI_SAFETY_HVSW3_PRECHARGE" +#define CAN_DIGITAL_INPUT_DI_SAFETY_K2_IN_NAME "DI_SAFETY_K2_IN" +#define CAN_DIGITAL_INPUT_DI_PT1_EN_NAME "DI_PT1_EN" +#define CAN_DIGITAL_INPUT_DI_PT2_EN_NAME "DI_PT2_EN" +#define CAN_DIGITAL_INPUT_DI_PT3_EN_NAME "DI_PT3_EN" +#define CAN_DIGITAL_INPUT_DI_SAFETY_CP_STATE_C_NAME "DI_SAFETY_CP_STATE_C" +#define CAN_DIGITAL_INPUT_DI_ESTOP1_NAME "DI_ESTOP1" +#define CAN_DIGITAL_INPUT_DI_ESTOP2_NAME "DI_ESTOP2" +#define CAN_DIGITAL_INPUT_DI_ESTOP3_NAME "DI_ESTOP3" + +/** + * Signals in message InquiryPacket. + * + * This packet is used to request a special message from the safety controller + * + * All signal values are as on the CAN bus. + */ +struct can_inquiry_packet_t { + /** + * The ID, which message shall be requested. Supported values are described below. + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t packet_id; +}; + +/** + * Signals in message DigitalOutput. + * + * All signal values are as on the CAN bus. + */ +struct can_digital_output_t { + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t do_safety_motor_out1; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t do_safety_motor_out2; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t do_cp_invert; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t do_pp_sae_iec; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t do_safety_hvsw1_hs; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t do_safety_hvsw2_hs; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t do_safety_gpio_exp; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t do_safety_imd_od_en; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t do_pt1000_ctrlen4; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t do_safety_hvsw3_precharge; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t do_pt1000_ctrlen1; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t do_pt1000_ctrlen2; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t do_pt1000_ctrlen3; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t do_safety_cp_state_c; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t do_cp_dutycycle; +}; + +/** + * Signals in message DiagnosticMeasurements2. + * + * All signal values are as on the CAN bus. + */ +struct can_diagnostic_measurements2_t { + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t dm_ce_adc_val; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t dm_id_adc_val; +}; + +/** + * Signals in message AnalogInput04. + * + * All signal values are as on the CAN bus. + */ +struct can_analog_input04_t { + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t ai_pt1000_cfb3_4; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t ai_int_temp; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t ai_int_refvolt; +}; + +/** + * Signals in message AnalogInput03. + * + * All signal values are as on the CAN bus. + */ +struct can_analog_input03_t { + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t ai_safety_precharge_cfb; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t ai_safety_hs1_cfb; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t ai_safety_hs2_cfb; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t ai_pt1000_cfb1_2; +}; + +/** + * Signals in message AnalogInput02. + * + * All signal values are as on the CAN bus. + */ +struct can_analog_input02_t { + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t ai_cp_buffered_neg_adc; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t ai_cp_buffered_pos_adc; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t ai_pp_value_adc; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t ai_safety_u_in_adc; +}; + +/** + * Signals in message AnalogInput01. + * + * All signal values are as on the CAN bus. + */ +struct can_analog_input01_t { + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t ai_pt1000_lead1_1; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t ai_pt1000_lead1_2; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t ai_pt1000_lead1_3; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t ai_pt1000_lead1_4; +}; + +/** + * Signals in message ChargeControl2. + * + * All signal values are as on the CAN bus. + */ +struct can_charge_control2_t { + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cc_ccs_ready; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cc_controller_reset; +}; + +/** + * Signals in message ChargeState2. + * + * All signal values are as on the CAN bus. + */ +struct can_charge_state2_t { + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cs_ce_state; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cs_id_state; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cs_e_stop_reason; +}; + +/** + * Signals in message ChargeState1. + * + * This message shall be sent from safety controller to host processor for indicating the state of the charging session as well as the state of connected peripherals. + * + * All signal values are as on the CAN bus. + */ +struct can_charge_state1_t { + /** + * Feedback if PWM is active. 0 means not active, 1 means active + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cs_pwm_active; + + /** + * The current duty cycle between 0.0% and 100.0%. If the PWM is not aczive this signal is 0 + * + * Range: - + * Scale: 0.1 + * Offset: 0 + */ + uint16_t cs_current_duty_cycle; + + /** + * Is set when the safety controller detects that the diode on EV side is missing. + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cs_diode_fault; + + /** + * Is set when the safety controller detects a short-circuit condition between CP and PE line. + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cs_cp_short_circuit; + + /** + * Current state of the control pilot. See value mappings below + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cs_current_cp_state; + + /** + * State of the proximity pin. For fixed cables at CCS2, this value is 0x0: No Cable detected + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cs_current_pp_state; + + /** + * This is the state of the HV ready or State C output. This output is high, if the chargeSOM dont see any errors and CP is at state C. Otherwise it is low. + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cs_hv_ready; + + /** + * Is set when an error in the contactor is detected + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cs_contactor2_error; + + /** + * Is set when the contactor is closed + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cs_contactor2_state; + + /** + * Is set when an error in the contactor is detected + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cs_contactor1_error; + + /** + * Is set when the contactor is closed + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cs_contactor1_state; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cs_estop3_charging_abort; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cs_estop2_charging_abort; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cs_estop1_charging_abort; +}; + +/** + * Signals in message ChargeControl1. + * + * This message shall be sent from the host processor to the safety controller to control the peripherals connected to the safety controller. + * + * All signal values are as on the CAN bus. + */ +struct can_charge_control1_t { + /** + * This flag indicates if the PWM should be activated. At a value of 0, the CP level is also 0V. At a value of 1, the CP level is dependant of the duty cycle + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cc_pwm_active; + + /** + * Duty cycle between 0.0 and 100.0%. Values above 100.0% are set as 100%. Only valid if the signal CC_PWM_Active is 1 + * + * Range: - + * Scale: 0.1 + * Offset: 0 + */ + uint16_t cc_target_duty_cycle; + + /** + * Request to close the contactor state. A value of 0 means open contactor, a value of 1 means closed contactor. The contactors are only closed if the system has no errors and is in state C. + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cc_contactor2_state; + + /** + * Request to close the contactor state. A value of 0 means open contactor, a value of 1 means closed contactor. The contactors are only closed if the system has no errors and is in state C. + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t cc_contactor1_state; +}; + +/** + * Signals in message FirmwareVersion. + * + * This message provides information about the type and version of the flashed firmware + * + * All signal values are as on the CAN bus. + */ +struct can_firmware_version_t { + /** + * Major version of the firmware + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t major_version; + + /** + * Minor version of the firmware + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t minor_version; + + /** + * Build or patch version of the firmware + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t build_version; + + /** + * This firmware can be used for several products with minor changes in the build process. The platform type describes the used platform + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t platform_type; + + /** + * The type of firmware. See possible values below + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t application_type; +}; + +/** + * Signals in message GitHash. + * + * This message provides information about the GIT hash, written in the firmware + * + * All signal values are as on the CAN bus. + */ +struct can_git_hash_t { + /** + * First 8 byte of the 160 bit (SHA-1) GIT hash + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint64_t hash_signal; +}; + +/** + * Signals in message ErrorMessage. + * + * All signal values are as on the CAN bus. + */ +struct can_error_message_t { + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t error_active; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t error_module; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t error_reason; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t error_add_data1; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint16_t error_add_data2; +}; + +/** + * Signals in message DiagnosticMeasurements. + * + * All signal values are as on the CAN bus. + */ +struct can_diagnostic_measurements_t { + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + int16_t dm_cp_voltage_negative_side; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + int16_t dm_cp_voltage_positive_side; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + int16_t dm_pp_voltage; +}; + +/** + * Signals in message PT1000State. + * + * This message shall be sent from safety controller to host processor for indicating the state of the connected temperature sensors + * + * All signal values are as on the CAN bus. + */ +struct can_pt1000_state_t { + /** + * Current temperature of PT1000 channel in C with one decimal digit. 0x1FFF stands for: temp sensor not used. + * + * Range: - + * Scale: 0.1 + * Offset: 0 + */ + int16_t pt1_temperature; + + /** + * Indicates whether this PT1000 channel is disturbed, multiple channel can signal the condition in parallel. + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t pt1_selftest_failed; + + /** + * Indicates whether this PT1000 channel prevents charging, multiple channel can signal the condition in parallel. + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t pt1_charging_stopped; + + /** + * Current temperature of PT1000 channel in C with one decimal digit. 0x1FFF stands for: temp sensor not used. + * + * Range: - + * Scale: 0.1 + * Offset: 0 + */ + int16_t pt2_temperature; + + /** + * Indicates whether this PT1000 channel is disturbed, multiple channel can signal the condition in parallel. + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t pt2_selftest_failed; + + /** + * Indicates whether this PT1000 channel prevents charging, multiple channel can signal the condition in parallel. + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t pt2_charging_stopped; + + /** + * Current temperature of PT1000 channel in C with one decimal digit. 0x1FFF stands for: temp sensor not used. + * + * Range: - + * Scale: 0.1 + * Offset: 0 + */ + int16_t pt3_temperature; + + /** + * Indicates whether this PT1000 channel is disturbed, multiple channel can signal the condition in parallel. + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t pt3_selftest_failed; + + /** + * Indicates whether this PT1000 channel prevents charging, multiple channel can signal the condition in parallel. + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t pt3_charging_stopped; + + /** + * Current temperature of PT1000 channel in C with one decimal digit. 0x1FFF stands for: temp sensor not used. + * + * Range: - + * Scale: 0.1 + * Offset: 0 + */ + int16_t pt4_temperature; + + /** + * Indicates whether this PT1000 channel is disturbed, multiple channel can signal the condition in parallel. + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t pt4_selftest_failed; + + /** + * Indicates whether this PT1000 channel prevents charging, multiple channel can signal the condition in parallel. + * + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t pt4_charging_stopped; +}; + +/** + * Signals in message DigitalInput. + * + * All signal values are as on the CAN bus. + */ +struct can_digital_input_t { + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_safety_k1_in; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_safety_motor_out1; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_safety_motor_out2; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_safety_motor_fault; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_cp_invert; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_ev_cp_edge; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_safety_dev_2; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_safety_dev_1; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_pp_sae_iec; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_safety_hvsw1_hs; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_safety_hvsw2_hs; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_safety_gpio_exp; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_safety_imd_od_en; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_pt4_en; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_safety_hvsw3_precharge; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_safety_k2_in; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_pt1_en; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_pt2_en; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_pt3_en; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_safety_cp_state_c; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_estop1; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_estop2; + + /** + * Range: - + * Scale: 1 + * Offset: 0 + */ + uint8_t di_estop3; +}; + +/** + * Pack message InquiryPacket. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int can_inquiry_packet_pack( + uint8_t *dst_p, + const struct can_inquiry_packet_t *src_p, + size_t size); + +/** + * Unpack message InquiryPacket. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int can_inquiry_packet_unpack( + struct can_inquiry_packet_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Init message fields to default values from InquiryPacket. + * + * @param[in] msg_p Message to init. + * + * @return zero(0) on success or (-1) in case of nullptr argument. + */ +int can_inquiry_packet_init(struct can_inquiry_packet_t *msg_p); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_inquiry_packet_packet_id_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_inquiry_packet_packet_id_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_inquiry_packet_packet_id_is_in_range(uint8_t value); + +/** + * Pack message DigitalOutput. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int can_digital_output_pack( + uint8_t *dst_p, + const struct can_digital_output_t *src_p, + size_t size); + +/** + * Unpack message DigitalOutput. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int can_digital_output_unpack( + struct can_digital_output_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Init message fields to default values from DigitalOutput. + * + * @param[in] msg_p Message to init. + * + * @return zero(0) on success or (-1) in case of nullptr argument. + */ +int can_digital_output_init(struct can_digital_output_t *msg_p); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_output_do_safety_motor_out1_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_output_do_safety_motor_out1_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_output_do_safety_motor_out1_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_output_do_safety_motor_out2_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_output_do_safety_motor_out2_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_output_do_safety_motor_out2_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_output_do_cp_invert_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_output_do_cp_invert_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_output_do_cp_invert_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_output_do_pp_sae_iec_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_output_do_pp_sae_iec_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_output_do_pp_sae_iec_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_output_do_safety_hvsw1_hs_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_output_do_safety_hvsw1_hs_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_output_do_safety_hvsw1_hs_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_output_do_safety_hvsw2_hs_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_output_do_safety_hvsw2_hs_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_output_do_safety_hvsw2_hs_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_output_do_safety_gpio_exp_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_output_do_safety_gpio_exp_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_output_do_safety_gpio_exp_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_output_do_safety_imd_od_en_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_output_do_safety_imd_od_en_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_output_do_safety_imd_od_en_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_output_do_pt1000_ctrlen4_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_output_do_pt1000_ctrlen4_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_output_do_pt1000_ctrlen4_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_output_do_safety_hvsw3_precharge_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_output_do_safety_hvsw3_precharge_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_output_do_safety_hvsw3_precharge_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_output_do_pt1000_ctrlen1_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_output_do_pt1000_ctrlen1_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_output_do_pt1000_ctrlen1_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_output_do_pt1000_ctrlen2_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_output_do_pt1000_ctrlen2_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_output_do_pt1000_ctrlen2_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_output_do_pt1000_ctrlen3_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_output_do_pt1000_ctrlen3_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_output_do_pt1000_ctrlen3_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_output_do_safety_cp_state_c_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_output_do_safety_cp_state_c_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_output_do_safety_cp_state_c_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_output_do_cp_dutycycle_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_output_do_cp_dutycycle_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_output_do_cp_dutycycle_is_in_range(uint8_t value); + +/** + * Pack message DiagnosticMeasurements2. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int can_diagnostic_measurements2_pack( + uint8_t *dst_p, + const struct can_diagnostic_measurements2_t *src_p, + size_t size); + +/** + * Unpack message DiagnosticMeasurements2. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int can_diagnostic_measurements2_unpack( + struct can_diagnostic_measurements2_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Init message fields to default values from DiagnosticMeasurements2. + * + * @param[in] msg_p Message to init. + * + * @return zero(0) on success or (-1) in case of nullptr argument. + */ +int can_diagnostic_measurements2_init(struct can_diagnostic_measurements2_t *msg_p); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_diagnostic_measurements2_dm_ce_adc_val_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_diagnostic_measurements2_dm_ce_adc_val_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_diagnostic_measurements2_dm_ce_adc_val_is_in_range(uint16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_diagnostic_measurements2_dm_id_adc_val_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_diagnostic_measurements2_dm_id_adc_val_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_diagnostic_measurements2_dm_id_adc_val_is_in_range(uint16_t value); + +/** + * Pack message AnalogInput04. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int can_analog_input04_pack( + uint8_t *dst_p, + const struct can_analog_input04_t *src_p, + size_t size); + +/** + * Unpack message AnalogInput04. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int can_analog_input04_unpack( + struct can_analog_input04_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Init message fields to default values from AnalogInput04. + * + * @param[in] msg_p Message to init. + * + * @return zero(0) on success or (-1) in case of nullptr argument. + */ +int can_analog_input04_init(struct can_analog_input04_t *msg_p); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_analog_input04_ai_pt1000_cfb3_4_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_analog_input04_ai_pt1000_cfb3_4_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_analog_input04_ai_pt1000_cfb3_4_is_in_range(uint16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_analog_input04_ai_int_temp_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_analog_input04_ai_int_temp_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_analog_input04_ai_int_temp_is_in_range(uint16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_analog_input04_ai_int_refvolt_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_analog_input04_ai_int_refvolt_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_analog_input04_ai_int_refvolt_is_in_range(uint16_t value); + +/** + * Pack message AnalogInput03. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int can_analog_input03_pack( + uint8_t *dst_p, + const struct can_analog_input03_t *src_p, + size_t size); + +/** + * Unpack message AnalogInput03. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int can_analog_input03_unpack( + struct can_analog_input03_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Init message fields to default values from AnalogInput03. + * + * @param[in] msg_p Message to init. + * + * @return zero(0) on success or (-1) in case of nullptr argument. + */ +int can_analog_input03_init(struct can_analog_input03_t *msg_p); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_analog_input03_ai_safety_precharge_cfb_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_analog_input03_ai_safety_precharge_cfb_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_analog_input03_ai_safety_precharge_cfb_is_in_range(uint16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_analog_input03_ai_safety_hs1_cfb_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_analog_input03_ai_safety_hs1_cfb_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_analog_input03_ai_safety_hs1_cfb_is_in_range(uint16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_analog_input03_ai_safety_hs2_cfb_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_analog_input03_ai_safety_hs2_cfb_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_analog_input03_ai_safety_hs2_cfb_is_in_range(uint16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_analog_input03_ai_pt1000_cfb1_2_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_analog_input03_ai_pt1000_cfb1_2_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_analog_input03_ai_pt1000_cfb1_2_is_in_range(uint16_t value); + +/** + * Pack message AnalogInput02. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int can_analog_input02_pack( + uint8_t *dst_p, + const struct can_analog_input02_t *src_p, + size_t size); + +/** + * Unpack message AnalogInput02. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int can_analog_input02_unpack( + struct can_analog_input02_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Init message fields to default values from AnalogInput02. + * + * @param[in] msg_p Message to init. + * + * @return zero(0) on success or (-1) in case of nullptr argument. + */ +int can_analog_input02_init(struct can_analog_input02_t *msg_p); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_analog_input02_ai_cp_buffered_neg_adc_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_analog_input02_ai_cp_buffered_neg_adc_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_analog_input02_ai_cp_buffered_neg_adc_is_in_range(uint16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_analog_input02_ai_cp_buffered_pos_adc_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_analog_input02_ai_cp_buffered_pos_adc_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_analog_input02_ai_cp_buffered_pos_adc_is_in_range(uint16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_analog_input02_ai_pp_value_adc_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_analog_input02_ai_pp_value_adc_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_analog_input02_ai_pp_value_adc_is_in_range(uint16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_analog_input02_ai_safety_u_in_adc_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_analog_input02_ai_safety_u_in_adc_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_analog_input02_ai_safety_u_in_adc_is_in_range(uint16_t value); + +/** + * Pack message AnalogInput01. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int can_analog_input01_pack( + uint8_t *dst_p, + const struct can_analog_input01_t *src_p, + size_t size); + +/** + * Unpack message AnalogInput01. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int can_analog_input01_unpack( + struct can_analog_input01_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Init message fields to default values from AnalogInput01. + * + * @param[in] msg_p Message to init. + * + * @return zero(0) on success or (-1) in case of nullptr argument. + */ +int can_analog_input01_init(struct can_analog_input01_t *msg_p); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_analog_input01_ai_pt1000_lead1_1_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_analog_input01_ai_pt1000_lead1_1_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_analog_input01_ai_pt1000_lead1_1_is_in_range(uint16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_analog_input01_ai_pt1000_lead1_2_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_analog_input01_ai_pt1000_lead1_2_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_analog_input01_ai_pt1000_lead1_2_is_in_range(uint16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_analog_input01_ai_pt1000_lead1_3_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_analog_input01_ai_pt1000_lead1_3_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_analog_input01_ai_pt1000_lead1_3_is_in_range(uint16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_analog_input01_ai_pt1000_lead1_4_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_analog_input01_ai_pt1000_lead1_4_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_analog_input01_ai_pt1000_lead1_4_is_in_range(uint16_t value); + +/** + * Pack message ChargeControl2. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int can_charge_control2_pack( + uint8_t *dst_p, + const struct can_charge_control2_t *src_p, + size_t size); + +/** + * Unpack message ChargeControl2. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int can_charge_control2_unpack( + struct can_charge_control2_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Init message fields to default values from ChargeControl2. + * + * @param[in] msg_p Message to init. + * + * @return zero(0) on success or (-1) in case of nullptr argument. + */ +int can_charge_control2_init(struct can_charge_control2_t *msg_p); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_control2_cc_ccs_ready_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_control2_cc_ccs_ready_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_control2_cc_ccs_ready_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_control2_cc_controller_reset_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_control2_cc_controller_reset_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_control2_cc_controller_reset_is_in_range(uint8_t value); + +/** + * Pack message ChargeState2. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int can_charge_state2_pack( + uint8_t *dst_p, + const struct can_charge_state2_t *src_p, + size_t size); + +/** + * Unpack message ChargeState2. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int can_charge_state2_unpack( + struct can_charge_state2_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Init message fields to default values from ChargeState2. + * + * @param[in] msg_p Message to init. + * + * @return zero(0) on success or (-1) in case of nullptr argument. + */ +int can_charge_state2_init(struct can_charge_state2_t *msg_p); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_state2_cs_ce_state_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_state2_cs_ce_state_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_state2_cs_ce_state_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_state2_cs_id_state_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_state2_cs_id_state_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_state2_cs_id_state_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_state2_cs_e_stop_reason_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_state2_cs_e_stop_reason_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_state2_cs_e_stop_reason_is_in_range(uint8_t value); + +/** + * Pack message ChargeState1. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int can_charge_state1_pack( + uint8_t *dst_p, + const struct can_charge_state1_t *src_p, + size_t size); + +/** + * Unpack message ChargeState1. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int can_charge_state1_unpack( + struct can_charge_state1_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Init message fields to default values from ChargeState1. + * + * @param[in] msg_p Message to init. + * + * @return zero(0) on success or (-1) in case of nullptr argument. + */ +int can_charge_state1_init(struct can_charge_state1_t *msg_p); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_state1_cs_pwm_active_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_state1_cs_pwm_active_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_state1_cs_pwm_active_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_charge_state1_cs_current_duty_cycle_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_state1_cs_current_duty_cycle_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_state1_cs_current_duty_cycle_is_in_range(uint16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_state1_cs_diode_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_state1_cs_diode_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_state1_cs_diode_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_state1_cs_cp_short_circuit_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_state1_cs_cp_short_circuit_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_state1_cs_cp_short_circuit_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_state1_cs_current_cp_state_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_state1_cs_current_cp_state_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_state1_cs_current_cp_state_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_state1_cs_current_pp_state_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_state1_cs_current_pp_state_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_state1_cs_current_pp_state_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_state1_cs_hv_ready_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_state1_cs_hv_ready_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_state1_cs_hv_ready_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_state1_cs_contactor2_error_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_state1_cs_contactor2_error_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_state1_cs_contactor2_error_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_state1_cs_contactor2_state_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_state1_cs_contactor2_state_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_state1_cs_contactor2_state_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_state1_cs_contactor1_error_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_state1_cs_contactor1_error_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_state1_cs_contactor1_error_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_state1_cs_contactor1_state_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_state1_cs_contactor1_state_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_state1_cs_contactor1_state_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_state1_cs_estop3_charging_abort_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_state1_cs_estop3_charging_abort_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_state1_cs_estop3_charging_abort_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_state1_cs_estop2_charging_abort_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_state1_cs_estop2_charging_abort_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_state1_cs_estop2_charging_abort_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_state1_cs_estop1_charging_abort_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_state1_cs_estop1_charging_abort_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_state1_cs_estop1_charging_abort_is_in_range(uint8_t value); + +/** + * Pack message ChargeControl1. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int can_charge_control1_pack( + uint8_t *dst_p, + const struct can_charge_control1_t *src_p, + size_t size); + +/** + * Unpack message ChargeControl1. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int can_charge_control1_unpack( + struct can_charge_control1_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Init message fields to default values from ChargeControl1. + * + * @param[in] msg_p Message to init. + * + * @return zero(0) on success or (-1) in case of nullptr argument. + */ +int can_charge_control1_init(struct can_charge_control1_t *msg_p); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_control1_cc_pwm_active_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_control1_cc_pwm_active_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_control1_cc_pwm_active_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_charge_control1_cc_target_duty_cycle_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_control1_cc_target_duty_cycle_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_control1_cc_target_duty_cycle_is_in_range(uint16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_control1_cc_contactor2_state_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_control1_cc_contactor2_state_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_control1_cc_contactor2_state_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_charge_control1_cc_contactor1_state_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_charge_control1_cc_contactor1_state_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_charge_control1_cc_contactor1_state_is_in_range(uint8_t value); + +/** + * Pack message FirmwareVersion. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int can_firmware_version_pack( + uint8_t *dst_p, + const struct can_firmware_version_t *src_p, + size_t size); + +/** + * Unpack message FirmwareVersion. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int can_firmware_version_unpack( + struct can_firmware_version_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Init message fields to default values from FirmwareVersion. + * + * @param[in] msg_p Message to init. + * + * @return zero(0) on success or (-1) in case of nullptr argument. + */ +int can_firmware_version_init(struct can_firmware_version_t *msg_p); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_firmware_version_major_version_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_firmware_version_major_version_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_firmware_version_major_version_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_firmware_version_minor_version_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_firmware_version_minor_version_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_firmware_version_minor_version_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_firmware_version_build_version_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_firmware_version_build_version_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_firmware_version_build_version_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_firmware_version_platform_type_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_firmware_version_platform_type_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_firmware_version_platform_type_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_firmware_version_application_type_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_firmware_version_application_type_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_firmware_version_application_type_is_in_range(uint8_t value); + +/** + * Pack message GitHash. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int can_git_hash_pack( + uint8_t *dst_p, + const struct can_git_hash_t *src_p, + size_t size); + +/** + * Unpack message GitHash. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int can_git_hash_unpack( + struct can_git_hash_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Init message fields to default values from GitHash. + * + * @param[in] msg_p Message to init. + * + * @return zero(0) on success or (-1) in case of nullptr argument. + */ +int can_git_hash_init(struct can_git_hash_t *msg_p); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint64_t can_git_hash_hash_signal_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_git_hash_hash_signal_decode(uint64_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_git_hash_hash_signal_is_in_range(uint64_t value); + +/** + * Pack message ErrorMessage. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int can_error_message_pack( + uint8_t *dst_p, + const struct can_error_message_t *src_p, + size_t size); + +/** + * Unpack message ErrorMessage. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int can_error_message_unpack( + struct can_error_message_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Init message fields to default values from ErrorMessage. + * + * @param[in] msg_p Message to init. + * + * @return zero(0) on success or (-1) in case of nullptr argument. + */ +int can_error_message_init(struct can_error_message_t *msg_p); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_error_message_error_active_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_error_message_error_active_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_error_message_error_active_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_error_message_error_module_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_error_message_error_module_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_error_message_error_module_is_in_range(uint16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_error_message_error_reason_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_error_message_error_reason_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_error_message_error_reason_is_in_range(uint16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_error_message_error_add_data1_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_error_message_error_add_data1_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_error_message_error_add_data1_is_in_range(uint16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint16_t can_error_message_error_add_data2_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_error_message_error_add_data2_decode(uint16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_error_message_error_add_data2_is_in_range(uint16_t value); + +/** + * Pack message DiagnosticMeasurements. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int can_diagnostic_measurements_pack( + uint8_t *dst_p, + const struct can_diagnostic_measurements_t *src_p, + size_t size); + +/** + * Unpack message DiagnosticMeasurements. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int can_diagnostic_measurements_unpack( + struct can_diagnostic_measurements_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Init message fields to default values from DiagnosticMeasurements. + * + * @param[in] msg_p Message to init. + * + * @return zero(0) on success or (-1) in case of nullptr argument. + */ +int can_diagnostic_measurements_init(struct can_diagnostic_measurements_t *msg_p); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +int16_t can_diagnostic_measurements_dm_cp_voltage_negative_side_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_diagnostic_measurements_dm_cp_voltage_negative_side_decode(int16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_diagnostic_measurements_dm_cp_voltage_negative_side_is_in_range(int16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +int16_t can_diagnostic_measurements_dm_cp_voltage_positive_side_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_diagnostic_measurements_dm_cp_voltage_positive_side_decode(int16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_diagnostic_measurements_dm_cp_voltage_positive_side_is_in_range(int16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +int16_t can_diagnostic_measurements_dm_pp_voltage_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_diagnostic_measurements_dm_pp_voltage_decode(int16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_diagnostic_measurements_dm_pp_voltage_is_in_range(int16_t value); + +/** + * Pack message PT1000State. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int can_pt1000_state_pack( + uint8_t *dst_p, + const struct can_pt1000_state_t *src_p, + size_t size); + +/** + * Unpack message PT1000State. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int can_pt1000_state_unpack( + struct can_pt1000_state_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Init message fields to default values from PT1000State. + * + * @param[in] msg_p Message to init. + * + * @return zero(0) on success or (-1) in case of nullptr argument. + */ +int can_pt1000_state_init(struct can_pt1000_state_t *msg_p); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +int16_t can_pt1000_state_pt1_temperature_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_pt1000_state_pt1_temperature_decode(int16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_pt1000_state_pt1_temperature_is_in_range(int16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_pt1000_state_pt1_selftest_failed_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_pt1000_state_pt1_selftest_failed_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_pt1000_state_pt1_selftest_failed_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_pt1000_state_pt1_charging_stopped_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_pt1000_state_pt1_charging_stopped_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_pt1000_state_pt1_charging_stopped_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +int16_t can_pt1000_state_pt2_temperature_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_pt1000_state_pt2_temperature_decode(int16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_pt1000_state_pt2_temperature_is_in_range(int16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_pt1000_state_pt2_selftest_failed_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_pt1000_state_pt2_selftest_failed_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_pt1000_state_pt2_selftest_failed_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_pt1000_state_pt2_charging_stopped_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_pt1000_state_pt2_charging_stopped_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_pt1000_state_pt2_charging_stopped_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +int16_t can_pt1000_state_pt3_temperature_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_pt1000_state_pt3_temperature_decode(int16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_pt1000_state_pt3_temperature_is_in_range(int16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_pt1000_state_pt3_selftest_failed_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_pt1000_state_pt3_selftest_failed_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_pt1000_state_pt3_selftest_failed_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_pt1000_state_pt3_charging_stopped_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_pt1000_state_pt3_charging_stopped_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_pt1000_state_pt3_charging_stopped_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +int16_t can_pt1000_state_pt4_temperature_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_pt1000_state_pt4_temperature_decode(int16_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_pt1000_state_pt4_temperature_is_in_range(int16_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_pt1000_state_pt4_selftest_failed_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_pt1000_state_pt4_selftest_failed_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_pt1000_state_pt4_selftest_failed_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_pt1000_state_pt4_charging_stopped_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_pt1000_state_pt4_charging_stopped_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_pt1000_state_pt4_charging_stopped_is_in_range(uint8_t value); + +/** + * Pack message DigitalInput. + * + * @param[out] dst_p Buffer to pack the message into. + * @param[in] src_p Data to pack. + * @param[in] size Size of dst_p. + * + * @return Size of packed data, or negative error code. + */ +int can_digital_input_pack( + uint8_t *dst_p, + const struct can_digital_input_t *src_p, + size_t size); + +/** + * Unpack message DigitalInput. + * + * @param[out] dst_p Object to unpack the message into. + * @param[in] src_p Message to unpack. + * @param[in] size Size of src_p. + * + * @return zero(0) or negative error code. + */ +int can_digital_input_unpack( + struct can_digital_input_t *dst_p, + const uint8_t *src_p, + size_t size); + +/** + * Init message fields to default values from DigitalInput. + * + * @param[in] msg_p Message to init. + * + * @return zero(0) on success or (-1) in case of nullptr argument. + */ +int can_digital_input_init(struct can_digital_input_t *msg_p); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_safety_k1_in_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_safety_k1_in_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_safety_k1_in_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_safety_motor_out1_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_safety_motor_out1_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_safety_motor_out1_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_safety_motor_out2_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_safety_motor_out2_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_safety_motor_out2_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_safety_motor_fault_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_safety_motor_fault_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_safety_motor_fault_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_cp_invert_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_cp_invert_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_cp_invert_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_ev_cp_edge_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_ev_cp_edge_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_ev_cp_edge_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_safety_dev_2_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_safety_dev_2_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_safety_dev_2_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_safety_dev_1_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_safety_dev_1_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_safety_dev_1_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_pp_sae_iec_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_pp_sae_iec_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_pp_sae_iec_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_safety_hvsw1_hs_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_safety_hvsw1_hs_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_safety_hvsw1_hs_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_safety_hvsw2_hs_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_safety_hvsw2_hs_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_safety_hvsw2_hs_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_safety_gpio_exp_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_safety_gpio_exp_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_safety_gpio_exp_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_safety_imd_od_en_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_safety_imd_od_en_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_safety_imd_od_en_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_pt4_en_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_pt4_en_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_pt4_en_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_safety_hvsw3_precharge_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_safety_hvsw3_precharge_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_safety_hvsw3_precharge_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_safety_k2_in_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_safety_k2_in_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_safety_k2_in_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_pt1_en_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_pt1_en_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_pt1_en_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_pt2_en_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_pt2_en_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_pt2_en_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_pt3_en_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_pt3_en_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_pt3_en_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_safety_cp_state_c_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_safety_cp_state_c_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_safety_cp_state_c_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_estop1_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_estop1_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_estop1_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_estop2_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_estop2_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_estop2_is_in_range(uint8_t value); + +/** + * Encode given signal by applying scaling and offset. + * + * @param[in] value Signal to encode. + * + * @return Encoded signal. + */ +uint8_t can_digital_input_di_estop3_encode(double value); + +/** + * Decode given signal by applying scaling and offset. + * + * @param[in] value Signal to decode. + * + * @return Decoded signal. + */ +double can_digital_input_di_estop3_decode(uint8_t value); + +/** + * Check that given signal is in allowed range. + * + * @param[in] value Signal to check. + * + * @return true if in range, false otherwise. + */ +bool can_digital_input_di_estop3_is_in_range(uint8_t value); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/modules/CbCPXDriver/evse_board_support/evse_board_supportImpl.cpp b/modules/CbCPXDriver/evse_board_support/evse_board_supportImpl.cpp new file mode 100644 index 0000000..7344a65 --- /dev/null +++ b/modules/CbCPXDriver/evse_board_support/evse_board_supportImpl.cpp @@ -0,0 +1,368 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "evse_board_supportImpl.hpp" + +using namespace std::chrono_literals; + +types::board_support_common::BspEvent cpstate_to_bspevent(const types::cb_board_support::CPState& other) { + switch (other) { + case types::cb_board_support::CPState::A: + return {types::board_support_common::Event::A}; + case types::cb_board_support::CPState::B: + return {types::board_support_common::Event::B}; + case types::cb_board_support::CPState::C: + return {types::board_support_common::Event::C}; + case types::cb_board_support::CPState::D: + return {types::board_support_common::Event::D}; + case types::cb_board_support::CPState::E: + return {types::board_support_common::Event::E}; + case types::cb_board_support::CPState::F: + return {types::board_support_common::Event::F}; + default: + throw std::runtime_error("Unable to map the value '" + cpstate_to_string(other) + "'."); + } +} + +namespace module { +namespace evse_board_support { + +void evse_board_supportImpl::init() { + // configure hardware capabilities: use user-configurable settings for flexibility + // but use the same value for import and export - there seems to be no reason for AC + // that these values differ for import and export + this->hw_capabilities.min_current_A_import = this->mod->config.min_current_A; + this->hw_capabilities.max_current_A_import = this->mod->config.max_current_A; + this->hw_capabilities.min_current_A_export = this->mod->config.min_current_A; + this->hw_capabilities.max_current_A_export = this->mod->config.max_current_A; + + this->hw_capabilities.supports_changing_phases_during_charging = false; + this->hw_capabilities.max_phase_count_import = 3; + this->hw_capabilities.min_phase_count_import = 3; + this->hw_capabilities.max_phase_count_export = 3; + this->hw_capabilities.min_phase_count_export = 3; + + this->hw_capabilities.connector_type = + types::evse_board_support::string_to_connector_type(this->mod->config.connector_type); + + // register our callback handlers + + this->mod->controller->on_pp_change.connect([this](uint8_t new_pp_state) { + std::scoped_lock lock(this->pp_mutex); + + try { + // saved previous value + const types::board_support_common::Ampacity prev_value(this->pp_ampacity.ampacity); + + this->pp_ampacity.ampacity = this->mod->controller->pp_state_to_ampacity(new_pp_state); + + if (this->pp_ampacity.ampacity == types::board_support_common::Ampacity::None) { + EVLOG_info << "PP noticed plug removal from socket"; + } else { + EVLOG_info << "PP ampacity change from " << prev_value << " to " << this->pp_ampacity.ampacity; + } + + // publish new value, upper layer should decide how to handle the change + this->publish_ac_pp_ampacity(this->pp_ampacity); + + if (this->pp_ampacity.ampacity == types::board_support_common::Ampacity::None && + (this->cp_current_state == types::cb_board_support::CPState::C || + this->cp_current_state == types::cb_board_support::CPState::D)) { + // publish a ProximityFault + Everest::error::Error error_object = this->error_factory->create_error( + "evse_board_support/MREC23ProximityFault", "", "Plug removed from socket during charge", + Everest::error::Severity::High); + this->raise_error(error_object); + this->pp_fault_reported = true; + } + + if (this->pp_ampacity.ampacity != types::board_support_common::Ampacity::None && this->pp_fault_reported) { + // clear a ProximityFault error on PP state change to a valid value but only if it exists + this->clear_error("evse_board_support/MREC23ProximityFault"); + this->pp_fault_reported = false; + } + } catch (const std::runtime_error& e) { + if (!this->pp_fault_reported) { + EVLOG_error << e.what(); + + // publish a ProximityFault + Everest::error::Error error_object = this->error_factory->create_error( + "evse_board_support/MREC23ProximityFault", "", e.what(), Everest::error::Severity::High); + this->raise_error(error_object); + + this->pp_fault_reported = true; + } + } + }); + + this->mod->controller->on_cp_change.connect([this](uint8_t current_cp_state) { + std::scoped_lock lock(this->cp_mutex); + + types::cb_board_support::CPState tmp_current_cp_state = + static_cast(current_cp_state); + + // FIXME REVISIT + // safety controller can report CP state unknown (which maps to our PowerOn here) in case + // of e.g short-circuit (should be clarified with safety fw development whether better report E) + if (tmp_current_cp_state == types::cb_board_support::CPState::PowerOn) + return; + + // FIXME REVISIT special case: during power on filter out transient state E + if (this->cp_current_state == types::cb_board_support::CPState::PowerOn && + tmp_current_cp_state == types::cb_board_support::CPState::E) { + EVLOG_debug << "CP state change from " << this->cp_current_state << " to " << tmp_current_cp_state << ", " + << "PWM: " << std::fixed << std::setprecision(1) + << (this->mod->controller->get_duty_cycle() / 10.0) << "%" << " [filtered]"; + return; + } + + EVLOG_info << "CP state change from " << this->cp_current_state << " to " << tmp_current_cp_state << ", " + << "PWM: " << std::fixed << std::setprecision(1) << (this->mod->controller->get_duty_cycle() / 10.0) + << "%"; + + // we can determine this directly from the currently seen value + this->cp_errors.pilot_fault.is_active = tmp_current_cp_state == types::cb_board_support::CPState::PilotFault; + + CPUtils::process_everest_errors(*this, this->cp_errors.errors); + + this->cp_current_state = tmp_current_cp_state; + + if (tmp_current_cp_state == types::cb_board_support::CPState::PilotFault) + return; + + try { + const types::board_support_common::BspEvent tmp = cpstate_to_bspevent(tmp_current_cp_state); + this->publish_event(tmp); + } catch (const std::runtime_error& e) { + // should never happen, when all invalid states are handled correctly + EVLOG_warning << e.what(); + } + }); + + this->mod->controller->on_cp_error.connect([this]() { + std::scoped_lock lock(this->cp_mutex); + + // we can determine this directly from the currently seen values + this->cp_errors.diode_fault.is_active = this->mod->controller->get_cs_diode_fault(); + this->cp_errors.cp_short_fault.is_active = this->mod->controller->get_cs_short_circuit(); + + CPUtils::process_everest_errors(*this, this->cp_errors.errors); + + // we only need to remember the PilotFault here, reset is done in `on_cp_change` + if (this->cp_errors.diode_fault.is_active || this->cp_errors.cp_short_fault.is_active) { + this->cp_current_state = types::cb_board_support::CPState::PilotFault; + } + }); + + this->mod->controller->on_contactor_error.connect( + [this](const std::string& source, bool desired_state, types::cb_board_support::ContactorState actual_state) { + raise_contactor_error(source, desired_state, actual_state); + }); + + this->mod->controller->on_estop.connect([this](const unsigned int& estop, const bool& active) { + if (active) + EVLOG_warning << "Emergency Stop " << estop << " TRIPPED"; + else + EVLOG_info << "Emergency Stop " << estop << " released"; + }); + + this->mod->controller->on_cpx_timeout.connect([this](bool timeout) { + const std::string error_type = "evse_board_support/VendorError"; + if (timeout == true) { + // signal State A so that EvseManager cancels session and starts + // fresh once CPX is back + this->cp_current_state = types::cb_board_support::CPState::A; + try { + const types::board_support_common::BspEvent tmp = cpstate_to_bspevent(this->cp_current_state); + this->publish_event(tmp); + } catch (const std::runtime_error& e) { + // should never happen, when all invalid states are handled correctly + EVLOG_warning << e.what(); + } + + // raise VendorError because of timeout + std::ostringstream errmsg; + errmsg << "CPX timeout registered"; + + EVLOG_warning << errmsg.str() << ", raising VendorError."; + + Everest::error::Error error_object = + this->error_factory->create_error(error_type, "", errmsg.str(), Everest::error::Severity::High); + this->raise_error(error_object); + } else { + // clear error after CPX is back after timeout + this->clear_error(error_type); + } + }); +} + +void evse_board_supportImpl::ready() { + // the BSP must publish this variable at least once during start up + this->publish_capabilities(this->hw_capabilities); + + // the PP ampacity should be published during start up + this->pp_ampacity.ampacity = this->mod->controller->get_ampacity(); + this->publish_ac_pp_ampacity(this->pp_ampacity); +} + +void evse_board_supportImpl::handle_enable(bool& value) { + try { + // enable CPX communication + if (value) + this->mod->controller->enable(); + + // generate state A or state F + const unsigned int new_duty_cycle = value ? 1000 : 0; + + EVLOG_info << "handle_enable: Setting new duty cycle of " << std::fixed << std::setprecision(1) + << (new_duty_cycle / 10.0) << "%"; + this->mod->controller->set_duty_cycle(new_duty_cycle); + + this->is_enabled = value; + } catch (const std::exception& e) { + EVLOG_error << e.what(); + } +} + +void evse_board_supportImpl::handle_pwm_on(double& value) { + try { + const unsigned int new_duty_cycle = static_cast(value * 10.0); + + EVLOG_info << "handle_pwm_on: Setting new duty cycle of " << std::fixed << std::setprecision(1) + << (new_duty_cycle / 10.0) << "%"; + this->mod->controller->set_duty_cycle(new_duty_cycle); + } catch (const std::exception& e) { + EVLOG_error << e.what(); + } +} + +void evse_board_supportImpl::handle_pwm_off() { + // in case safety controller was in emergency state, we have to reset it + // with a disable -> enable toggle + if (this->mod->controller->is_emergency()) { + EVLOG_info << "handle_pwm_off: recovering after safety state"; + + // FIXME: figure out viable solution to handle emergency and then implement + } + + try { + // generate state A + const unsigned int new_duty_cycle = 1000; + + EVLOG_info << "handle_pwm_off: Setting new duty cycle of " << std::fixed << std::setprecision(1) + << (new_duty_cycle / 10.0) << "%"; + this->mod->controller->set_duty_cycle(new_duty_cycle); + } catch (const std::exception& e) { + EVLOG_error << e.what(); + } +} + +void evse_board_supportImpl::handle_pwm_F() { + try { + // generate state F + const unsigned int new_duty_cycle = 0; + + EVLOG_info << "handle_pwm_F: Generating CP state F"; + + this->mod->controller->set_duty_cycle(new_duty_cycle); + } catch (const std::exception& e) { + EVLOG_error << e.what(); + } +} + +void evse_board_supportImpl::handle_allow_power_on(types::evse_board_support::PowerOnOff& value) { + // this method is called very often, even the contactor state is already matching the desired one + // so let's use this as helper to control the log noise a little bit and whether we actually + // need to report a BSP event + const bool contactor_state = this->mod->controller->get_contactor_state(); + + const bool state_change = value.allow_power_on != contactor_state; + + if (value.allow_power_on && this->cp_current_state == types::cb_board_support::CPState::PilotFault) { + EVLOG_warning << "Power on rejected: pilot fault detected."; + return; + } + + if (value.allow_power_on && this->mod->controller->is_emergency()) { + EVLOG_warning << "Power on rejected: emergency state active."; + return; + } + + if (value.allow_power_on && this->contactor_fault_reported) { + EVLOG_warning << "Power on rejected: contactor fault present."; + return; + } + + std::ostringstream ss; + ss << "handle_allow_power_on: request to " << (value.allow_power_on ? "CLOSE" : "OPEN") << " the contactor"; + + if (state_change) + EVLOG_info << ss.str(); + else + EVLOG_debug << ss.str(); + + // exit early if we don't actually change the state + if (!state_change) { + EVLOG_info << "Current (unchanged) contactor state: " << (contactor_state ? "CLOSED" : "OPEN"); + return; + } + + const int code_switch_state = this->mod->controller->switch_state(value.allow_power_on); + + if (code_switch_state == 0) { + EVLOG_info << "Current contactor state: " << (contactor_state ? "OPEN" : "CLOSED"); + + // publish PowerOn or PowerOff event + const types::board_support_common::Event tmp_event = value.allow_power_on + ? types::board_support_common::Event::PowerOn + : types::board_support_common::Event::PowerOff; + types::board_support_common::BspEvent tmp {tmp_event}; + this->publish_event(tmp); + } else { + const types::cb_board_support::ContactorState actual_state = + !(value.allow_power_on) ? types::cb_board_support::ContactorState::Closed + : types::cb_board_support::ContactorState::Open; + raise_contactor_error("Contactor " + std::to_string(code_switch_state), value.allow_power_on, actual_state); + } + // Note: errors while switching the contactor are reported and handled via on_error slot +} + +void evse_board_supportImpl::handle_ac_switch_three_phases_while_charging(bool& value) { + EVLOG_info << "handle_ac_switch_three_phases_while_charging: switching to " << (value ? "3-phase" : "1-phase") + << " mode"; + (void)value; +} + +void evse_board_supportImpl::handle_evse_replug(int& value) { + // your code for cmd evse_replug goes here + (void)value; +} + +void evse_board_supportImpl::handle_ac_set_overcurrent_limit_A(double& value) { + // your code for cmd ac_set_overcurrent_limit_A goes here + (void)value; +} + +void evse_board_supportImpl::raise_contactor_error(const std::string& source, bool desired_state, + types::cb_board_support::ContactorState actual_state) { + // An error occurred while switching - i.e. feedback does not match our expected new state. + // This fault is critical as it might be a sign of a hardware issue, therefore + // the fault will not be cleared to prevent further damage. + std::ostringstream errmsg; + errmsg << "Failed to " << (desired_state ? "CLOSE" : "OPEN") << " the " << source << ", it is still " + << actual_state; + + // raise a contactor fault if it was not done before + if (!this->contactor_fault_reported.exchange(true)) { + EVLOG_error << errmsg.str() << ", raising MREC17EVSEContactorFault."; + + Everest::error::Error error_object = this->error_factory->create_error( + "evse_board_support/MREC17EVSEContactorFault", "", errmsg.str(), Everest::error::Severity::High); + this->raise_error(error_object); + } else { + EVLOG_error << errmsg.str(); + } +} + +} // namespace evse_board_support +} // namespace module diff --git a/modules/CbCPXDriver/evse_board_support/evse_board_supportImpl.hpp b/modules/CbCPXDriver/evse_board_support/evse_board_supportImpl.hpp new file mode 100644 index 0000000..07d91f6 --- /dev/null +++ b/modules/CbCPXDriver/evse_board_support/evse_board_supportImpl.hpp @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest +#ifndef EVSE_BOARD_SUPPORT_EVSE_BOARD_SUPPORT_IMPL_HPP +#define EVSE_BOARD_SUPPORT_EVSE_BOARD_SUPPORT_IMPL_HPP + +// +// AUTO GENERATED - MARKED REGIONS WILL BE KEPT +// template version 3 +// + +#include "../CbCPXDriver.hpp" + +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 +// insert your custom include headers here + +#include + +struct cp_state_signal_side { + /// @brief previous state is what we measured before the last round + types::cb_board_support::CPState previous_state; + + /// @brief current state is what we measured in the last round + types::cb_board_support::CPState current_state; + + /// @brief measured state is what we just measured in this round + types::cb_board_support::CPState measured_state; + + /// @brief the voltage of the just completed measurement (in mV) + int voltage; +}; +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 + +namespace module { +namespace evse_board_support { + +struct Conf {}; + +class evse_board_supportImpl : public evse_board_supportImplBase { +public: + evse_board_supportImpl() = delete; + evse_board_supportImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer& mod, Conf& config) : + evse_board_supportImplBase(ev, "evse_board_support"), mod(mod), config(config) {}; + + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + // insert your public definitions here + + /// @brief Callback method called to react on new values of low-level PP observation + void pp_observation_worker(); + + /// @brief Callback method called to react on new values of low-level CP observation + void cp_observation_worker(); + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + +protected: + // command handler functions (virtual) + virtual void handle_enable(bool& value) override; + virtual void handle_pwm_on(double& value) override; + virtual void handle_pwm_off() override; + virtual void handle_pwm_F() override; + virtual void handle_allow_power_on(types::evse_board_support::PowerOnOff& value) override; + virtual void handle_ac_switch_three_phases_while_charging(bool& value) override; + virtual void handle_evse_replug(int& value) override; + virtual void handle_ac_set_overcurrent_limit_A(double& value) override; + + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + // insert your protected definitions here + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + +private: + const Everest::PtrContainer& mod; + const Conf& config; + + virtual void init() override; + virtual void ready() override; + + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 + // insert your private definitions here + + /// @brief Hardware Capabilities + types::evse_board_support::HardwareCapabilities hw_capabilities; + + /// @brief Mutex to enable/disable CP observation thread. Usually hold by the observation + /// worker thread but can be requested via `disable_cp_observation` method. + std::mutex cp_observation_lock; + + /// @brief Tracks whether this EVSE is enabled or not. + std::atomic_bool is_enabled {false}; + + /// @brief Helper to track whether the CP observation is running + bool cp_observation_enabled {false}; + + /// @brief Tracks the last published CP state. + std::atomic cp_current_state {types::cb_board_support::CPState::PowerOn}; + + /// @brief Store CP state errors + CPUtils::cp_state_errors cp_errors {}; + + /// @brief Last published/detected ampacity + types::board_support_common::ProximityPilot pp_ampacity {.ampacity = types::board_support_common::Ampacity::None}; + + /// @brief Flag to remember whether we already published a proximity error + std::atomic_bool pp_fault_reported {false}; + + /// @brief Flag to remember whether we already published a control pilot error + std::atomic_bool pilot_fault_reported {false}; + + /// @brief Flag to remember whether we already published a diode error + std::atomic_bool diode_fault_reported {false}; + + /// @brief Flag to remember whether we already published a ventilation error + std::atomic_bool ventilation_fault_reported {false}; + + /// @brief Mutex to synchronize PP observation + std::mutex pp_observation_lock; + + /// @brief Cached value for cp_observation_worker callback + double previous_duty_cycle {100.0}; + + /// @brief Cached values for cp_observation_worker callback + struct cp_state_signal_side cp_positive_side { + types::cb_board_support::CPState::PilotFault, types::cb_board_support::CPState::PilotFault, + types::cb_board_support::CPState::PilotFault, 0 + }; + + struct cp_state_signal_side cp_negative_side { + types::cb_board_support::CPState::PilotFault, types::cb_board_support::CPState::PilotFault, + types::cb_board_support::CPState::PilotFault, 0 + }; + + /// @brief Mutex to protect `pp_ampacity` and `pp_fault_reported` + std::mutex pp_mutex; + + /// @brief Mutex to protect `cp_errors` and `cp_current_state`. + std::mutex cp_mutex; + + /// @brief Flag to remember whether we already published a contactor fault. + std::atomic_bool contactor_fault_reported {false}; + + /// @brief Helper to report contactor_fault + void raise_contactor_error(const std::string& source, bool desired_state, + types::cb_board_support::ContactorState actual_state); + + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 +}; + +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 +// insert other definitions here +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 + +} // namespace evse_board_support +} // namespace module + +#endif // EVSE_BOARD_SUPPORT_EVSE_BOARD_SUPPORT_IMPL_HPP diff --git a/modules/CbCPXDriver/manifest.yaml b/modules/CbCPXDriver/manifest.yaml new file mode 100644 index 0000000..356a541 --- /dev/null +++ b/modules/CbCPXDriver/manifest.yaml @@ -0,0 +1,69 @@ +description: Hardware abstraction layer for chargebyte's CPX +config: + device_id: + description: First digit of the CAN-ID. Also used as VLAN-ID. + type: integer + default: 0 + connector_type: + description: Type of charging connector available at this EVSE + type: string + enum: + - IEC62196Type2Cable + - IEC62196Type2Socket + default: IEC62196Type2Cable + min_current_A: + description: >- + The minimum current in ampere which can be imported/exported. The default value + of 6 A corresponds to the lowest value which is technically possible to signal + by the IEC 61851 standard via PWM. + type: number + default: 6 + max_current_A: + description: >- + The maximum current in ampere which can be imported/exported. Here too, + the default corresponds to the maximum value which is possible to signal + with the IEC 61851 standard. The value should be adjusted if there is no + other limitating value/instance which represents the physical charger limits. + type: number + default: 80 + can_interface: + description: >- + Device name of the CAN interface to use for communication with CPX + hardware. + type: string + default: can0 + can_bitrate: + description: Bitrate of CAN bus used by CPX CAN controller. + type: integer + default: 500000 + minimum: 1000 + maximum: 1000000 + pt1000_1_identification: + description: Identification string used when this temperature channel is published + type: string + default: "PT1000-1" + pt1000_2_identification: + description: Identification string used when this temperature channel is published + type: string + default: "PT1000-2" + pt1000_3_identification: + description: Identification string used when this temperature channel is published + type: string + default: "PT1000-3" + pt1000_4_identification: + description: Identification string used when this temperature channel is published + type: string + default: "PT1000-4" +provides: + evse_board_support: + description: >- + Provide the board support driver for the CPX platform, e.g., control pilot + monitoring and controlling, contactor switching and similar. + interface: evse_board_support + temperatures: + description: Temperature readings as measured by the safety controller + interface: cb_temperatures +metadata: + license: https://spdx.org/licenses/Apache-2.0.html + authors: + - chargebyte GmbH diff --git a/modules/CbCPXDriver/temperatures/cb_temperaturesImpl.cpp b/modules/CbCPXDriver/temperatures/cb_temperaturesImpl.cpp new file mode 100644 index 0000000..c294cce --- /dev/null +++ b/modules/CbCPXDriver/temperatures/cb_temperaturesImpl.cpp @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "cb_temperaturesImpl.hpp" + +using namespace std::chrono_literals; + +namespace module { +namespace temperatures { + +void cb_temperaturesImpl::init() { +} + +void cb_temperaturesImpl::ready() { + this->publish_thread = std::thread([&]() { + const unsigned int supported_channels = this->mod->controller->get_temperature_channels(); + const std::vector> ident_config { + std::ref(this->mod->config.pt1000_1_identification), std::ref(this->mod->config.pt1000_2_identification), + std::ref(this->mod->config.pt1000_3_identification), std::ref(this->mod->config.pt1000_4_identification)}; + + while (!this->mod->termination_requested) { + std::vector v; + + // we sleep here to not overfill MQTT buffer + std::this_thread::sleep_for(1s); + + if (this->mod->termination_requested) + break; + + // this check whether we received temperature data at least once + if (!this->mod->controller->temperature_data_is_valid) + continue; + + for (unsigned int i = 1; i <= supported_channels; ++i) { + types::temperature::Temperature t; + + // skip this channel if not enabled at all + if (!this->mod->controller->is_temperature_enabled(i)) + continue; + + if (i - 1 < ident_config.size()) { + t.identification = ident_config[i - 1].get(); + } else { + t.identification = "Channel " + std::to_string(i); + } + + t.temperature = this->mod->controller->get_temperature(i); + + if (this->mod->controller->is_pt_charging_stopped(i)) { + if (!this->charging_abort_cause_reported[i]) { + EVLOG_warning << "Charging aborted due to " << t.identification.value() + << " temperature: " << std::fixed << t.temperature << " °C"; + this->charging_abort_cause_reported[i] = true; + } + } else { + // we can just reset the flag here since we don't want to + // notify the user explicitly because the port was reset completely + if (this->charging_abort_cause_reported[i]) { + EVLOG_warning << t.identification.value() << " charging abort flag reset"; + this->charging_abort_cause_reported[i] = false; + } + } + + if (this->mod->controller->is_pt_selftest_failed(i)) { + if (!this->selftest_failed_reported[i]) { + EVLOG_error << "Self-test for " << t.identification.value() << " failed."; + this->selftest_failed_reported[i] = true; + } + // unsure whether the data is (still) valid at all, so don't forward it anymore + continue; + } else { + // we can just reset the flag here since we don't want to + // notify the user explicitly because the port was reset completely + if (this->selftest_failed_reported[i]) { + EVLOG_warning << t.identification.value() << " selftest failed flag reset"; + this->selftest_failed_reported[i] = false; + } + } + + v.push_back(t); + } + + this->publish_temperatures(v); + } + }); +} + +} // namespace temperatures +} // namespace module diff --git a/modules/CbCPXDriver/temperatures/cb_temperaturesImpl.hpp b/modules/CbCPXDriver/temperatures/cb_temperaturesImpl.hpp new file mode 100644 index 0000000..f225685 --- /dev/null +++ b/modules/CbCPXDriver/temperatures/cb_temperaturesImpl.hpp @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest +#ifndef TEMPERATURES_CB_TEMPERATURES_IMPL_HPP +#define TEMPERATURES_CB_TEMPERATURES_IMPL_HPP + +// +// AUTO GENERATED - MARKED REGIONS WILL BE KEPT +// template version 3 +// + +#include "../CbCPXDriver.hpp" + +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 +// insert your custom include headers here +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 + +namespace module { +namespace temperatures { + +struct Conf {}; + +class cb_temperaturesImpl : public cb_temperaturesImplBase { +public: + cb_temperaturesImpl() = delete; + cb_temperaturesImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer& mod, Conf& config) : + cb_temperaturesImplBase(ev, "temperatures"), mod(mod), config(config) {}; + + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + // insert your public definitions here + + /// @brief Dtor + ~cb_temperaturesImpl() { + if (this->publish_thread.joinable()) + this->publish_thread.join(); + } + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + +protected: + // no commands defined for this interface + + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + // insert your protected definitions here + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + +private: + const Everest::PtrContainer& mod; + const Conf& config; + + virtual void init() override; + virtual void ready() override; + + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 + // insert your private definitions here + + /// @brief Remember whether we already reported the failed selftest + bool selftest_failed_reported[CB_PROTO_MAX_PT1000S + 1] = {false}; + + /// @brief Remember whether we already reported that this channel caused charging abort + bool charging_abort_cause_reported[CB_PROTO_MAX_PT1000S + 1] = {false}; + + /// @brief Thread for periodic publishing + std::thread publish_thread; + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 +}; + +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 +// insert other definitions here +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 + +} // namespace temperatures +} // namespace module + +#endif // TEMPERATURES_CB_TEMPERATURES_IMPL_HPP