diff --git a/examples/seeder_example/BasePool.iop b/examples/seeder_example/BasePool.iop index 06a0a2380..69a6a3d5b 100644 Binary files a/examples/seeder_example/BasePool.iop and b/examples/seeder_example/BasePool.iop differ diff --git a/examples/seeder_example/object_pool.hpp b/examples/seeder_example/object_pool.hpp index 86dd01ef3..f5323a3a2 100644 --- a/examples/seeder_example/object_pool.hpp +++ b/examples/seeder_example/object_pool.hpp @@ -16,7 +16,9 @@ #define mainRunscreen_DataMask 1000 //0x03E8 #define statisticsRunscreen_DataMask 1001 //0x03E9 #define settingsRunscreen_DataMask 1002 //0x03EA -#define example_AlarmMask 2000 //0x07D0 +#define alarmsRunscreen_DataMask 1003 //0x03EB +#define noSpeed_AlarmMask 2000 //0x07D0 +#define noTaskController_AlarmMask 2001 //0x07D1 #define planterRunscreenStatus_Container 3000 //0x0BB8 #define credits_Container 3001 //0x0BB9 #define section1Switch_Container 3002 //0x0BBA @@ -44,13 +46,15 @@ #define autoManual_Container 3024 //0x0BD0 #define autoMode_Container 3025 //0x0BD1 #define speedReadout_Container 3026 //0x0BD2 +#define noTcAlarmLine_Container 3027 //0x0BD3 +#define noSpeedAlarmLine_Container 3028 //0x0BD4 #define mainRunscreen_SoftKeyMask 4000 //0x0FA0 #define alarm_SKeyMask 4001 //0x0FA1 #define returnHome_SKeyMask 4002 //0x0FA2 #define home_Key 5000 //0x1388 #define acknowledgeAlarm_SoftKey 5001 //0x1389 #define settings_Key 5002 //0x138A -#define info_Key 5003 //0x138B +#define alarms_Key 5003 //0x138B #define statistics_Key 5004 //0x138C #define section1Toggle_Button 6000 //0x1770 #define section2Toggle_Button 6001 //0x1771 @@ -64,7 +68,7 @@ #define onOffIconCredit_OutStr 11001 //0x2AF9 #define speed_OutStr 11002 //0x2AFA #define temp_OutStr_ID_11003 11003 //0x2AFB -#define AlarmMaskTitle_OutStr 11004 //0x2AFC +#define noMachineSpeedTitle_OutStr 11004 //0x2AFC #define longTest_OutStr 11005 //0x2AFD #define credits_OutStr 11006 //0x2AFE #define planterIconCredit_OutStr 11007 //0x2AFF @@ -91,6 +95,13 @@ #define listItemCredits_OutStr 11028 //0x2B14 #define unitKph_OutStr 11029 //0x2B15 #define unitMph_OutStr 11030 //0x2B16 +#define machineSpeedNotDetectedSummary_OutStr 11031 //0x2B17 +#define noTCTitle_OutStr 11032 //0x2B18 +#define TCNotConnectedSummary_OutStr 11033 //0x2B19 +#define currentAlarmsHeader_OutStr 11034 //0x2B1A +#define noActiveAlarms_OutStr 11035 //0x2B1B +#define NoTaskController_OutStr 11036 //0x2B1C +#define NoMachineSpeed_OutStr 11037 //0x2B1D #define busload_OutNum 12000 //0x2EE0 #define canAddress_OutNum 12001 //0x2EE1 #define utVersion_OutNum 12002 //0x2EE2 @@ -105,6 +116,7 @@ #define zero_OutNum 12011 //0x2EEB #define twenty_OutNum 12012 //0x2EEC #define thirty_OutNum 12013 //0x2EED +#define headerBorder_OutLine 13000 //0x32C8 #define Title_OutRect 14000 //0x36B0 #define MainRunscreenBackground_OutRect 14001 //0x36B1 #define section1Status_OutRect 14002 //0x36B2 @@ -116,10 +128,12 @@ #define dropdown_OutRect 14008 //0x36B8 #define hamburgerLine_OutRect 14009 //0x36B9 #define readoutGeneric_OutRect 14010 //0x36BA +#define buttonCover_OutRect 14011 //0x36BB +#define temp_OutEllipse 15000 //0x3A98 #define currentSpeed_OutMeter 17000 //0x4268 #define avatar_OutPict 20000 //0x4E20 #define warningIcon_OutPict 20001 //0x4E21 -#define redAlert_OutPict 20002 //0x4E22 +#define warning_OutPict 20002 //0x4E22 #define greenCheck_OutPict 20003 //0x4E23 #define gear_OutPict 20004 //0x4E24 #define planter_OutPict 20005 //0x4E25 @@ -130,6 +144,7 @@ #define manual_OutPict 20010 //0x4E2A #define info_OutPict 20011 //0x4E2B #define stats_OutPict 20012 //0x4E2C +#define Copy1_info_OutPict 20013 //0x4E2D #define ButtonExampleNumber_VarNum 21000 //0x5208 #define statisticsSelection_VarNum 21001 //0x5209 #define busload_VarNum 21002 //0x520A @@ -169,11 +184,17 @@ #define infoIconsCredit_VarStr 22023 //0x5607 #define statisticsIconsCredit_VarStr 22024 //0x5608 #define speed_VarStr 22025 //0x5609 +#define machineSpeedNotDetectedSummary_VarStr 22026 //0x560A +#define noTaskController_VarStr 22027 //0x560B +#define noTCSummary_VarStr 22028 //0x560C +#define currentAlarms_VarStr 22029 //0x560D +#define noActiveAlarms_VarStr 22030 //0x560E #define temp_FontAttr_ID_23000 23000 //0x59D8 #define temp_FontAttr_ID_23001 23001 //0x59D9 #define black48x64_FontAttr 23002 //0x59DA #define unitFont_FontAttr 23003 //0x59DB #define listItem_FontAttr 23004 //0x59DC +#define blackBold24x32_FontAttr 23005 //0x59DD #define solidBlack_LineAttr 24000 //0x5DC0 #define suppressed_LineAttr 24001 //0x5DC1 #define solidWhite_FillAttr 25000 //0x61A8 @@ -190,5 +211,7 @@ #define section6EnableState_ObjPtr 27006 //0x697E #define selectedStatisticsContainer_ObjPtr 27007 //0x697F #define autoManual_ObjPtr 27008 //0x6980 +#define currentAlarms1_ObjPtr 27009 //0x6981 +#define currentAlarms2_ObjPtr 27010 //0x6982 #endif // OBJECT_POOL_HPP diff --git a/examples/seeder_example/seeder.hpp b/examples/seeder_example/seeder.hpp index b350d8ee7..246a31d7d 100644 --- a/examples/seeder_example/seeder.hpp +++ b/examples/seeder_example/seeder.hpp @@ -9,8 +9,8 @@ #ifndef SEEDER_HPP #define SEEDER_HPP -#include "vt_application.hpp" #include "isobus/isobus/isobus_diagnostic_protocol.hpp" +#include "vt_application.hpp" class Seeder { diff --git a/examples/seeder_example/vt_application.cpp b/examples/seeder_example/vt_application.cpp index 431829d34..6d7703e7c 100644 --- a/examples/seeder_example/vt_application.cpp +++ b/examples/seeder_example/vt_application.cpp @@ -20,7 +20,9 @@ const std::map SeederVtApplica { ActiveScreen::Main, mainRunscreen_DataMask }, { ActiveScreen::Settings, settingsRunscreen_DataMask }, { ActiveScreen::Statistics, statisticsRunscreen_DataMask }, - { ActiveScreen::Info, mainRunscreen_DataMask } + { ActiveScreen::Alarms, alarmsRunscreen_DataMask }, + { ActiveScreen::NoMachineSpeed, noSpeed_AlarmMask }, + { ActiveScreen::NoTaskController, noTaskController_AlarmMask } }; const std::map SeederVtApplication::STATISTICS_CONTAINER_MAP = { @@ -49,6 +51,16 @@ const std::array S section6EnableState_ObjPtr }; +const std::array(SeederVtApplication::Alarm::NumberOfAlarms)> SeederVtApplication::ALARM_SCREENS = { + ActiveScreen::NoMachineSpeed, + ActiveScreen::NoTaskController +}; + +const std::array(SeederVtApplication::Alarm::NumberOfAlarms)> SeederVtApplication::ALARM_DESCRIPTION_LINES = { + NoMachineSpeed_OutStr, + NoTaskController_OutStr +}; + SeederVtApplication::SeederVtApplication(std::shared_ptr VTPartner, std::shared_ptr TCPartner, std::shared_ptr source) : TCClientInterface(TCPartner, source, nullptr), VTClientInterface(VTPartner, source), @@ -131,9 +143,24 @@ void SeederVtApplication::handle_vt_key_events(const isobus::VirtualTerminalClie } break; - case info_Key: + case alarms_Key: + { + set_currently_active_screen(ActiveScreen::Alarms); + } + break; + + case acknowledgeAlarm_SoftKey: { - set_currently_active_screen(ActiveScreen::Info); + for (std::size_t i = 0; i < alarmConditions.size(); i++) + { + if ((isobus::SystemTiming::time_expired_ms(alarmConditions.at(i).conditionTimestamp, alarmConditions.at(i).conditionTimeout)) && + (false == alarmConditions.at(i).acknowledged)) + { + alarmConditions.at(i).acknowledged = true; + set_currently_active_screen(previousActiveScreen); + break; + } + } } break; @@ -231,11 +258,12 @@ void SeederVtApplication::handle_machine_selected_speed(const std::shared_ptr(UpdateVTStateFlags::UpdateSpeed_OutNum)); txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateSpeedUnits_ObjPtr)); } + + if (nullptr != TCControlFunction) + { + set_current_tc_address(TCControlFunction->get_address()); + } + set_current_tc_number_booms(TCClientInterface.get_connected_tc_number_booms_supported()); + set_current_tc_number_channels(TCClientInterface.get_connected_tc_number_channels_supported()); + set_current_tc_number_sections(TCClientInterface.get_connected_tc_number_sections_supported()); + set_current_tc_version(TCClientInterface.get_connected_tc_version()); + update_alarms(); + slowUpdateTimestamp_ms = isobus::SystemTiming::get_timestamp_ms(); } speedMessages.update(); @@ -338,6 +377,42 @@ void SeederVtApplication::processFlags(std::uint32_t flag, void *parentPointer) } break; + case UpdateVTStateFlags::UpdateCurrentAlarms1_ObjPtr: + { + if (seeder->get_number_active_alarms() > 0) + { + auto alarmToShow = seeder->get_active_alarm_by_priority_index(0); + + if (Alarm::NumberOfAlarms != alarmToShow) + { + transmitSuccessful = seeder->VTClientInterface.send_change_numeric_value(currentAlarms1_ObjPtr, ALARM_DESCRIPTION_LINES.at(static_cast(alarmToShow))); + } // else, process the flag again on the next loop + } + else + { + transmitSuccessful = seeder->VTClientInterface.send_change_numeric_value(currentAlarms1_ObjPtr, noActiveAlarms_OutStr); + } + } + break; + + case UpdateVTStateFlags::UpdateCurrentAlarms2_ObjPtr: + { + if (seeder->get_number_active_alarms() > 1) + { + auto alarmToShow = seeder->get_active_alarm_by_priority_index(1); + + if (Alarm::NumberOfAlarms != alarmToShow) + { + transmitSuccessful = seeder->VTClientInterface.send_change_numeric_value(currentAlarms2_ObjPtr, ALARM_DESCRIPTION_LINES.at(static_cast(alarmToShow))); + } // else, process the flag again on the next loop + } + else + { + transmitSuccessful = seeder->VTClientInterface.send_change_numeric_value(currentAlarms2_ObjPtr, UNDEFINED); + } + } + break; + case UpdateVTStateFlags::UpdateStatisticsSelection_VarNum: { if (seeder->get_is_object_shown(statisticsSelection_VarNum)) @@ -454,6 +529,79 @@ void SeederVtApplication::processFlags(std::uint32_t flag, void *parentPointer) } break; + case UpdateVTStateFlags::UpdateTcVersion_VarNum: + { + if (seeder->get_is_object_shown(tcVersion_VarNum)) + { + transmitSuccessful = seeder->VTClientInterface.send_change_numeric_value(tcVersion_VarNum, static_cast(seeder->TCClientInterface.get_connected_tc_version())); + } + } + break; + + case UpdateVTStateFlags::UpdateTcAddress_VarNum: + { + if (seeder->get_is_object_shown(tcAddress_VarNum)) + { + if (nullptr != seeder->TCClientInterface.get_partner_control_function()) + { + transmitSuccessful = seeder->VTClientInterface.send_change_numeric_value(tcAddress_VarNum, seeder->TCClientInterface.get_partner_control_function()->get_address()); + } + else + { + transmitSuccessful = seeder->VTClientInterface.send_change_numeric_value(tcAddress_VarNum, 0xFE); + } + } + } + break; + + case UpdateVTStateFlags::UpdateTcNumberBoomsSupported_VarNum: + { + if (seeder->get_is_object_shown(tcNumberBoomsSupported_VarNum)) + { + if (nullptr != seeder->TCClientInterface.get_partner_control_function()) + { + transmitSuccessful = seeder->VTClientInterface.send_change_numeric_value(tcNumberBoomsSupported_VarNum, seeder->TCClientInterface.get_connected_tc_number_booms_supported()); + } + else + { + transmitSuccessful = seeder->VTClientInterface.send_change_numeric_value(tcNumberBoomsSupported_VarNum, 0); + } + } + } + break; + + case UpdateVTStateFlags::UpdateTcSupportedSections_VarNum: + { + if (seeder->get_is_object_shown(tcSupportedSections_VarNum)) + { + if (nullptr != seeder->TCClientInterface.get_partner_control_function()) + { + transmitSuccessful = seeder->VTClientInterface.send_change_numeric_value(tcSupportedSections_VarNum, seeder->TCClientInterface.get_connected_tc_number_sections_supported()); + } + else + { + transmitSuccessful = seeder->VTClientInterface.send_change_numeric_value(tcSupportedSections_VarNum, 0); + } + } + } + break; + + case UpdateVTStateFlags::UpdateTcControlChannels_VarNum: + { + if (seeder->get_is_object_shown(tcControlChannels_VarNum)) + { + if (nullptr != seeder->TCClientInterface.get_partner_control_function()) + { + transmitSuccessful = seeder->VTClientInterface.send_change_numeric_value(tcControlChannels_VarNum, seeder->TCClientInterface.get_connected_tc_number_channels_supported()); + } + else + { + transmitSuccessful = seeder->VTClientInterface.send_change_numeric_value(tcControlChannels_VarNum, 0); + } + } + } + break; + case UpdateVTStateFlags::UpdateSection1Status_OutRect: case UpdateVTStateFlags::UpdateSection2Status_OutRect: case UpdateVTStateFlags::UpdateSection3Status_OutRect: @@ -580,6 +728,46 @@ bool SeederVtApplication::get_is_object_shown(std::uint16_t objectID) } break; + case tcVersion_VarNum: + case tcAddress_VarNum: + case tcNumberBoomsSupported_VarNum: + case tcSupportedSections_VarNum: + case tcControlChannels_VarNum: + { + retVal = ((ActiveScreen::Statistics == currentlyActiveScreen) && + (Statistics::TaskController == currentlySelectedStatistic)); + } + break; + + case machineSpeedNotDetectedSummary_OutStr: + { + retVal = (ActiveScreen::NoMachineSpeed == currentlyActiveScreen); + } + break; + + case TCNotConnectedSummary_OutStr: + case noTCTitle_OutStr: + { + retVal = (ActiveScreen::NoTaskController == currentlyActiveScreen); + } + break; + + case warning_OutPict: + case alarm_SKeyMask: + { + retVal = ((ActiveScreen::NoTaskController == currentlyActiveScreen) || + (ActiveScreen::NoMachineSpeed == currentlyActiveScreen)); + } + break; + + case currentAlarms1_ObjPtr: + case currentAlarms2_ObjPtr: + case currentAlarmsHeader_OutStr: + { + retVal = (ActiveScreen::Alarms == currentlyActiveScreen); + } + break; + default: { retVal = true; @@ -590,10 +778,46 @@ bool SeederVtApplication::get_is_object_shown(std::uint16_t objectID) return retVal; } +std::size_t SeederVtApplication::get_number_active_alarms() const +{ + std::size_t retVal = 0; + + for (auto &alarm : alarmConditions) + { + if (alarm.active) + { + retVal++; + } + } + return retVal; +} + +SeederVtApplication::Alarm SeederVtApplication::get_active_alarm_by_priority_index(std::size_t index) const +{ + std::size_t numberOfProcessedAlarms = 0; + Alarm retVal = Alarm::NumberOfAlarms; + + for (std::size_t i = 0; i < alarmConditions.size(); i++) + { + if (alarmConditions.at(i).active) + { + numberOfProcessedAlarms++; + } + + if (numberOfProcessedAlarms == (index + 1)) + { + retVal = static_cast(i); + break; + } + } + return retVal; +} + void SeederVtApplication::set_currently_active_screen(ActiveScreen newScreen) { if (newScreen != currentlyActiveScreen) { + previousActiveScreen = currentlyActiveScreen; currentlyActiveScreen = newScreen; txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateActiveDataMask)); } @@ -644,3 +868,107 @@ void SeederVtApplication::set_current_ut_address(std::uint8_t address) txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateUtAddress_VarNum)); } } + +void SeederVtApplication::set_current_tc_address(std::uint8_t address) +{ + if (address != tcAddress) + { + tcAddress = address; + txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateTcAddress_VarNum)); + } +} + +void SeederVtApplication::set_current_tc_version(isobus::TaskControllerClient::Version version) +{ + if (version != tcVersion) + { + tcVersion = version; + txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateTcVersion_VarNum)); + } +} + +void SeederVtApplication::set_current_tc_number_booms(std::uint8_t numberBooms) +{ + if (numberBooms != tcNumberBooms) + { + tcNumberBooms = numberBooms; + txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateTcNumberBoomsSupported_VarNum)); + } +} + +void SeederVtApplication::set_current_tc_number_channels(std::uint8_t numberChannels) +{ + if (numberChannels != tcNumberChannels) + { + tcNumberChannels = numberChannels; + txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateTcControlChannels_VarNum)); + } +} + +void SeederVtApplication::set_current_tc_number_sections(std::uint8_t numberSections) +{ + if (numberSections != tcNumberSections) + { + tcNumberSections = numberSections; + txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateTcSupportedSections_VarNum)); + } +} + +void SeederVtApplication::update_alarms() +{ + bool updateShownMask = false; + + if (0 == speedMessages.get_number_received_machine_selected_speed_command_sources()) + { + if (isobus::SystemTiming::time_expired_ms(alarmConditions.at(static_cast(Alarm::NoMachineSpeed)).conditionTimestamp, alarmConditions.at(static_cast(Alarm::NoMachineSpeed)).conditionTimeout)) + { + updateShownMask = true; + alarmConditions.at(static_cast(Alarm::NoMachineSpeed)).active = true; + } + else + { + alarmConditions.at(static_cast(Alarm::NoMachineSpeed)).active = false; + } + } + else + { + alarmConditions.at(static_cast(Alarm::NoMachineSpeed)).conditionTimestamp = isobus::SystemTiming::get_timestamp_ms(); + alarmConditions.at(static_cast(Alarm::NoMachineSpeed)).acknowledged = false; + alarmConditions.at(static_cast(Alarm::NoMachineSpeed)).active = false; + } + + if (false == TCClientInterface.get_is_connected()) + { + if (isobus::SystemTiming::time_expired_ms(alarmConditions.at(static_cast(Alarm::NoTaskController)).conditionTimestamp, alarmConditions.at(static_cast(Alarm::NoTaskController)).conditionTimeout)) + { + updateShownMask = true; + alarmConditions.at(static_cast(Alarm::NoTaskController)).active = true; + } + else + { + alarmConditions.at(static_cast(Alarm::NoTaskController)).active = false; + } + } + else + { + alarmConditions.at(static_cast(Alarm::NoTaskController)).conditionTimestamp = isobus::SystemTiming::get_timestamp_ms(); + alarmConditions.at(static_cast(Alarm::NoTaskController)).acknowledged = false; + alarmConditions.at(static_cast(Alarm::NoTaskController)).active = false; + } + + if (updateShownMask) + { + for (std::size_t i = 0; i < alarmConditions.size(); i++) + { + if ((alarmConditions.at(i).active) && + (false == alarmConditions.at(i).acknowledged)) + { + set_currently_active_screen(ALARM_SCREENS.at(i)); + break; + } + } + } + // Not ideal, but since we filter on shown screen it's not that bad. Todo, update these flags to be event driven + txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateCurrentAlarms1_ObjPtr)); + txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateCurrentAlarms2_ObjPtr)); +} diff --git a/examples/seeder_example/vt_application.hpp b/examples/seeder_example/vt_application.hpp index c13c55bb8..09b802041 100644 --- a/examples/seeder_example/vt_application.hpp +++ b/examples/seeder_example/vt_application.hpp @@ -1,40 +1,49 @@ //================================================================================================ /// @file vt_application.hpp /// -/// @brief This is the definition of the VT portion of the seeder example +/// @brief This is the definition of a class that handles the main application logic for this +/// example application. /// @author Adrian Del Grosso /// -/// @copyright 2023 Adrian Del Grosso +/// @copyright 2023 Adrian Del Grosso and the AgIsoStack++ developers //================================================================================================ #ifndef VT_APPLICATION_HPP #define VT_APPLICATION_HPP +#include "isobus/isobus/isobus_speed_distance_messages.hpp" +#include "isobus/isobus/isobus_task_controller_client.hpp" #include "isobus/isobus/isobus_virtual_terminal_client.hpp" #include "section_control_implement_sim.hpp" -#include "isobus/isobus/isobus_task_controller_client.hpp" -#include "isobus/isobus/isobus_speed_distance_messages.hpp" +/// @brief A class that manages the main application logic for this example program. class SeederVtApplication { public: + /// @brief Enumerates the different screens (data/alarm masks) that the app can display enum class ActiveScreen { Main, Settings, Statistics, - Info + Alarms, + NoMachineSpeed, + NoTaskController }; + /// @brief Constructor for a SeederVtApplication SeederVtApplication(std::shared_ptr VTPartner, std::shared_ptr TCPartner, std::shared_ptr source); + /// @brief Initializes the class. Should be called before update is called the first time bool initialize(); - isobus::TaskControllerClient TCClientInterface; - isobus::VirtualTerminalClient VTClientInterface; + isobus::TaskControllerClient TCClientInterface; ///< An instance of the application's task controller interface + isobus::VirtualTerminalClient VTClientInterface; ///< An instance of the application's universal/virtual terminal interface + /// @brief Cyclically updates the application void update(); private: + /// @brief A set of flags which are used to manage updating objects in the object pool at runtime. enum class UpdateVTStateFlags : std::uint32_t { UpdateActiveDataMask = 0, @@ -48,6 +57,8 @@ class SeederVtApplication UpdateSelectedStatisticsContainer_ObjPtr, UpdateAutoManual_ObjPtr, UpdateSpeedUnits_ObjPtr, + UpdateCurrentAlarms1_ObjPtr, + UpdateCurrentAlarms2_ObjPtr, UpdateStatisticsSelection_VarNum, UpdateCanAddress_VarNum, @@ -55,6 +66,11 @@ class SeederVtApplication UpdateUtVersion_VarNum, UpdateUtAddress_VarNum, UpdateCurrentSpeedMeter_VarNum, + UpdateTcVersion_VarNum, + UpdateTcAddress_VarNum, + UpdateTcNumberBoomsSupported_VarNum, + UpdateTcSupportedSections_VarNum, + UpdateTcControlChannels_VarNum, UpdateSection1Status_OutRect, UpdateSection2Status_OutRect, @@ -68,6 +84,7 @@ class SeederVtApplication NumberOfFlags }; + /// @brief Enumerates the statistics in the statistics input list enum class Statistics : std::uint16_t { None = 0, @@ -77,43 +94,135 @@ class SeederVtApplication Credits }; + /// @brief Stores information associated to if an alarm mask should be shown + class AlarmMaskCondition + { + public: + AlarmMaskCondition() = default; + std::uint32_t conditionTimestamp = 0; + std::uint32_t conditionTimeout = 10000; + bool acknowledged = false; + bool active = false; + }; + + /// @brief Lists the different alarm conditions that might exist + enum class Alarm : std::uint8_t + { + NoMachineSpeed = 0, ///< No MSS message, needed for section control + NoTaskController, ///< No TC, makes the demo less interesting + + NumberOfAlarms ///< The number of alarms in this enumeration + }; + + /// @brief Catches UpdateVTStateFlags and uses them to transmit messages to the VT at runtime to adjust the object pool's state static void processFlags(std::uint32_t flag, void *parentPointer); + + /// @brief A callback for handling VT softkey events + /// @param[in] event The event data to process void handle_vt_key_events(const isobus::VirtualTerminalClient::VTKeyEvent &event); + + /// @brief A callback for handling VT numeric value events (user enters a new value, for example) + /// @param[in] event The event data to process void handle_numeric_value_events(const isobus::VirtualTerminalClient::VTChangeNumericValueEvent &event); + + /// @brief A callback for handling machine selected speed events, used to set appropriate VT flags + /// @param[in] event The event data to process void handle_machine_selected_speed(const std::shared_ptr mssData, bool changed); + /// @brief Returns if the selected object ID is shown currently + /// @param[in] objectID The object ID of the object to check the shown state of + /// @returns True if the selected object ID is shown currently, otherwise false bool get_is_object_shown(std::uint16_t objectID); + /// @brief Returns the number of active alarms + /// @returns The number of currently active alarms + std::size_t get_number_active_alarms() const; + + /// @brief Returns active alarms by index/priority + /// @returns Active alarms by index/priority or NumberOfAlarms if index is invalid + Alarm get_active_alarm_by_priority_index(std::size_t index) const; + + /// @brief Sets the currently active screen, which sets the appropriate VT flag to change the active mask if needed + /// @param[in] newScreen The screen to set as the active screen void set_currently_active_screen(ActiveScreen newScreen); + + /// @brief Sets the current busload, which is used to compare against the NetworkManager's reported + /// load each loop to update the displayed load if needed. + /// @param[in] busload The busload to set, in percent void set_current_busload(float busload); + + /// @brief Sets the current address of our ICF, used to tell if it ever changes when + /// compared against the address reported by the stack so the VT flag can be set. + /// @param[in] address The address to set void set_current_can_address(std::uint8_t address); + + /// @brief Sets the statistic that is selected in the stats data mask's input list + /// @param[in] newSelectedStatistic The statistic to set as the currently selected one void set_selected_statistic(Statistics newSelectedStatistic); + + /// @brief Sets the connected UT's reported version, used to update the displayed value in the object pool + /// @param[in] version The UT version to set void set_current_ut_version(isobus::VirtualTerminalClient::VTVersion version); + + /// @brief Sets the connected UT's CAN address, used to update the displayed value in the object pool + /// @param[in] address The UT's address to set void set_current_ut_address(std::uint8_t address); - static constexpr std::uint8_t NUMBER_ONSCREEN_SECTIONS = 6; - static const std::map SCREEN_TO_DATA_MASK_MAP; - static const std::map STATISTICS_CONTAINER_MAP; - static const std::array SECTION_STATUS_OUTRECTS; - static const std::array SECTION_SWITCH_STATES; - - SectionControlImplementSimulator sectionControl; - isobus::ProcessingFlags txFlags; - std::vector objectPool; - std::shared_ptr softkeyEventListener = nullptr; - std::shared_ptr buttonEventListener = nullptr; - std::shared_ptr numericValueEventListener = nullptr; - float currentBusload = 0.0f; - ActiveScreen currentlyActiveScreen = ActiveScreen::Main; - Statistics currentlySelectedStatistic = Statistics::None; + /// @brief Sets the connected TC's CAN address, used to update the displayed value in the object pool + /// @param[in] address The TC's address to set + void set_current_tc_address(std::uint8_t address); + + /// @brief Sets the connected TC's reported version, used to update the displayed value in the object pool + /// @param[in] version The TC's version to set + void set_current_tc_version(isobus::TaskControllerClient::Version version); + + /// @brief Sets the connected TC's reported number of supported booms, used to update the displayed value in the object pool + /// @param[in] numberBooms The TC's reported number of booms to set + void set_current_tc_number_booms(std::uint8_t numberBooms); + + /// @brief Sets the connected TC's reported number of supported channels, used to update the displayed value in the object pool + /// @param[in] numberChannels The TC's reported number of channels to set + void set_current_tc_number_channels(std::uint8_t numberChannels); + + /// @brief Sets the connected TC's reported number of supported sections, used to update the displayed value in the object pool + /// @param[in] numberSections The TC's reported number of sections to set + void set_current_tc_number_sections(std::uint8_t numberSections); + + /// @brief Called cyclically by the update routine, checks if any alarm masks need to be shown to the user + void update_alarms(); + + static constexpr std::uint8_t NUMBER_ONSCREEN_SECTIONS = 6; ///< The number of sections we can display on the screen + static const std::map SCREEN_TO_DATA_MASK_MAP; ///< A map between our screen enum and the corresponding data masks' object IDs + static const std::map STATISTICS_CONTAINER_MAP; ///< Maps selected statistics to corresponding container object IDs + static const std::array SECTION_STATUS_OUTRECTS; ///< An array of object IDs corresponding to the on screen section status objects + static const std::array SECTION_SWITCH_STATES; ///< An array of object IDs corresponding to the on screen section switch objects + static const std::array(Alarm::NumberOfAlarms)> ALARM_SCREENS; ///< Used to convert index of alarm to active screen + static const std::array(Alarm::NumberOfAlarms)> ALARM_DESCRIPTION_LINES; ///< Stores object IDs corresponding to each alarm's one line description + + SectionControlImplementSimulator sectionControl; ///< A class that manages section control + isobus::ProcessingFlags txFlags; ///< A set of flags to handle transmitting VT messages and retries as needed + std::vector objectPool; ///< Stores our object pool + std::array(Alarm::NumberOfAlarms)> alarmConditions; ///< Tracks alarm conditions in priority order + std::shared_ptr softkeyEventListener = nullptr; ///< Stores a handle for soft key event callbacks + std::shared_ptr buttonEventListener = nullptr; ///< Stores a handle for button event callbacks + std::shared_ptr numericValueEventListener = nullptr; ///< Stores a handle for numeric value event callbacks + float currentBusload = 0.0f; ///< Stores the current CAN bus load, used to detect changes and update the UT value + ActiveScreen currentlyActiveScreen = ActiveScreen::Main; ///< The currently active data mask + ActiveScreen previousActiveScreen = ActiveScreen::Main; ///< Stores the previous active screen so it can be returned to if needed + Statistics currentlySelectedStatistic = Statistics::None; ///< The currently selected statistic on the stats data mask isobus::LanguageCommandInterface::DistanceUnits distanceUnits = isobus::LanguageCommandInterface::DistanceUnits::Metric; ///< Stores the current displayed distance units isobus::VirtualTerminalClient::VTVersion utVersion = isobus::VirtualTerminalClient::VTVersion::ReservedOrUnknown; ///< Stores the UT's version for display in the pool + isobus::TaskControllerClient::Version tcVersion = isobus::TaskControllerClient::Version::Unknown; ///< Stores the TC's version for display in the object pool isobus::SpeedMessagesInterface speedMessages; ///< Interface for reading speed from the bus std::shared_ptr ddop = nullptr; ///< Stores our application's DDOP std::shared_ptr &, const bool &)>> machineSelectedSpeedEventHandle; ///< Handle for MSS events std::uint32_t slowUpdateTimestamp_ms = 0; ///< A timestamp to limit some polled data to 1Hz update rate std::uint8_t canAddress = 0xFE; ///< Stores our CAN address so we know if it changes and can update it in the pool std::uint8_t utAddress = 0xFE; ///< Stores the UT's current address so we can tell if it changes and update it in the pool + std::uint8_t tcAddress = 0xFE; ///< Stores the TC's current address so we can tell if it changes and update it in the pool + std::uint8_t tcNumberBooms = 0; ///< Stores the TC's number of supported booms so we can tell if it changes and update it in the pool + std::uint8_t tcNumberSections = 0; ///< Stores the TC's number of supported sections so we can tell if it changes and update it in the pool + std::uint8_t tcNumberChannels = 0; ///< Stores the TC's number of control channels so we can tell if it changes and update it in the pool bool languageDataRequested = false; ///< Stores if we've requested the current language data yet };