Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ if(BUILD_EXAMPLES)
if(NOT CAN_STACK_DISABLE_THREADS)
# Cannot build without threading as vt server is multi-threaded
add_subdirectory("examples/virtual_terminal/iop_parser_tester")
add_subdirectory("examples/virtual_terminal/multiple_object_pools")
endif()
add_subdirectory("examples/task_controller_client")
add_subdirectory("examples/task_controller_server")
Expand Down
31 changes: 31 additions & 0 deletions examples/virtual_terminal/multiple_object_pools/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
cmake_minimum_required(VERSION 3.16)
project(vt_client_multiple_object_pools_example)

if(NOT BUILD_EXAMPLES)
find_package(isobus REQUIRED)
endif()
find_package(Threads REQUIRED)

add_executable(VTClientMultiplePoolsExampleTarget main.cpp console_logger.cpp
objectPoolObjects.h)

target_compile_features(VTClientMultiplePoolsExampleTarget PUBLIC cxx_std_11)
set_target_properties(VTClientMultiplePoolsExampleTarget
PROPERTIES CXX_EXTENSIONS OFF)

target_link_libraries(
VTClientMultiplePoolsExampleTarget
PRIVATE isobus::Isobus isobus::HardwareIntegration Threads::Threads
isobus::Utility)

add_custom_command(
TARGET VTClientMultiplePoolsExampleTarget
POST_BUILD
COMMENT "Copying object pools to build directory"
COMMAND
${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/../version3_object_pool/VT3TestPool.iop
$<TARGET_FILE_DIR:VTClientMultiplePoolsExampleTarget>/VT3TestPool.iop
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/window_masks.iop
$<TARGET_FILE_DIR:VTClientMultiplePoolsExampleTarget>/window_masks.iop
)
67 changes: 67 additions & 0 deletions examples/virtual_terminal/multiple_object_pools/console_logger.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#include "isobus/isobus/can_stack_logger.hpp"

#include <iostream>

// A log sink for the CAN stack
class CustomLogger : public isobus::CANStackLogger
{
public:
void sink_CAN_stack_log(CANStackLogger::LoggingLevel level, const std::string &text) override
{
switch (level)
{
case LoggingLevel::Debug:
{
std::cout << "["
<< "\033[1;36m"
<< "Debug"
<< "\033[0m"
<< "]";
}
break;

case LoggingLevel::Info:
{
std::cout << "["
<< "\033[1;32m"
<< "Info"
<< "\033[0m"
<< "]";
}
break;

case LoggingLevel::Warning:
{
std::cout << "["
<< "\033[1;33m"
<< "Warn"
<< "\033[0m"
<< "]";
}
break;

case LoggingLevel::Error:
{
std::cout << "["
<< "\033[1;31m"
<< "Error"
<< "\033[0m"
<< "]";
}
break;

case LoggingLevel::Critical:
{
std::cout << "["
<< "\033[1;35m"
<< "Critical"
<< "\033[0m"
<< "]";
}
break;
}
std::cout << text << std::endl; // Write the text to stdout
}
};

static CustomLogger logger;
220 changes: 220 additions & 0 deletions examples/virtual_terminal/multiple_object_pools/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
#include "isobus/hardware_integration/available_can_drivers.hpp"
#include "isobus/hardware_integration/can_hardware_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/isobus_virtual_terminal_client.hpp"
#include "isobus/isobus/isobus_virtual_terminal_client_update_helper.hpp"
#include "isobus/utility/iop_file_interface.hpp"

#include "console_logger.cpp"
#include "objectPoolObjects.h"

#include <atomic>
#include <csignal>
#include <iostream>

//! It is discouraged to use global variables, but it is done here for simplicity.
static std::shared_ptr<isobus::VirtualTerminalClient> virtualTerminalClient = nullptr;
static std::shared_ptr<isobus::VirtualTerminalClientUpdateHelper> virtualTerminalUpdateHelper = nullptr;
static std::atomic_bool running = { true };

void signal_handler(int)
{
running = false;
}

// This callback will provide us with event driven notifications of softkey presses from the stack
void handle_softkey_event(const isobus::VirtualTerminalClient::VTKeyEvent &event)
{
if (event.keyNumber == 0)
{
// We have the alarm ACK code, so if we have an active alarm, acknowledge it by going back to the main runscreen
virtualTerminalUpdateHelper->set_active_data_or_alarm_mask(example_WorkingSet, mainRunscreen_DataMask);
}

switch (event.keyEvent)
{
case isobus::VirtualTerminalClient::KeyActivationCode::ButtonUnlatchedOrReleased:
{
switch (event.objectID)
{
case alarm_SoftKey:
{
virtualTerminalUpdateHelper->set_active_data_or_alarm_mask(example_WorkingSet, example_AlarmMask);
}
break;

case acknowledgeAlarm_SoftKey:
{
virtualTerminalUpdateHelper->set_active_data_or_alarm_mask(example_WorkingSet, mainRunscreen_DataMask);
}
break;

default:
break;
}
}
break;

default:
break;
}
}

// This callback will provide us with event driven notifications of button presses from the stack
void handle_button_event(const isobus::VirtualTerminalClient::VTKeyEvent &event)
{
switch (event.keyEvent)
{
case isobus::VirtualTerminalClient::KeyActivationCode::ButtonUnlatchedOrReleased:
case isobus::VirtualTerminalClient::KeyActivationCode::ButtonStillHeld:
{
switch (event.objectID)
{
case Plus_Button:
{
virtualTerminalUpdateHelper->increase_numeric_value(ButtonExampleNumber_VarNum);
}
break;

case Minus_Button:
{
virtualTerminalUpdateHelper->decrease_numeric_value(ButtonExampleNumber_VarNum);
}
break;

default:
break;
}
}
break;

default:
break;
}
}

int main()
{
std::signal(SIGINT, signal_handler);

// Automatically load the desired CAN driver based on the available drivers
std::shared_ptr<isobus::CANHardwarePlugin> canDriver = nullptr;
#if defined(ISOBUS_SOCKETCAN_AVAILABLE)
canDriver = std::make_shared<isobus::SocketCANInterface>("can0");
#elif defined(ISOBUS_WINDOWSPCANBASIC_AVAILABLE)
canDriver = std::make_shared<isobus::PCANBasicWindowsPlugin>(PCAN_USBBUS1);
#elif defined(ISOBUS_WINDOWSINNOMAKERUSB2CAN_AVAILABLE)
canDriver = std::make_shared<isobus::InnoMakerUSB2CANWindowsPlugin>(0); // CAN0
#elif defined(ISOBUS_MACCANPCAN_AVAILABLE)
canDriver = std::make_shared<isobus::MacCANPCANPlugin>(PCAN_USBBUS1);
#elif defined(ISOBUS_SYS_TEC_AVAILABLE)
canDriver = std::make_shared<isobus::SysTecWindowsPlugin>();
#endif
if (nullptr == canDriver)
{
std::cout << "Unable to find a CAN driver. Please make sure you have one of the above drivers installed with the library." << std::endl;
std::cout << "If you want to use a different driver, please add it to the list above." << std::endl;
return -1;
}

isobus::CANStackLogger::set_can_stack_logger_sink(&logger);
isobus::CANStackLogger::set_log_level(isobus::CANStackLogger::LoggingLevel::Info); // Change this to Debug to see more information
isobus::CANHardwareInterface::set_number_of_can_channels(1);
isobus::CANHardwareInterface::assign_can_channel_frame_handler(0, canDriver);

if ((!isobus::CANHardwareInterface::start()) || (!canDriver->get_is_valid()))
{
std::cout << "Failed to start hardware interface. The CAN driver might be invalid." << std::endl;
return -2;
}

std::this_thread::sleep_for(std::chrono::milliseconds(250));

isobus::NAME TestDeviceNAME(0);

//! Make sure you change these for your device!!!!
TestDeviceNAME.set_arbitrary_address_capable(true);
TestDeviceNAME.set_industry_group(1);
TestDeviceNAME.set_device_class(0);
TestDeviceNAME.set_function_code(static_cast<std::uint8_t>(isobus::NAME::Function::SteeringControl));
TestDeviceNAME.set_identity_number(2);
TestDeviceNAME.set_ecu_instance(0);
TestDeviceNAME.set_function_instance(0);
TestDeviceNAME.set_device_class_instance(0);
TestDeviceNAME.set_manufacturer_code(1407);

std::vector<std::uint8_t> version3pool = isobus::IOPFileInterface::read_iop_file("VT3TestPool.iop");
std::vector<std::uint8_t> version4pool = isobus::IOPFileInterface::read_iop_file("window_masks.iop");

if (version3pool.empty())
{
std::cout << "Failed to load object pool from VT3TestPool.iop" << std::endl;
return -3;
}
std::cout << "Loaded object pool from VT3TestPool.iop" << std::endl;

if (version4pool.empty())
{
std::cout << "Failed to load object pool from window_masks.iop" << std::endl;
return -4;
}
std::cout << "Loaded object pool from window_masks.iop" << std::endl;

// Generate a unique version string for this object pool (this is optional, and is entirely application specific behavior)
std::string objectPoolHash = "";

const isobus::NAMEFilter filterVirtualTerminal(isobus::NAME::NAMEParameters::FunctionCode, static_cast<std::uint8_t>(isobus::NAME::Function::VirtualTerminal));
const std::vector<isobus::NAMEFilter> vtNameFilters = { filterVirtualTerminal };
auto TestInternalECU = isobus::CANNetworkManager::CANNetwork.create_internal_control_function(TestDeviceNAME, 0);
auto TestPartnerVT = isobus::CANNetworkManager::CANNetwork.create_partnered_control_function(0, vtNameFilters);

virtualTerminalClient = std::make_shared<isobus::VirtualTerminalClient>(TestPartnerVT, TestInternalECU);
virtualTerminalClient->get_vt_soft_key_event_dispatcher().add_listener(handle_softkey_event);
virtualTerminalClient->get_vt_button_event_dispatcher().add_listener(handle_button_event);
virtualTerminalClient->set_on_ready_for_object_pool_callback([&version3pool, &version4pool, objectPoolHash](isobus::VirtualTerminalClient::VTVersion version) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a (void)version to suppress the unused parameter warning?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead, I'd say we remove the "version" parameter of the callback altogether. There are many more requirements possible as can be seen in #376. We can just let the application access those via more member functions like get_connected_vt_version() does for the version

// You can check the connected VT version if you need to know what features are available, and select which object pool(s) to use based on that.
// This is optional though. If you want, you can just call set_object_pool() blindly exactly one time at any point if you want to try to use the same object pool for all VT versions.
switch (virtualTerminalClient->get_connected_vt_version())
{
case isobus::VirtualTerminalClient::VTVersion::Version3:
{
// For version 3, we upload a base pool with only VT version 3 complaint objects
virtualTerminalClient->set_object_pool(0, version3pool.data(), version3pool.size(), objectPoolHash);
}
break;

case isobus::VirtualTerminalClient::VTVersion::Version4:
case isobus::VirtualTerminalClient::VTVersion::Version5:
case isobus::VirtualTerminalClient::VTVersion::Version6:
{
// For version 4, 5, and 6, we upload the same base pool as version 3, but also upload a second pool with version 4 objects
virtualTerminalClient->set_object_pool(0, version3pool.data(), version3pool.size(), objectPoolHash);
virtualTerminalClient->set_object_pool(1, version4pool.data(), version4pool.size(), objectPoolHash);
}
break;

default:
{
// Either we're not ready yet, or we don't have an object pool for this version
}
break;
}
});
virtualTerminalClient->initialize(true);

virtualTerminalUpdateHelper = std::make_shared<isobus::VirtualTerminalClientUpdateHelper>(virtualTerminalClient);
virtualTerminalUpdateHelper->add_tracked_numeric_value(ButtonExampleNumber_VarNum, 214748364); // In the object pool the output number has an offset of -214748364 so we use this to represent 0.
virtualTerminalUpdateHelper->initialize();

while (running)
{
// CAN stack runs in other threads. Do nothing forever.
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}

virtualTerminalClient->terminate();
isobus::CANHardwareInterface::stop();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// This is a file that will be auto-generated by your object pool designer application.
// These are the object IDs for the objects in the object pool
#define UNDEFINED 65535 //0xFFFF
#define example_WorkingSet 0 //0x0000
#define mainRunscreen_DataMask 1000 //0x03E8
#define example_AlarmMask 2000 //0x07D0
#define exampleNumberInc_Container 3000 //0x0BB8
#define mainRunscreen_SoftKeyMask 4000 //0x0FA0
#define alarm_SKeyMask 4001 //0x0FA1
#define alarm_SoftKey 5000 //0x1388
#define acknowledgeAlarm_SoftKey 5001 //0x1389
#define Plus_Button 6000 //0x1770
#define Minus_Button 6001 //0x1771
#define Title_OutStr 11000 //0x2AF8
#define MainDescription_OutStr 11001 //0x2AF9
#define temp_OutStr_ID_11002 11002 //0x2AFA
#define temp_OutStr_ID_11003 11003 //0x2AFB
#define AlarmMaskTitle_OutStr 11004 //0x2AFC
#define exampleOutput_OutNum 12000 //0x2EE0
#define Title_OutRect 14000 //0x36B0
#define MainRunscreenBackground_OutRect 14001 //0x36B1
#define avatar_OutPict 20000 //0x4E20
#define warningIcon_OutPict 20001 //0x4E21
#define redAlert_OutPict 20002 //0x4E22
#define greenCheck_OutPict 20003 //0x4E23
#define ButtonExampleNumber_VarNum 21000 //0x5208
#define title_OutStr 22000 //0x55F0
#define MainDescription_VarStr 22001 //0x55F1
#define alarmMaskTitle_OutStr 22002 //0x55F2
#define ExampleAlarmMask_VarStr 22003 //0x55F3
#define temp_FontAttr_ID_23000 23000 //0x59D8
#define temp_FontAttr_ID_23001 23001 //0x59D9
#define black48x64_FontAttr 23002 //0x59DA
#define solidBlack_LineAttr 24000 //0x5DC0
#define solidWhite_FillAttr 25000 //0x61A8
#define exampleNumberInc_ObjPtr 27000 //0x6978
Binary file not shown.
Loading
Loading