/**
 * @file datalink.cpp
 * @date 2025-09-06 08:00:37
 * This file implements the data link object between CAN and the V-API devices.
 * This file was generated by the DBC utility from:
 *   "", "datalink_4doors_example.dbc"
 *   DBC file version: 1.0.0.1
 */
#include "datalink.h"
#include <algorithm>
#include <cmath>

#ifdef _MSC_VER
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
#endif

CDataLink::CDataLink() :
    m_sRxMsgCAN_Input_L1(m_dispatch),
    m_sRxMsgCAN_Input_R1(m_dispatch),
    m_sRxMsgCAN_Input_L2(m_dispatch),
    m_sRxMsgCAN_Input_R2(m_dispatch),
    m_sTxMsgCAN_Output(m_dispatch)
{}

CDataLink::~CDataLink()
{
    Shutdown(); // Just in case
}

void CDataLink::Initialize(const sdv::u8string& /*ssObjectConfig*/)
{
    if (m_eStatus != sdv::EObjectStatus::initialization_pending) return;

    // Get the CAN communication object.
    sdv::TInterfaceAccessPtr ptrCANObject = sdv::core::GetObject("CAN_Communication_Object");
    if (!ptrCANObject)
    {
		SDV_LOG_ERROR("CDataLink::Initialize() failure, 'CAN_Communication_Object' not found");
        m_eStatus = sdv::EObjectStatus::initialization_failure;
        return;
    }

    // Get the CAN receiver registration interface.
    m_pRegister = ptrCANObject.GetInterface<sdv::can::IRegisterReceiver>();
    if (!m_pRegister)
    {
		SDV_LOG_ERROR("CDataLink::Initialize() failure, 'sdv::can::IRegisterReceiver' interface not found");
        m_eStatus = sdv::EObjectStatus::initialization_failure;
        return;
    }
    m_pRegister->RegisterReceiver(static_cast<sdv::can::IReceive*>(this));

    // Get the CAN transmit interface
    m_pSend = ptrCANObject.GetInterface<sdv::can::ISend>();
    if (!m_pSend)
    {
		SDV_LOG_ERROR("CDataLink::Initialize() failure, 'sdv::can::ISend' interface not found");
        m_eStatus = sdv::EObjectStatus::initialization_failure;
        return;
    }

    // Initialize messages
    bool bSuccess = true;
    bSuccess &= m_sRxMsgCAN_Input_L1.Init();
    bSuccess &= m_sRxMsgCAN_Input_R1.Init();
    bSuccess &= m_sRxMsgCAN_Input_L2.Init();
    bSuccess &= m_sRxMsgCAN_Input_R2.Init();
    bSuccess &= m_sTxMsgCAN_Output.Init(m_pSend);

    m_eStatus = bSuccess ? sdv::EObjectStatus::initialized : sdv::EObjectStatus::initialization_failure;
}

sdv::EObjectStatus CDataLink::GetStatus() const
{
    return m_eStatus;
}

void CDataLink::SetOperationMode(sdv::EOperationMode eMode)
{
    switch (eMode)
    {
    case sdv::EOperationMode::configuring:
        if (m_eStatus == sdv::EObjectStatus::running || m_eStatus == sdv::EObjectStatus::initialized)
            m_eStatus = sdv::EObjectStatus::configuring;
        break;
    case sdv::EOperationMode::running:
        if (m_eStatus == sdv::EObjectStatus::configuring || m_eStatus == sdv::EObjectStatus::initialized)
            m_eStatus = sdv::EObjectStatus::running;
        break;
    default:
        break;
    }
}

void CDataLink::Shutdown()
{
    m_eStatus = sdv::EObjectStatus::shutdown_in_progress;

    // Unregister receiver interface.
    if (m_pRegister) m_pRegister->UnregisterReceiver(static_cast<sdv::can::IReceive*>(this));
    m_pRegister = nullptr;

    m_pSend = nullptr;

    // Terminate messages
    m_sRxMsgCAN_Input_L1.Term();
    m_sRxMsgCAN_Input_R1.Term();
    m_sRxMsgCAN_Input_L2.Term();
    m_sRxMsgCAN_Input_R2.Term();
    m_sTxMsgCAN_Output.Term();

    // Update the status
    m_eStatus = sdv::EObjectStatus::destruction_pending;
}

void CDataLink::Receive(/*in*/ [[maybe_unused]] const sdv::can::SMessage& sMsg, /*in*/ uint32_t uiIfcIndex)
{
    if (static_cast<size_t>(uiIfcIndex) != m_nIfcIndex) return;
    
    switch (sMsg.uiID)
    {
    case 1: // CAN_Input_L1
        m_sRxMsgCAN_Input_L1.Process(sMsg.seqData);
        break;
    case 2: // CAN_Input_R1
        m_sRxMsgCAN_Input_R1.Process(sMsg.seqData);
        break;
    case 3: // CAN_Input_L2
        m_sRxMsgCAN_Input_L2.Process(sMsg.seqData);
        break;
    case 4: // CAN_Input_R2
        m_sRxMsgCAN_Input_R2.Process(sMsg.seqData);
        break;
    default:
        break;
    }
}

void CDataLink::Error(/*in*/ [[maybe_unused]] const sdv::can::SErrorFrame& sError, /*in*/ uint32_t uiIfcIndex)
{
    if (static_cast<size_t>(uiIfcIndex) != m_nIfcIndex) return;

    // TODO: Currently no error frame handling...
}


CDataLink::SRxMsg_CAN_Input_L1::SRxMsg_CAN_Input_L1(sdv::core::CDispatchService& rdispatch) :
    m_rdispatch(rdispatch)
{}

bool CDataLink::SRxMsg_CAN_Input_L1::Init()
{
    // Register signals
    [[maybe_unused]] bool bSuccess = true;
    m_sigDoor01LeftIsOpen = m_rdispatch.RegisterRxSignal("CAN_Input_L1.Door01LeftIsOpen");
    bSuccess &= m_sigDoor01LeftIsOpen ? true : false;
    return bSuccess;
}

void CDataLink::SRxMsg_CAN_Input_L1::Term()
{
    // Unregister signals
    m_sigDoor01LeftIsOpen.Reset();
}

void CDataLink::SRxMsg_CAN_Input_L1::Process(const sdv::sequence<uint8_t>& rseqData)
{
    // Check for the correct size.
    if (rseqData.size() != 1)
    {
        // TODO: Error. Delivered data has different size as compared to the specification.
        return;
    }

    // Helper variable
    [[maybe_unused]] UValueHelper uValueHelper;

    // Start a transaction
    sdv::core::CTransaction transaction = m_rdispatch.CreateTransaction();

    // Process CAN_Input_L1.Door01LeftIsOpen
    uValueHelper.uiUint64Value = rseqData[0] & 3;
    uValueHelper.uiUint64Value >>= 1;
    m_sigDoor01LeftIsOpen.Write(std::min(std::max(uValueHelper.s32.u32.uiValue, 0u), 1u), transaction);

    // Finalize the transaction
    transaction.Finish();
}

CDataLink::SRxMsg_CAN_Input_R1::SRxMsg_CAN_Input_R1(sdv::core::CDispatchService& rdispatch) :
    m_rdispatch(rdispatch)
{}

bool CDataLink::SRxMsg_CAN_Input_R1::Init()
{
    // Register signals
    [[maybe_unused]] bool bSuccess = true;
    m_sigDoor01RightIsOpen = m_rdispatch.RegisterRxSignal("CAN_Input_R1.Door01RightIsOpen");
    bSuccess &= m_sigDoor01RightIsOpen ? true : false;
    return bSuccess;
}

void CDataLink::SRxMsg_CAN_Input_R1::Term()
{
    // Unregister signals
    m_sigDoor01RightIsOpen.Reset();
}

void CDataLink::SRxMsg_CAN_Input_R1::Process(const sdv::sequence<uint8_t>& rseqData)
{
    // Check for the correct size.
    if (rseqData.size() != 1)
    {
        // TODO: Error. Delivered data has different size as compared to the specification.
        return;
    }

    // Helper variable
    [[maybe_unused]] UValueHelper uValueHelper;

    // Start a transaction
    sdv::core::CTransaction transaction = m_rdispatch.CreateTransaction();

    // Process CAN_Input_R1.Door01RightIsOpen
    uValueHelper.uiUint64Value = rseqData[0] & 15;
    uValueHelper.uiUint64Value >>= 3;
    m_sigDoor01RightIsOpen.Write(std::min(std::max(uValueHelper.s32.u32.uiValue, 0u), 1u), transaction);

    // Finalize the transaction
    transaction.Finish();
}

CDataLink::SRxMsg_CAN_Input_L2::SRxMsg_CAN_Input_L2(sdv::core::CDispatchService& rdispatch) :
    m_rdispatch(rdispatch)
{}

bool CDataLink::SRxMsg_CAN_Input_L2::Init()
{
    // Register signals
    [[maybe_unused]] bool bSuccess = true;
    m_sigDoor02LeftIsOpen = m_rdispatch.RegisterRxSignal("CAN_Input_L2.Door02LeftIsOpen");
    bSuccess &= m_sigDoor02LeftIsOpen ? true : false;
    return bSuccess;
}

void CDataLink::SRxMsg_CAN_Input_L2::Term()
{
    // Unregister signals
    m_sigDoor02LeftIsOpen.Reset();
}

void CDataLink::SRxMsg_CAN_Input_L2::Process(const sdv::sequence<uint8_t>& rseqData)
{
    // Check for the correct size.
    if (rseqData.size() != 1)
    {
        // TODO: Error. Delivered data has different size as compared to the specification.
        return;
    }

    // Helper variable
    [[maybe_unused]] UValueHelper uValueHelper;

    // Start a transaction
    sdv::core::CTransaction transaction = m_rdispatch.CreateTransaction();

    // Process CAN_Input_L2.Door02LeftIsOpen
    uValueHelper.uiUint64Value = rseqData[0] & 63;
    uValueHelper.uiUint64Value >>= 5;
    m_sigDoor02LeftIsOpen.Write(std::min(std::max(uValueHelper.s32.u32.uiValue, 0u), 1u), transaction);

    // Finalize the transaction
    transaction.Finish();
}

CDataLink::SRxMsg_CAN_Input_R2::SRxMsg_CAN_Input_R2(sdv::core::CDispatchService& rdispatch) :
    m_rdispatch(rdispatch)
{}

bool CDataLink::SRxMsg_CAN_Input_R2::Init()
{
    // Register signals
    [[maybe_unused]] bool bSuccess = true;
    m_sigDoor02RightIsOpen = m_rdispatch.RegisterRxSignal("CAN_Input_R2.Door02RightIsOpen");
    bSuccess &= m_sigDoor02RightIsOpen ? true : false;
    return bSuccess;
}

void CDataLink::SRxMsg_CAN_Input_R2::Term()
{
    // Unregister signals
    m_sigDoor02RightIsOpen.Reset();
}

void CDataLink::SRxMsg_CAN_Input_R2::Process(const sdv::sequence<uint8_t>& rseqData)
{
    // Check for the correct size.
    if (rseqData.size() != 1)
    {
        // TODO: Error. Delivered data has different size as compared to the specification.
        return;
    }

    // Helper variable
    [[maybe_unused]] UValueHelper uValueHelper;

    // Start a transaction
    sdv::core::CTransaction transaction = m_rdispatch.CreateTransaction();

    // Process CAN_Input_R2.Door02RightIsOpen
    uValueHelper.uiUint64Value = rseqData[0];
    uValueHelper.uiUint64Value >>= 7;
    m_sigDoor02RightIsOpen.Write(std::min(std::max(uValueHelper.s32.u32.uiValue, 0u), 1u), transaction);

    // Finalize the transaction
    transaction.Finish();
}

CDataLink::STxMsg_CAN_Output::STxMsg_CAN_Output(sdv::core::CDispatchService& rdispatch) :
    m_rdispatch(rdispatch)
{}

bool CDataLink::STxMsg_CAN_Output::Init(sdv::can::ISend* pSend)
{
    if (!pSend) return false;
    m_pSend = pSend;

    // Register signals
    bool bSuccess = true;
    m_sigLockDoor02Right = m_rdispatch.RegisterTxSignal("CAN_Output.LockDoor02Right", sdv::any_t());
    bSuccess &= m_sigLockDoor02Right ? true : false;
    m_sigLockDoor02Left = m_rdispatch.RegisterTxSignal("CAN_Output.LockDoor02Left", sdv::any_t());
    bSuccess &= m_sigLockDoor02Left ? true : false;
    m_sigLockDoor01Right = m_rdispatch.RegisterTxSignal("CAN_Output.LockDoor01Right", sdv::any_t());
    bSuccess &= m_sigLockDoor01Right ? true : false;
    m_sigLockDoor01Left = m_rdispatch.RegisterTxSignal("CAN_Output.LockDoor01Left", sdv::any_t());
    bSuccess &= m_sigLockDoor01Left ? true : false;

    // Initialize the trigger
    m_trigger = m_rdispatch.CreateTxTrigger([&] { Transmit(); }, false, 0, 10, false);
    m_trigger.AddSignal(m_sigLockDoor02Right);
    m_trigger.AddSignal(m_sigLockDoor02Left);
    m_trigger.AddSignal(m_sigLockDoor01Right);
    m_trigger.AddSignal(m_sigLockDoor01Left);
    bSuccess &= m_trigger;

    return bSuccess;
}

void CDataLink::STxMsg_CAN_Output::Term()
{
    // Reset the trigger
    m_trigger.Reset();

    // Unregister signals
    m_sigLockDoor02Right.Reset();
    m_sigLockDoor02Left.Reset();
    m_sigLockDoor01Right.Reset();
    m_sigLockDoor01Left.Reset();
}

void CDataLink::STxMsg_CAN_Output::Transmit()
{
    if (!m_pSend) return;

    // Compose CAN message
    sdv::can::SMessage msg;
    msg.uiID = 5;
    msg.bExtended = false;
    msg.bCanFd = false;         // TODO: Currently not supported
    msg.seqData.resize(1);
    std::fill(msg.seqData.begin(), msg.seqData.end(), static_cast<uint8_t>(0));

    // Helper variable
    [[maybe_unused]] UValueHelper uValueHelper;

    // Start a transaction
    sdv::core::CTransaction transaction = m_rdispatch.CreateTransaction();

    // Compose CAN_Output.LockDoor02Right
    uValueHelper.s32.u32.uiValue = static_cast<uint32_t>(std::min(std::max(static_cast<long long int>(m_sigLockDoor02Right.Read(transaction)), 0ll), 1ll));
    uValueHelper.uiUint64Value <<= 7;
    msg.seqData[0] |= uValueHelper.uiUint64Value & 0xff;

    // Compose CAN_Output.LockDoor02Left
    uValueHelper.s32.u32.uiValue = static_cast<uint32_t>(std::min(std::max(static_cast<long long int>(m_sigLockDoor02Left.Read(transaction)), 0ll), 1ll));
    uValueHelper.uiUint64Value <<= 5;
    msg.seqData[0] |= uValueHelper.uiUint64Value & 63;

    // Compose CAN_Output.LockDoor01Right
    uValueHelper.s32.u32.uiValue = static_cast<uint32_t>(std::min(std::max(static_cast<long long int>(m_sigLockDoor01Right.Read(transaction)), 0ll), 1ll));
    uValueHelper.uiUint64Value <<= 3;
    msg.seqData[0] |= uValueHelper.uiUint64Value & 15;

    // Compose CAN_Output.LockDoor01Left
    uValueHelper.s32.u32.uiValue = static_cast<uint32_t>(std::min(std::max(static_cast<long long int>(m_sigLockDoor01Left.Read(transaction)), 0ll), 1ll));
    uValueHelper.uiUint64Value <<= 1;
    msg.seqData[0] |= uValueHelper.uiUint64Value & 3;

    // Finalize the transaction
    transaction.Finish();

    // Transmit the message
    // TODO: Determine CAN interface index...
    m_pSend->Send(msg, 0);
}

