|
#include "isobus/hardware_integration/can_hardware_interface.hpp" |
|
#include "isobus/hardware_integration/socket_can_interface.hpp" |
|
#include "isobus/isobus/can_network_manager.hpp" |
|
#include "isobus/isobus/can_partnered_control_function.hpp" |
|
#include "isobus/isobus/can_stack_logger.hpp" |
|
#include "isobus/isobus/can_callbacks.hpp" |
|
|
|
#include <atomic> |
|
#include <chrono> |
|
#include <csignal> |
|
#include <iostream> |
|
#include <memory> |
|
#include <thread> |
|
|
|
using namespace isobus; |
|
|
|
// Global variables |
|
static std::atomic_bool running = { true }; |
|
|
|
// Signal handler for graceful shutdown |
|
void signal_handler(int) |
|
{ |
|
std::cout << "\nECU1: Received shutdown signal. Stopping..." << std::endl; |
|
running = false; |
|
} |
|
|
|
// Callback function for control function state changes |
|
void control_function_state_change_callback(std::shared_ptr<ControlFunction> controlFunction, ControlFunctionState state) |
|
{ |
|
std::cout << "ECU1: Control Function State Change Detected!" << std::endl; |
|
std::cout << " Control Function: " << controlFunction.get() << std::endl; |
|
|
|
// Print NAME details if available |
|
if (controlFunction->get_NAME().get_full_name() != 0) |
|
{ |
|
std::cout << " NAME: 0x" << std::hex << controlFunction->get_NAME().get_full_name() << std::dec << std::endl; |
|
std::cout << " Manufacturer Code: " << controlFunction->get_NAME().get_manufacturer_code() << std::endl; |
|
std::cout << " Function Code: " << static_cast<int>(controlFunction->get_NAME().get_function_code()) << std::endl; |
|
std::cout << " Identity Number: " << controlFunction->get_NAME().get_identity_number() << std::endl; |
|
std::cout << " ECU Instance: " << static_cast<int>(controlFunction->get_NAME().get_ecu_instance()) << std::endl; |
|
} |
|
|
|
// Print address information |
|
std::cout << " Address: 0x" << std::hex << static_cast<int>(controlFunction->get_address()) << std::dec << std::endl; |
|
std::cout << " Address Valid: " << (controlFunction->get_address_valid() ? "Yes" : "No") << std::endl; |
|
|
|
std::cout << " Current State: "; |
|
switch (state) |
|
{ |
|
case ControlFunctionState::Offline: |
|
std::cout << "Offline"; |
|
break; |
|
case ControlFunctionState::Online: |
|
std::cout << "Online"; |
|
break; |
|
default: |
|
std::cout << "Unknown (" << static_cast<int>(state) << ")"; |
|
break; |
|
} |
|
std::cout << std::endl; |
|
std::cout << " ------------------------" << std::endl; |
|
} |
|
|
|
int main() |
|
{ |
|
std::signal(SIGINT, signal_handler); |
|
std::signal(SIGTERM, signal_handler); |
|
|
|
std::cout << "ECU1: Starting AgIsoStack ECU1 on vcan0..." << std::endl; |
|
|
|
#ifndef ISOBUS_SOCKETCAN_AVAILABLE |
|
std::cout << "ECU1: ERROR - SocketCAN not available. Make sure you're running on Linux with SocketCAN support." << std::endl; |
|
return -1; |
|
#endif |
|
|
|
// Initialize the CAN hardware interface |
|
auto canDriver = std::make_shared<SocketCANInterface>("vcan0"); |
|
|
|
CANHardwareInterface::set_number_of_can_channels(1); |
|
CANHardwareInterface::assign_can_channel_frame_handler(0, canDriver); |
|
|
|
if (!CANHardwareInterface::start() || !canDriver->get_is_valid()) |
|
{ |
|
std::cout << "ECU1: ERROR - Failed to start CAN hardware interface on vcan0." << std::endl; |
|
std::cout << "ECU1: Make sure vcan0 is available with: sudo modprobe vcan && sudo ip link add dev vcan0 type vcan && sudo ip link set up vcan0" << std::endl; |
|
return -2; |
|
} |
|
|
|
std::cout << "ECU1: CAN interface started successfully on " << canDriver->get_device_name() << std::endl; |
|
|
|
// Allow some time for the interface to initialize |
|
std::this_thread::sleep_for(std::chrono::milliseconds(100)); |
|
|
|
// Create the NAME for this ECU |
|
NAME ecu1Name(0); |
|
ecu1Name.set_arbitrary_address_capable(true); |
|
ecu1Name.set_industry_group(1); |
|
ecu1Name.set_device_class(25); // Non-specific system |
|
ecu1Name.set_function_code(static_cast<std::uint8_t>(NAME::Function::Engine)); |
|
ecu1Name.set_identity_number(100); |
|
ecu1Name.set_ecu_instance(0); |
|
ecu1Name.set_function_instance(0); |
|
ecu1Name.set_device_class_instance(0); |
|
ecu1Name.set_manufacturer_code(1407); // Example manufacturer code |
|
|
|
// Create internal control function |
|
auto ecu1ControlFunction = CANNetworkManager::CANNetwork.create_internal_control_function(ecu1Name, 0, 0x80); |
|
|
|
if (nullptr == ecu1ControlFunction) |
|
{ |
|
std::cout << "ECU1: ERROR - Failed to create internal control function." << std::endl; |
|
CANHardwareInterface::stop(); |
|
return -3; |
|
} |
|
|
|
// Wait for address claiming to complete |
|
std::cout << "ECU1: Waiting for address claiming..." << std::endl; |
|
auto start_time = std::chrono::steady_clock::now(); |
|
while (!ecu1ControlFunction->get_address_valid() && |
|
(std::chrono::steady_clock::now() - start_time < std::chrono::seconds(5))) |
|
{ |
|
std::this_thread::sleep_for(std::chrono::milliseconds(100)); |
|
} |
|
|
|
if (!ecu1ControlFunction->get_address_valid()) |
|
{ |
|
std::cout << "ECU1: ERROR - Address claiming failed." << std::endl; |
|
CANHardwareInterface::stop(); |
|
return -4; |
|
} |
|
|
|
std::cout << "ECU1: Address claiming successful! ECU1 claimed address: 0x" |
|
<< std::hex << static_cast<int>(ecu1ControlFunction->get_address()) << std::dec << std::endl; |
|
|
|
// Register control function state change callback |
|
CANNetworkManager::CANNetwork.add_control_function_status_change_callback(control_function_state_change_callback); |
|
std::cout << "ECU1: Control function state change callback registered." << std::endl; |
|
|
|
// Create a filter to listen for ECU2 messages |
|
const NAMEFilter ecu2Filter(NAME::NAMEParameters::FunctionCode, static_cast<std::uint8_t>(NAME::Function::VirtualTerminal)); |
|
auto ecu2Partner = CANNetworkManager::CANNetwork.create_partnered_control_function(0, { ecu2Filter }); |
|
|
|
// Main loop |
|
std::cout << "ECU1: Entering main loop. Press Ctrl+C to stop." << std::endl; |
|
auto last_heartbeat = std::chrono::steady_clock::now(); |
|
|
|
while (running) |
|
{ |
|
isobus::CANNetworkManager::CANNetwork.send_request_for_address_claim(0); |
|
// Process CAN messages and update network manager |
|
std::this_thread::sleep_for(std::chrono::milliseconds(10)); |
|
} |
|
|
|
std::cout << "ECU1: Shutting down..." << std::endl; |
|
CANHardwareInterface::stop(); |
|
std::cout << "ECU1: Shutdown complete." << std::endl; |
|
|
|
return 0; |
|
} |