|
| 1 | +#include "isobus/hardware_integration/available_can_drivers.hpp" |
| 2 | +#include "isobus/hardware_integration/can_hardware_interface.hpp" |
| 3 | +#include "isobus/isobus/can_network_manager.hpp" |
| 4 | +#include "isobus/isobus/can_partnered_control_function.hpp" |
| 5 | +#include "isobus/isobus/can_stack_logger.hpp" |
| 6 | +#include "isobus/isobus/isobus_virtual_terminal_client.hpp" |
| 7 | +#include "isobus/isobus/isobus_virtual_terminal_client_update_helper.hpp" |
| 8 | +#include "isobus/utility/iop_file_interface.hpp" |
| 9 | + |
| 10 | +#include "console_logger.cpp" |
| 11 | +#include "objectPoolObjects.h" |
| 12 | + |
| 13 | +#include <atomic> |
| 14 | +#include <csignal> |
| 15 | +#include <iostream> |
| 16 | + |
| 17 | +//! It is discouraged to use global variables, but it is done here for simplicity. |
| 18 | +static std::shared_ptr<isobus::VirtualTerminalClient> virtualTerminalClient = nullptr; |
| 19 | +static std::shared_ptr<isobus::VirtualTerminalClientUpdateHelper> virtualTerminalUpdateHelper = nullptr; |
| 20 | +static std::atomic_bool running = { true }; |
| 21 | + |
| 22 | +void signal_handler(int) |
| 23 | +{ |
| 24 | + running = false; |
| 25 | +} |
| 26 | + |
| 27 | +// This callback will provide us with event driven notifications of softkey presses from the stack |
| 28 | +void handle_softkey_event(const isobus::VirtualTerminalClient::VTKeyEvent &event) |
| 29 | +{ |
| 30 | + if (event.keyNumber == 0) |
| 31 | + { |
| 32 | + // We have the alarm ACK code, so if we have an active alarm, acknowledge it by going back to the main runscreen |
| 33 | + virtualTerminalUpdateHelper->set_active_data_or_alarm_mask(example_WorkingSet, mainRunscreen_DataMask); |
| 34 | + } |
| 35 | + |
| 36 | + switch (event.keyEvent) |
| 37 | + { |
| 38 | + case isobus::VirtualTerminalClient::KeyActivationCode::ButtonUnlatchedOrReleased: |
| 39 | + { |
| 40 | + switch (event.objectID) |
| 41 | + { |
| 42 | + case alarm_SoftKey: |
| 43 | + { |
| 44 | + virtualTerminalUpdateHelper->set_active_data_or_alarm_mask(example_WorkingSet, example_AlarmMask); |
| 45 | + } |
| 46 | + break; |
| 47 | + |
| 48 | + case acknowledgeAlarm_SoftKey: |
| 49 | + { |
| 50 | + virtualTerminalUpdateHelper->set_active_data_or_alarm_mask(example_WorkingSet, mainRunscreen_DataMask); |
| 51 | + } |
| 52 | + break; |
| 53 | + |
| 54 | + default: |
| 55 | + break; |
| 56 | + } |
| 57 | + } |
| 58 | + break; |
| 59 | + |
| 60 | + default: |
| 61 | + break; |
| 62 | + } |
| 63 | +} |
| 64 | + |
| 65 | +// This callback will provide us with event driven notifications of button presses from the stack |
| 66 | +void handle_button_event(const isobus::VirtualTerminalClient::VTKeyEvent &event) |
| 67 | +{ |
| 68 | + switch (event.keyEvent) |
| 69 | + { |
| 70 | + case isobus::VirtualTerminalClient::KeyActivationCode::ButtonUnlatchedOrReleased: |
| 71 | + case isobus::VirtualTerminalClient::KeyActivationCode::ButtonStillHeld: |
| 72 | + { |
| 73 | + switch (event.objectID) |
| 74 | + { |
| 75 | + case Plus_Button: |
| 76 | + { |
| 77 | + virtualTerminalUpdateHelper->increase_numeric_value(ButtonExampleNumber_VarNum); |
| 78 | + } |
| 79 | + break; |
| 80 | + |
| 81 | + case Minus_Button: |
| 82 | + { |
| 83 | + virtualTerminalUpdateHelper->decrease_numeric_value(ButtonExampleNumber_VarNum); |
| 84 | + } |
| 85 | + break; |
| 86 | + |
| 87 | + default: |
| 88 | + break; |
| 89 | + } |
| 90 | + } |
| 91 | + break; |
| 92 | + |
| 93 | + default: |
| 94 | + break; |
| 95 | + } |
| 96 | +} |
| 97 | + |
| 98 | +int main() |
| 99 | +{ |
| 100 | + std::signal(SIGINT, signal_handler); |
| 101 | + |
| 102 | + // Automatically load the desired CAN driver based on the available drivers |
| 103 | + std::shared_ptr<isobus::CANHardwarePlugin> canDriver = nullptr; |
| 104 | +#if defined(ISOBUS_SOCKETCAN_AVAILABLE) |
| 105 | + canDriver = std::make_shared<isobus::SocketCANInterface>("can0"); |
| 106 | +#elif defined(ISOBUS_WINDOWSPCANBASIC_AVAILABLE) |
| 107 | + canDriver = std::make_shared<isobus::PCANBasicWindowsPlugin>(PCAN_USBBUS1); |
| 108 | +#elif defined(ISOBUS_WINDOWSINNOMAKERUSB2CAN_AVAILABLE) |
| 109 | + canDriver = std::make_shared<isobus::InnoMakerUSB2CANWindowsPlugin>(0); // CAN0 |
| 110 | +#elif defined(ISOBUS_MACCANPCAN_AVAILABLE) |
| 111 | + canDriver = std::make_shared<isobus::MacCANPCANPlugin>(PCAN_USBBUS1); |
| 112 | +#elif defined(ISOBUS_SYS_TEC_AVAILABLE) |
| 113 | + canDriver = std::make_shared<isobus::SysTecWindowsPlugin>(); |
| 114 | +#endif |
| 115 | + if (nullptr == canDriver) |
| 116 | + { |
| 117 | + std::cout << "Unable to find a CAN driver. Please make sure you have one of the above drivers installed with the library." << std::endl; |
| 118 | + std::cout << "If you want to use a different driver, please add it to the list above." << std::endl; |
| 119 | + return -1; |
| 120 | + } |
| 121 | + |
| 122 | + isobus::CANStackLogger::set_can_stack_logger_sink(&logger); |
| 123 | + isobus::CANStackLogger::set_log_level(isobus::CANStackLogger::LoggingLevel::Info); // Change this to Debug to see more information |
| 124 | + isobus::CANHardwareInterface::set_number_of_can_channels(1); |
| 125 | + isobus::CANHardwareInterface::assign_can_channel_frame_handler(0, canDriver); |
| 126 | + |
| 127 | + if ((!isobus::CANHardwareInterface::start()) || (!canDriver->get_is_valid())) |
| 128 | + { |
| 129 | + std::cout << "Failed to start hardware interface. The CAN driver might be invalid." << std::endl; |
| 130 | + return -2; |
| 131 | + } |
| 132 | + |
| 133 | + std::this_thread::sleep_for(std::chrono::milliseconds(250)); |
| 134 | + |
| 135 | + isobus::NAME TestDeviceNAME(0); |
| 136 | + |
| 137 | + //! Make sure you change these for your device!!!! |
| 138 | + TestDeviceNAME.set_arbitrary_address_capable(true); |
| 139 | + TestDeviceNAME.set_industry_group(1); |
| 140 | + TestDeviceNAME.set_device_class(0); |
| 141 | + TestDeviceNAME.set_function_code(static_cast<std::uint8_t>(isobus::NAME::Function::SteeringControl)); |
| 142 | + TestDeviceNAME.set_identity_number(2); |
| 143 | + TestDeviceNAME.set_ecu_instance(0); |
| 144 | + TestDeviceNAME.set_function_instance(0); |
| 145 | + TestDeviceNAME.set_device_class_instance(0); |
| 146 | + TestDeviceNAME.set_manufacturer_code(1407); |
| 147 | + |
| 148 | + std::vector<std::uint8_t> version3pool = isobus::IOPFileInterface::read_iop_file("VT3TestPool.iop"); |
| 149 | + std::vector<std::uint8_t> version4pool = isobus::IOPFileInterface::read_iop_file("window_masks.iop"); |
| 150 | + |
| 151 | + if (version3pool.empty()) |
| 152 | + { |
| 153 | + std::cout << "Failed to load object pool from VT3TestPool.iop" << std::endl; |
| 154 | + return -3; |
| 155 | + } |
| 156 | + std::cout << "Loaded object pool from VT3TestPool.iop" << std::endl; |
| 157 | + |
| 158 | + if (version4pool.empty()) |
| 159 | + { |
| 160 | + std::cout << "Failed to load object pool from window_masks.iop" << std::endl; |
| 161 | + return -4; |
| 162 | + } |
| 163 | + std::cout << "Loaded object pool from window_masks.iop" << std::endl; |
| 164 | + |
| 165 | + // Generate a unique version string for this object pool (this is optional, and is entirely application specific behavior) |
| 166 | + std::string objectPoolHash = ""; |
| 167 | + |
| 168 | + const isobus::NAMEFilter filterVirtualTerminal(isobus::NAME::NAMEParameters::FunctionCode, static_cast<std::uint8_t>(isobus::NAME::Function::VirtualTerminal)); |
| 169 | + const std::vector<isobus::NAMEFilter> vtNameFilters = { filterVirtualTerminal }; |
| 170 | + auto TestInternalECU = isobus::CANNetworkManager::CANNetwork.create_internal_control_function(TestDeviceNAME, 0); |
| 171 | + auto TestPartnerVT = isobus::CANNetworkManager::CANNetwork.create_partnered_control_function(0, vtNameFilters); |
| 172 | + |
| 173 | + virtualTerminalClient = std::make_shared<isobus::VirtualTerminalClient>(TestPartnerVT, TestInternalECU); |
| 174 | + virtualTerminalClient->get_vt_soft_key_event_dispatcher().add_listener(handle_softkey_event); |
| 175 | + virtualTerminalClient->get_vt_button_event_dispatcher().add_listener(handle_button_event); |
| 176 | + virtualTerminalClient->set_on_ready_for_object_pool_callback([&version3pool, &version4pool, objectPoolHash](isobus::VirtualTerminalClient::VTVersion version) { |
| 177 | + // 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. |
| 178 | + // 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. |
| 179 | + switch (virtualTerminalClient->get_connected_vt_version()) |
| 180 | + { |
| 181 | + case isobus::VirtualTerminalClient::VTVersion::Version3: |
| 182 | + { |
| 183 | + // For version 3, we upload a base pool with only VT version 3 complaint objects |
| 184 | + virtualTerminalClient->set_object_pool(0, version3pool.data(), version3pool.size(), objectPoolHash); |
| 185 | + } |
| 186 | + break; |
| 187 | + |
| 188 | + case isobus::VirtualTerminalClient::VTVersion::Version4: |
| 189 | + case isobus::VirtualTerminalClient::VTVersion::Version5: |
| 190 | + case isobus::VirtualTerminalClient::VTVersion::Version6: |
| 191 | + { |
| 192 | + // 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 |
| 193 | + virtualTerminalClient->set_object_pool(0, version3pool.data(), version3pool.size(), objectPoolHash); |
| 194 | + virtualTerminalClient->set_object_pool(1, version4pool.data(), version4pool.size(), objectPoolHash); |
| 195 | + } |
| 196 | + break; |
| 197 | + |
| 198 | + default: |
| 199 | + { |
| 200 | + // Either we're not ready yet, or we don't have an object pool for this version |
| 201 | + } |
| 202 | + break; |
| 203 | + } |
| 204 | + }); |
| 205 | + virtualTerminalClient->initialize(true); |
| 206 | + |
| 207 | + virtualTerminalUpdateHelper = std::make_shared<isobus::VirtualTerminalClientUpdateHelper>(virtualTerminalClient); |
| 208 | + 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. |
| 209 | + virtualTerminalUpdateHelper->initialize(); |
| 210 | + |
| 211 | + while (running) |
| 212 | + { |
| 213 | + // CAN stack runs in other threads. Do nothing forever. |
| 214 | + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); |
| 215 | + } |
| 216 | + |
| 217 | + virtualTerminalClient->terminate(); |
| 218 | + isobus::CANHardwareInterface::stop(); |
| 219 | + return 0; |
| 220 | +} |
0 commit comments