From 5f9fffd896d394356df9b6f3e3df5ffb37e08917 Mon Sep 17 00:00:00 2001 From: Adrian Del Grosso <10929341+ad3154@users.noreply.github.com> Date: Sat, 22 Jul 2023 19:02:16 -0500 Subject: [PATCH] [Examples]: Finalize first version of advanced example Added multiple speed source selection to seeder example. Added lots of TC enhancements to the seeder example, now reporting seed counts instead of random other things and properly reporting some values that the TC is requesting instead of sending zeros. Alarm updates, add enable/disable. --- examples/seeder_example/BasePool.iop | Bin 39736 -> 39826 bytes examples/seeder_example/object_pool.hpp | 5 + .../section_control_implement_sim.cpp | 119 +++++-- .../section_control_implement_sim.hpp | 27 +- examples/seeder_example/seeder.cpp | 1 + examples/seeder_example/vt_application.cpp | 307 ++++++++++++++---- examples/seeder_example/vt_application.hpp | 26 ++ 7 files changed, 393 insertions(+), 92 deletions(-) diff --git a/examples/seeder_example/BasePool.iop b/examples/seeder_example/BasePool.iop index 69a6a3d5bd4202b8e1a9778ed62914c4a4c7f800..38af5e05a67909d2e5ba4afdbb9c80b44feabd58 100644 GIT binary patch delta 363 zcmdn7jcL+$rU_XJtPC5>7=U0SkhEc7(B@;{WVp)h$>7g$!U!ZX*>RT6#=1$&+LyVR zColqqm>6bQF))ZQ$Y_fIm0m#;l?91z=4Vl1c9hWOKFS!tz<4*3kvohLXoaLU_XI`> zM#dYF%sgQrJEcG>v>AW`jJ#kK(g+oNVGNTm&r;oN%s!2g>x2>Sf!GI(Ktm><;Fv2l z!;1evEEfY8(1@rti3}M|f+-9v3=F*NTQ(PPy0$awZr;$|#4>s7gaAga&1@5knJ3Fn zRh}F?HCvcBOpK4gxwNP#HLpa$F(z%(JpL&z#uIGO1^}EcL1+K~ diff --git a/examples/seeder_example/object_pool.hpp b/examples/seeder_example/object_pool.hpp index f5323a3a..a4c8044f 100644 --- a/examples/seeder_example/object_pool.hpp +++ b/examples/seeder_example/object_pool.hpp @@ -48,6 +48,7 @@ #define speedReadout_Container 3026 //0x0BD2 #define noTcAlarmLine_Container 3027 //0x0BD3 #define noSpeedAlarmLine_Container 3028 //0x0BD4 +#define enableAlarms_Container 3029 //0x0BD5 #define mainRunscreen_SoftKeyMask 4000 //0x0FA0 #define alarm_SKeyMask 4001 //0x0FA1 #define returnHome_SKeyMask 4002 //0x0FA2 @@ -63,6 +64,7 @@ #define section5Toggle_Button 6004 //0x1774 #define section6Toggle_Button 6005 //0x1775 #define autoManualToggle_Button 6006 //0x1776 +#define enableAlarms_InBool 7000 //0x1B58 #define statistics_InList 10000 //0x2710 #define Title_OutStr 11000 //0x2AF8 #define onOffIconCredit_OutStr 11001 //0x2AF9 @@ -102,6 +104,7 @@ #define noActiveAlarms_OutStr 11035 //0x2B1B #define NoTaskController_OutStr 11036 //0x2B1C #define NoMachineSpeed_OutStr 11037 //0x2B1D +#define enableAlarms_OutStr 11038 //0x2B1E #define busload_OutNum 12000 //0x2EE0 #define canAddress_OutNum 12001 //0x2EE1 #define utVersion_OutNum 12002 //0x2EE2 @@ -158,6 +161,7 @@ #define tcControlChannels_VarNum 21010 //0x5212 #define currentSpeedMeter_VarNum 21011 //0x5213 #define currentSpeedReadout_VarNum 21012 //0x5214 +#define enableAlarms_VarNum 21013 //0x5215 #define title_OutStr 22000 //0x55F0 #define onOffIconsCredit_VarStr 22001 //0x55F1 #define alarmMaskTitle_VarStr 22002 //0x55F2 @@ -189,6 +193,7 @@ #define noTCSummary_VarStr 22028 //0x560C #define currentAlarms_VarStr 22029 //0x560D #define noActiveAlarms_VarStr 22030 //0x560E +#define enableAlarms_VarStr 22031 //0x560F #define temp_FontAttr_ID_23000 23000 //0x59D8 #define temp_FontAttr_ID_23001 23001 //0x59D9 #define black48x64_FontAttr 23002 //0x59DA diff --git a/examples/seeder_example/section_control_implement_sim.cpp b/examples/seeder_example/section_control_implement_sim.cpp index a43ebf48..a1c4cad3 100644 --- a/examples/seeder_example/section_control_implement_sim.cpp +++ b/examples/seeder_example/section_control_implement_sim.cpp @@ -9,6 +9,7 @@ void SectionControlImplementSimulator::set_number_of_sections(std::uint8_t value) { sectionStates.resize(value); + sectionSetpoints.resize(value); switchStates.resize(value); } @@ -31,9 +32,23 @@ bool SectionControlImplementSimulator::get_section_state(std::uint8_t index) con return sectionStates.at(index); } +void SectionControlImplementSimulator::set_section_setpoint_state(std::uint8_t index, bool value) +{ + if (index < sectionSetpoints.size()) + { + sectionSetpoints.at(index) = value; + } +} + +bool SectionControlImplementSimulator::get_section_setpoint_state(std::uint8_t index) const +{ + assert(index < sectionSetpoints.size()); + return sectionSetpoints.at(index); +} + void SectionControlImplementSimulator::set_switch_state(std::uint8_t index, bool value) { - if (index < switchStates.size()) + if ((index < switchStates.size()) && (switchStates.at(index) != value)) { switchStates.at(index) = value; } @@ -65,6 +80,11 @@ void SectionControlImplementSimulator::set_target_rate(std::uint32_t value) targetRate = value; } +std::uint32_t SectionControlImplementSimulator::get_target_rate() const +{ + return targetRate; +} + bool SectionControlImplementSimulator::get_actual_work_state() const { return setpointWorkState; @@ -99,7 +119,6 @@ bool SectionControlImplementSimulator::create_ddop(std::shared_ptrclear(); @@ -109,7 +128,7 @@ bool SectionControlImplementSimulator::create_ddop(std::shared_ptradd_device("Isobus Seeder", "1.0.0", "123", "IS1.0", localizationData, std::vector(), clientName.get_full_name()); + retVal &= poolToPopulate->add_device("Isobus Seeder", "1.0.0", "123", "IS1.1", localizationData, std::vector(), clientName.get_full_name()); retVal &= poolToPopulate->add_device_element("Seeder", elementCounter++, 0, isobus::task_controller_object::DeviceElementObject::Type::Device, static_cast(ImplementDDOPObjectIDs::MainDeviceElement)); retVal &= poolToPopulate->add_device_process_data("Actual Work State", static_cast(isobus::DataDescriptionIndex::ActualWorkState), isobus::task_controller_object::Object::NULL_OBJECT_ID, static_cast(isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::MemberOfDefaultSet), static_cast(isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::OnChange), static_cast(ImplementDDOPObjectIDs::DeviceActualWorkState)); retVal &= poolToPopulate->add_device_process_data("Request Default PD", static_cast(ImplementDDOPObjectIDs::RequestDefaultProcessData), isobus::task_controller_object::Object::NULL_OBJECT_ID, 0, static_cast(isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::Total), static_cast(ImplementDDOPObjectIDs::RequestDefaultProcessData)); @@ -131,12 +150,12 @@ bool SectionControlImplementSimulator::create_ddop(std::shared_ptradd_device_element("Product", elementCounter++, 9, isobus::task_controller_object::DeviceElementObject::Type::Bin, static_cast(ImplementDDOPObjectIDs::GranularProduct)); - retVal &= poolToPopulate->add_device_process_data("Bin Capacity", static_cast(isobus::DataDescriptionIndex::MaximumMassContent), static_cast(ImplementDDOPObjectIDs::MassPresentation), static_cast(isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::MemberOfDefaultSet), static_cast(isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::OnChange) | static_cast(isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::TimeInterval), static_cast(ImplementDDOPObjectIDs::BinCapacity)); - retVal &= poolToPopulate->add_device_process_data("Bin Level", static_cast(isobus::DataDescriptionIndex::ActualMassContent), static_cast(ImplementDDOPObjectIDs::MassPresentation), static_cast(isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::MemberOfDefaultSet) | static_cast(isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::Settable), static_cast(isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::OnChange) | static_cast(isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::TimeInterval), static_cast(ImplementDDOPObjectIDs::BinLevel)); - retVal &= poolToPopulate->add_device_process_data("Lifetime Total Mass", static_cast(isobus::DataDescriptionIndex::LifetimeApplicationTotalVolume), static_cast(ImplementDDOPObjectIDs::MassPresentation), static_cast(isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::MemberOfDefaultSet), static_cast(isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::Total), static_cast(ImplementDDOPObjectIDs::LifetimeApplicationVolumeTotal)); + retVal &= poolToPopulate->add_device_process_data("Bin Capacity", static_cast(isobus::DataDescriptionIndex::MaximumCountContent), static_cast(ImplementDDOPObjectIDs::CountPresentation), static_cast(isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::MemberOfDefaultSet), static_cast(isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::OnChange) | static_cast(isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::TimeInterval), static_cast(ImplementDDOPObjectIDs::BinCapacity)); + retVal &= poolToPopulate->add_device_process_data("Bin Level", static_cast(isobus::DataDescriptionIndex::ActualCountContent), static_cast(ImplementDDOPObjectIDs::CountPresentation), static_cast(isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::MemberOfDefaultSet) | static_cast(isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::Settable), static_cast(isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::OnChange) | static_cast(isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::TimeInterval), static_cast(ImplementDDOPObjectIDs::BinLevel)); + retVal &= poolToPopulate->add_device_process_data("Lifetime Total Count", static_cast(isobus::DataDescriptionIndex::LifetimeApplicationTotalCount), static_cast(ImplementDDOPObjectIDs::CountPresentation), static_cast(isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::MemberOfDefaultSet), static_cast(isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::Total), static_cast(ImplementDDOPObjectIDs::LifetimeApplicationCountTotal)); retVal &= poolToPopulate->add_device_process_data("Rx Control State", static_cast(isobus::DataDescriptionIndex::PrescriptionControlState), isobus::task_controller_object::Object::NULL_OBJECT_ID, static_cast(isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::MemberOfDefaultSet) | static_cast(isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::Settable), static_cast(isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::OnChange) | static_cast(isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::TimeInterval), static_cast(ImplementDDOPObjectIDs::PrescriptionControlState)); - retVal &= poolToPopulate->add_device_process_data("Target Rate", static_cast(isobus::DataDescriptionIndex::SetpointCountPerAreaApplicationRate), static_cast(ImplementDDOPObjectIDs::MassPerAreaPresentation), static_cast(isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::MemberOfDefaultSet) | static_cast(isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::Settable), static_cast(isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::OnChange), static_cast(ImplementDDOPObjectIDs::TargetRate)); - retVal &= poolToPopulate->add_device_process_data("Actual Rate", static_cast(isobus::DataDescriptionIndex::ActualCountPerAreaApplicationRate), static_cast(ImplementDDOPObjectIDs::MassPerAreaPresentation), static_cast(isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::MemberOfDefaultSet), static_cast(isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::OnChange) | static_cast(isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::TimeInterval), static_cast(ImplementDDOPObjectIDs::ActualRate)); + retVal &= poolToPopulate->add_device_process_data("Target Rate", static_cast(isobus::DataDescriptionIndex::SetpointCountPerAreaApplicationRate), static_cast(ImplementDDOPObjectIDs::CountPerAreaPresentation), static_cast(isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::MemberOfDefaultSet) | static_cast(isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::Settable), static_cast(isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::OnChange), static_cast(ImplementDDOPObjectIDs::TargetRate)); + retVal &= poolToPopulate->add_device_process_data("Actual Rate", static_cast(isobus::DataDescriptionIndex::ActualCountPerAreaApplicationRate), static_cast(ImplementDDOPObjectIDs::CountPerAreaPresentation), static_cast(isobus::task_controller_object::DeviceProcessDataObject::PropertiesBit::MemberOfDefaultSet), static_cast(isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::OnChange) | static_cast(isobus::task_controller_object::DeviceProcessDataObject::AvailableTriggerMethods::TimeInterval), static_cast(ImplementDDOPObjectIDs::ActualRate)); retVal &= poolToPopulate->add_device_property("Operation Type", 2, static_cast(isobus::DataDescriptionIndex::ActualCulturalPractice), isobus::task_controller_object::Object::NULL_OBJECT_ID, static_cast(ImplementDDOPObjectIDs::ActualCulturalPractice)); // Set up sections for section control @@ -162,13 +181,13 @@ bool SectionControlImplementSimulator::create_ddop(std::shared_ptradd_device_value_presentation("mm", 0, 1.0f, 0, static_cast(ImplementDDOPObjectIDs::ShortWidthPresentation)); retVal &= poolToPopulate->add_device_value_presentation("m", 0, 0.001f, 0, static_cast(ImplementDDOPObjectIDs::LongWidthPresentation)); retVal &= poolToPopulate->add_device_value_presentation("m^2", 0, 1.0f, 0, static_cast(ImplementDDOPObjectIDs::AreaPresentation)); - retVal &= poolToPopulate->add_device_value_presentation("Kg", 0, 0.001f, 0, static_cast(ImplementDDOPObjectIDs::MassPresentation)); + retVal &= poolToPopulate->add_device_value_presentation("seeds", 0, 1.0f, 0, static_cast(ImplementDDOPObjectIDs::CountPresentation)); retVal &= poolToPopulate->add_device_value_presentation("minutes", 0, 1.0f, 1, static_cast(ImplementDDOPObjectIDs::TimePresentation)); - retVal &= poolToPopulate->add_device_value_presentation("Kg/ha", 0, 0.001f, 1, static_cast(ImplementDDOPObjectIDs::MassPerAreaPresentation)); + retVal &= poolToPopulate->add_device_value_presentation("seeds/ha", 0, 1.0f, 0, static_cast(ImplementDDOPObjectIDs::CountPerAreaPresentation)); // Add child linkages to device elements if all objects were added OK if (retVal) @@ -206,7 +225,7 @@ bool SectionControlImplementSimulator::create_ddop(std::shared_ptradd_reference_to_child_object(static_cast(ImplementDDOPObjectIDs::BinCapacity)); product->add_reference_to_child_object(static_cast(ImplementDDOPObjectIDs::BinLevel)); - product->add_reference_to_child_object(static_cast(ImplementDDOPObjectIDs::LifetimeApplicationVolumeTotal)); + product->add_reference_to_child_object(static_cast(ImplementDDOPObjectIDs::LifetimeApplicationCountTotal)); product->add_reference_to_child_object(static_cast(ImplementDDOPObjectIDs::PrescriptionControlState)); product->add_reference_to_child_object(static_cast(ImplementDDOPObjectIDs::ActualCulturalPractice)); product->add_reference_to_child_object(static_cast(ImplementDDOPObjectIDs::TargetRate)); @@ -225,15 +244,15 @@ bool SectionControlImplementSimulator::request_value_command_callback(std::uint1 auto sim = reinterpret_cast(parentPointer); switch (DDI) { - case static_cast(isobus::DataDescriptionIndex::MaximumVolumeContent): + case static_cast(isobus::DataDescriptionIndex::MaximumCountContent): { - value = 4542494; // 1200 Gallons in ml + value = 200000; // Arbitrary values... not sure what is a realistic count } break; - case static_cast(isobus::DataDescriptionIndex::ActualVolumeContent): + case static_cast(isobus::DataDescriptionIndex::ActualCountContent): { - value = 3785000; // 1000 Gal in ml; + value = 150000; } break; @@ -273,7 +292,7 @@ bool SectionControlImplementSimulator::request_value_command_callback(std::uint1 { if ((i + (sectionIndexOffset)) < sim->get_number_of_sections()) { - value |= ((true == sim->get_section_state(i + sectionIndexOffset)) ? static_cast(0x01) : static_cast(0x00)) << (2 * i); + value |= ((true == sim->get_section_state(i + sectionIndexOffset)) ? (static_cast(0x01) & sim->get_switch_state(i + sectionIndexOffset)) : static_cast(0x00)) << (2 * i); } else { @@ -283,7 +302,7 @@ bool SectionControlImplementSimulator::request_value_command_callback(std::uint1 } break; - case static_cast(isobus::DataDescriptionIndex::ActualVolumePerAreaApplicationRate): + case static_cast(isobus::DataDescriptionIndex::ActualCountPerAreaApplicationRate): { value = sim->get_actual_rate(); } @@ -295,6 +314,60 @@ bool SectionControlImplementSimulator::request_value_command_callback(std::uint1 } break; + case static_cast(isobus::DataDescriptionIndex::DeviceElementOffsetX): + case static_cast(isobus::DataDescriptionIndex::DeviceElementOffsetY): + case static_cast(isobus::DataDescriptionIndex::RequestDefaultProcessData): + { + value = 0; + } + break; + + case static_cast(isobus::DataDescriptionIndex::ActualWorkingWidth): + { + value = BOOM_WIDTH; + } + break; + + case static_cast(isobus::DataDescriptionIndex::SetpointCondensedWorkState1_16): + case static_cast(isobus::DataDescriptionIndex::SetpointCondensedWorkState17_32): + case static_cast(isobus::DataDescriptionIndex::SetpointCondensedWorkState33_48): + case static_cast(isobus::DataDescriptionIndex::SetpointCondensedWorkState49_64): + case static_cast(isobus::DataDescriptionIndex::SetpointCondensedWorkState65_80): + case static_cast(isobus::DataDescriptionIndex::SetpointCondensedWorkState81_96): + case static_cast(isobus::DataDescriptionIndex::SetpointCondensedWorkState97_112): + case static_cast(isobus::DataDescriptionIndex::SetpointCondensedWorkState113_128): + case static_cast(isobus::DataDescriptionIndex::SetpointCondensedWorkState129_144): + case static_cast(isobus::DataDescriptionIndex::SetpointCondensedWorkState145_160): + case static_cast(isobus::DataDescriptionIndex::SetpointCondensedWorkState161_176): + case static_cast(isobus::DataDescriptionIndex::SetpointCondensedWorkState177_192): + case static_cast(isobus::DataDescriptionIndex::SetpointCondensedWorkState193_208): + case static_cast(isobus::DataDescriptionIndex::SetpointCondensedWorkState209_224): + case static_cast(isobus::DataDescriptionIndex::SetpointCondensedWorkState225_240): + case static_cast(isobus::DataDescriptionIndex::SetpointCondensedWorkState241_256): + { + std::uint16_t sectionIndexOffset = NUMBER_SECTIONS_PER_CONDENSED_MESSAGE * (DDI - static_cast(isobus::DataDescriptionIndex::SetpointCondensedWorkState1_16)); + value = 0; + + for (std::uint_fast8_t i = 0; i < NUMBER_SECTIONS_PER_CONDENSED_MESSAGE; i++) + { + if ((i + (sectionIndexOffset)) < sim->get_number_of_sections()) + { + value |= ((true == sim->get_section_setpoint_state(i + sectionIndexOffset)) ? static_cast(0x01) : static_cast(0x00)) << (2 * i); + } + else + { + value |= (static_cast(0x03) << (2 * i)); + } + } + } + break; + + case static_cast(isobus::DataDescriptionIndex::SetpointCountPerAreaApplicationRate): + { + value = sim->get_target_rate(); + } + break; + default: { value = 0; @@ -340,11 +413,12 @@ bool SectionControlImplementSimulator::value_command_callback(std::uint16_t, for (std::uint_fast8_t i = 0; i < NUMBER_SECTIONS_PER_CONDENSED_MESSAGE; i++) { sim->set_section_state(sectionIndexOffset + i, (0x01 == (processVariableValue >> (2 * i) & 0x03))); + sim->set_section_setpoint_state(sectionIndexOffset + i, (0x01 == (processVariableValue >> (2 * i) & 0x03))); } } break; - case static_cast(isobus::DataDescriptionIndex::SetpointVolumePerAreaApplicationRate): + case static_cast(isobus::DataDescriptionIndex::SetpointCountPerAreaApplicationRate): { sim->set_target_rate(processVariableValue); } @@ -356,10 +430,15 @@ bool SectionControlImplementSimulator::value_command_callback(std::uint16_t, } break; - default: + case static_cast(isobus::DataDescriptionIndex::PrescriptionControlState): + case static_cast(isobus::DataDescriptionIndex::SectionControlState): { + // Technically the TC can set this, but we're just managing it with our auto state } break; + + default: + break; } } // The actual use of the return value here is for the TC client to know if it needs to keep calling more callbacks to search diff --git a/examples/seeder_example/section_control_implement_sim.hpp b/examples/seeder_example/section_control_implement_sim.hpp index b9cbfcf9..12dd227d 100644 --- a/examples/seeder_example/section_control_implement_sim.hpp +++ b/examples/seeder_example/section_control_implement_sim.hpp @@ -91,7 +91,7 @@ class SectionControlImplementSimulator GranularProduct, ///< The main bin element that describes the main product BinCapacity, ///< The max bin content for the product device element BinLevel, ///< Actual Device Element Content specified as volume - LifetimeApplicationVolumeTotal, ///< https://www.isobus.net/isobus/dDEntity/400 + LifetimeApplicationCountTotal, PrescriptionControlState, ///< https://www.isobus.net/isobus/dDEntity/203 ActualCulturalPractice, ///< https://www.isobus.net/isobus/dDEntity/205 TargetRate, ///< The target rate for the rate controller main product @@ -101,8 +101,8 @@ class SectionControlImplementSimulator TimePresentation, ///< Describes to the TC how to display time units ShortWidthPresentation, ///< Describes to the TC how to display small width units LongWidthPresentation, ///< Describes to the TC how to display large width units - MassPresentation, ///< Describes to the TC how to display volume units - MassPerAreaPresentation ///< Describes to the TC how to display volume per area units + CountPresentation, ///< Describes to the TC how to display volume units + CountPerAreaPresentation ///< Describes to the TC how to display volume per area units }; /// @brief Constructor for the simulator @@ -125,6 +125,15 @@ class SectionControlImplementSimulator /// @param[in] index The index of the section to get the state for bool get_section_state(std::uint8_t index) const; + /// @brief Sets the current commanded section state by index + /// @param[in] index The index of the section state to set + /// @param[in] value The new state for the section + void set_section_setpoint_state(std::uint8_t index, bool value); + + /// @brief Returns the current section setpoint state by index + /// @param[in] index The index of the section to get the setpoint state for + bool get_section_setpoint_state(std::uint8_t index) const; + /// @brief Sets the current section's switch state by index /// @param[in] index The index of the switch to set /// @param[in] value The new state for the switch @@ -142,6 +151,10 @@ class SectionControlImplementSimulator /// @param[in] value The rate to set void set_target_rate(std::uint32_t value); + /// @brief Returns the current target rate + /// @returns Current target rate in seeds per hectare + std::uint32_t get_target_rate() const; + /// @brief Returns the actual work state of the device bool get_actual_work_state() const; @@ -194,11 +207,13 @@ class SectionControlImplementSimulator private: static constexpr std::uint8_t NUMBER_SECTIONS_PER_CONDENSED_MESSAGE = 16; ///< Number of section states in a condensed working state message + static constexpr std::int32_t BOOM_WIDTH = 9144; // 30ft expressed in mm - std::vector sectionStates; ///< Stores the commanded section states as a set of boolean values + std::vector sectionStates; ///< Stores the actual section states as a set of boolean values + std::vector sectionSetpoints; ///< Stores the commanded section states as a set of boolean values std::vector switchStates; ///< Stores the UT section switch states as a set of boolean values - std::uint32_t targetRate = 0; ///< The target rate - bool setpointWorkState = false; ///< The overall work state + std::uint32_t targetRate = 12000; ///< The target rate, default of 12k seeds per hectare + bool setpointWorkState = true; ///< The overall work state bool isAutoMode = true; ///< Stores auto vs manual mode setting }; diff --git a/examples/seeder_example/seeder.cpp b/examples/seeder_example/seeder.cpp index 4e2c5198..11f66a00 100644 --- a/examples/seeder_example/seeder.cpp +++ b/examples/seeder_example/seeder.cpp @@ -105,6 +105,7 @@ void Seeder::terminate() if (nullptr != VTApplication) { VTApplication->VTClientInterface.terminate(); + VTApplication->TCClientInterface.terminate(); } isobus::CANHardwareInterface::stop(); } diff --git a/examples/seeder_example/vt_application.cpp b/examples/seeder_example/vt_application.cpp index 6d7703e7..7f7aba86 100644 --- a/examples/seeder_example/vt_application.cpp +++ b/examples/seeder_example/vt_application.cpp @@ -9,6 +9,7 @@ #include "vt_application.hpp" #include "isobus/isobus/can_network_manager.hpp" +#include "isobus/isobus/isobus_standard_data_description_indices.hpp" #include "isobus/utility/iop_file_interface.hpp" #include "isobus/utility/system_timing.hpp" #include "object_pool.hpp" @@ -64,8 +65,8 @@ const std::array(SeederVtApplication::A SeederVtApplication::SeederVtApplication(std::shared_ptr VTPartner, std::shared_ptr TCPartner, std::shared_ptr source) : TCClientInterface(TCPartner, source, nullptr), VTClientInterface(VTPartner, source), - speedMessages(source, false, false, false, false), - txFlags(static_cast(UpdateVTStateFlags::NumberOfFlags), processFlags, this) + txFlags(static_cast(UpdateVTStateFlags::NumberOfFlags), processFlags, this), + speedMessages(source, false, false, false, false) { } @@ -100,6 +101,8 @@ bool SeederVtApplication::initialize() speedMessages.initialize(); machineSelectedSpeedEventHandle = speedMessages.get_machine_selected_speed_data_event_publisher().add_listener([this](const std::shared_ptr mssData, bool changed) { this->handle_machine_selected_speed(mssData, changed); }); + groundBasedSpeedEventHandle = speedMessages.get_ground_based_machine_speed_data_event_publisher().add_listener([this](const std::shared_ptr gbsData, bool changed) { this->handle_ground_based_speed(gbsData, changed); }); + wheelBasedSpeedEventHandle = speedMessages.get_wheel_based_machine_speed_data_event_publisher().add_listener([this](const std::shared_ptr wbsData, bool changed) { this->handle_wheel_based_speed(wbsData, changed); }); ddop = std::make_shared(3); if (sectionControl.create_ddop(ddop, TCClientInterface.get_internal_control_function()->get_NAME())) @@ -157,10 +160,19 @@ void SeederVtApplication::handle_vt_key_events(const isobus::VirtualTerminalClie (false == alarmConditions.at(i).acknowledged)) { alarmConditions.at(i).acknowledged = true; - set_currently_active_screen(previousActiveScreen); break; } } + + if (previousActiveScreen != currentlyActiveScreen) + { + set_currently_active_screen(previousActiveScreen); + } + else + { + set_currently_active_screen(ActiveScreen::Main); + } + break; } break; @@ -180,6 +192,7 @@ void SeederVtApplication::handle_vt_key_events(const isobus::VirtualTerminalClie case section1Toggle_Button: { sectionControl.set_switch_state(0, !sectionControl.get_switch_state(0)); + TCClientInterface.on_value_changed_trigger(2, static_cast(isobus::DataDescriptionIndex::ActualCondensedWorkState1_16)); txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateSection1EnableState_ObjPtr)); txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateSection1Status_OutRect)); } @@ -188,6 +201,7 @@ void SeederVtApplication::handle_vt_key_events(const isobus::VirtualTerminalClie case section2Toggle_Button: { sectionControl.set_switch_state(1, !sectionControl.get_switch_state(1)); + TCClientInterface.on_value_changed_trigger(2, static_cast(isobus::DataDescriptionIndex::ActualCondensedWorkState1_16)); txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateSection2EnableState_ObjPtr)); txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateSection2Status_OutRect)); } @@ -196,6 +210,7 @@ void SeederVtApplication::handle_vt_key_events(const isobus::VirtualTerminalClie case section3Toggle_Button: { sectionControl.set_switch_state(2, !sectionControl.get_switch_state(2)); + TCClientInterface.on_value_changed_trigger(2, static_cast(isobus::DataDescriptionIndex::ActualCondensedWorkState1_16)); txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateSection3EnableState_ObjPtr)); txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateSection3Status_OutRect)); } @@ -204,6 +219,7 @@ void SeederVtApplication::handle_vt_key_events(const isobus::VirtualTerminalClie case section4Toggle_Button: { sectionControl.set_switch_state(3, !sectionControl.get_switch_state(3)); + TCClientInterface.on_value_changed_trigger(2, static_cast(isobus::DataDescriptionIndex::ActualCondensedWorkState1_16)); txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateSection4EnableState_ObjPtr)); txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateSection4Status_OutRect)); } @@ -212,6 +228,7 @@ void SeederVtApplication::handle_vt_key_events(const isobus::VirtualTerminalClie case section5Toggle_Button: { sectionControl.set_switch_state(4, !sectionControl.get_switch_state(4)); + TCClientInterface.on_value_changed_trigger(2, static_cast(isobus::DataDescriptionIndex::ActualCondensedWorkState1_16)); txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateSection5EnableState_ObjPtr)); txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateSection5Status_OutRect)); } @@ -220,6 +237,7 @@ void SeederVtApplication::handle_vt_key_events(const isobus::VirtualTerminalClie case section6Toggle_Button: { sectionControl.set_switch_state(5, !sectionControl.get_switch_state(5)); + TCClientInterface.on_value_changed_trigger(2, static_cast(isobus::DataDescriptionIndex::ActualCondensedWorkState1_16)); txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateSection6EnableState_ObjPtr)); txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateSection6Status_OutRect)); } @@ -230,6 +248,9 @@ void SeederVtApplication::handle_vt_key_events(const isobus::VirtualTerminalClie } } break; + + default: + break; } } @@ -243,16 +264,75 @@ void SeederVtApplication::handle_numeric_value_events(const isobus::VirtualTermi } break; + case enableAlarms_VarNum: + { + alarmsEnabled = event.value; + txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateEnableAlarms_VarNum)); + } + break; + default: break; } } -void SeederVtApplication::handle_machine_selected_speed(const std::shared_ptr, bool changed) +void SeederVtApplication::handle_machine_selected_speed(const std::shared_ptr mssData, bool changed) { if (changed) { + process_new_speed(SpeedSources::MachineSelected, mssData->get_machine_speed()); + } +} + +void SeederVtApplication::handle_ground_based_speed(const std::shared_ptr gbsData, bool changed) +{ + if (changed) + { + process_new_speed(SpeedSources::GroundBased, gbsData->get_machine_speed()); + } +} + +void SeederVtApplication::handle_wheel_based_speed(const std::shared_ptr wbsData, bool changed) +{ + if (changed) + { + process_new_speed(SpeedSources::WheelBased, wbsData->get_machine_speed()); + } +} + +void SeederVtApplication::process_new_speed(SpeedSources source, std::uint32_t speed) +{ + bool shouldConsumeThisSpeed = false; + + if (source == SpeedSources::MachineSelected) + { + shouldConsumeThisSpeed = true; // Best speed source. + } + else if ((SpeedSources::GroundBased == source) && (0 == speedMessages.get_number_received_machine_selected_speed_sources())) + { + shouldConsumeThisSpeed = true; // Second best speed source. + } + else if ((SpeedSources::WheelBased == source) && + (0 == speedMessages.get_number_received_machine_selected_speed_sources()) && + (0 == speedMessages.get_number_received_ground_based_speed_sources())) + { + shouldConsumeThisSpeed = true; // Third best speed source. + } + + if (shouldConsumeThisSpeed) + { + currentSpeedSource = source; + lastMachineSpeed = speed; txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateCurrentSpeedMeter_VarNum)); + txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateCurrentSpeedReadout_VarNum)); + + // Be nice to the user and auto-clear if we're showing the alarm + if ((ActiveScreen::NoMachineSpeed == currentlyActiveScreen) && + (previousActiveScreen != currentlyActiveScreen)) + { + alarmConditions.at(static_cast(Alarm::NoMachineSpeed)).acknowledged = true; + set_currently_active_screen(previousActiveScreen); + } } } @@ -294,6 +374,17 @@ void SeederVtApplication::update() 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()); + + if ((0 != lastMachineSpeed) && + (0 == speedMessages.get_number_received_machine_selected_speed_command_sources()) && + (0 == speedMessages.get_number_received_ground_based_speed_sources()) && + (0 == speedMessages.get_number_received_wheel_based_speed_sources())) + { + lastMachineSpeed = 0; + txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateCurrentSpeedMeter_VarNum)); + txFlags.set_flag(static_cast(UpdateVTStateFlags::UpdateCurrentSpeedReadout_VarNum)); + } + update_alarms(); slowUpdateTimestamp_ms = isobus::SystemTiming::get_timestamp_ms(); @@ -379,36 +470,42 @@ void SeederVtApplication::processFlags(std::uint32_t flag, void *parentPointer) case UpdateVTStateFlags::UpdateCurrentAlarms1_ObjPtr: { - if (seeder->get_number_active_alarms() > 0) + if (seeder->get_is_object_shown(currentAlarms1_ObjPtr)) { - auto alarmToShow = seeder->get_active_alarm_by_priority_index(0); + if (seeder->get_number_active_alarms() > 0) + { + auto alarmToShow = seeder->get_active_alarm_by_priority_index(0); - if (Alarm::NumberOfAlarms != alarmToShow) + 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, 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); + transmitSuccessful = seeder->VTClientInterface.send_change_numeric_value(currentAlarms1_ObjPtr, noActiveAlarms_OutStr); + } } } break; case UpdateVTStateFlags::UpdateCurrentAlarms2_ObjPtr: { - if (seeder->get_number_active_alarms() > 1) + if (seeder->get_is_object_shown(currentAlarms2_ObjPtr)) { - auto alarmToShow = seeder->get_active_alarm_by_priority_index(1); + if (seeder->get_number_active_alarms() > 1) + { + auto alarmToShow = seeder->get_active_alarm_by_priority_index(1); - if (Alarm::NumberOfAlarms != alarmToShow) + 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, 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); + transmitSuccessful = seeder->VTClientInterface.send_change_numeric_value(currentAlarms2_ObjPtr, UNDEFINED); + } } } break; @@ -516,15 +613,40 @@ void SeederVtApplication::processFlags(std::uint32_t flag, void *parentPointer) { if (seeder->get_is_object_shown(currentSpeedMeter_VarNum)) { - auto mss = seeder->speedMessages.get_received_machine_selected_speed(0); - if (nullptr != mss) - { - transmitSuccessful = seeder->VTClientInterface.send_change_numeric_value(currentSpeedMeter_VarNum, mss->get_machine_speed()); - } - else + // The meter uses a fixed max of "30", so we'll have to do some scaling ourselves + std::uint32_t currentSpeed = seeder->lastMachineSpeed; + switch (seeder->VTClientInterface.languageCommandInterface.get_commanded_distance_units()) { - transmitSuccessful = seeder->VTClientInterface.send_change_numeric_value(currentSpeedMeter_VarNum, 0); + case isobus::LanguageCommandInterface::DistanceUnits::Metric: + { + // Scale to KPH + currentSpeed = ((currentSpeed * 0.001f) * 3.6f); // Converting mm/s to m/s, then mm/s to kph + } + break; + + case isobus::LanguageCommandInterface::DistanceUnits::ImperialUS: + { + // Scale to MPH + currentSpeed = ((currentSpeed * 0.001f) * 2.23694f); // Converting mm/s to m/s, then mm/s to mph + } + break; + + default: + { + currentSpeed = 0; // Reserved or n/a? + } + break; } + transmitSuccessful = seeder->VTClientInterface.send_change_numeric_value(currentSpeedMeter_VarNum, currentSpeed); + } + } + break; + + case UpdateVTStateFlags::UpdateCurrentSpeedReadout_VarNum: + { + if (seeder->get_is_object_shown(currentSpeedReadout_VarNum)) + { + transmitSuccessful = seeder->VTClientInterface.send_change_numeric_value(currentSpeedReadout_VarNum, seeder->lastMachineSpeed); } } break; @@ -602,6 +724,15 @@ void SeederVtApplication::processFlags(std::uint32_t flag, void *parentPointer) } break; + case UpdateVTStateFlags::UpdateEnableAlarms_VarNum: + { + if (seeder->get_is_object_shown(enableAlarms_VarNum)) + { + transmitSuccessful = seeder->VTClientInterface.send_change_numeric_value(enableAlarms_VarNum, seeder->alarmsEnabled); + } + } + break; + case UpdateVTStateFlags::UpdateSection1Status_OutRect: case UpdateVTStateFlags::UpdateSection2Status_OutRect: case UpdateVTStateFlags::UpdateSection3Status_OutRect: @@ -615,13 +746,33 @@ void SeederVtApplication::processFlags(std::uint32_t flag, void *parentPointer) { std::uint32_t fillAttribute = solidRed_FillAttr; - if (seeder->sectionControl.get_actual_work_state()) + if (seeder->sectionControl.get_is_mode_auto()) { - fillAttribute = solidGreen_FillAttr; + // Auto mode is TC controlled and logged + if (seeder->sectionControl.get_section_state(sectionIndex) && seeder->sectionControl.get_switch_state(sectionIndex)) + { + fillAttribute = solidGreen_FillAttr; + } + else + { + fillAttribute = solidRed_FillAttr; + } } - else if ((!seeder->sectionControl.get_is_mode_auto()) && (seeder->sectionControl.get_switch_state(sectionIndex))) + else { - fillAttribute = solidYellow_FillAttr; + // Manual mode is basically UT display only, no TC reporting + if (seeder->sectionControl.get_switch_state(sectionIndex) && (0 != seeder->lastMachineSpeed)) + { + fillAttribute = solidGreen_FillAttr; + } + else if (seeder->sectionControl.get_switch_state(sectionIndex)) + { + fillAttribute = solidYellow_FillAttr; + } + else + { + fillAttribute = solidRed_FillAttr; + } } transmitSuccessful = seeder->VTClientInterface.send_change_attribute(SECTION_STATUS_OUTRECTS.at(sectionIndex), 5, fillAttribute); // 5 Is the attribute ID of the fill attribute } @@ -768,6 +919,15 @@ bool SeederVtApplication::get_is_object_shown(std::uint16_t objectID) } break; + case enableAlarms_VarNum: + case enableAlarms_Container: + case enableAlarms_InBool: + case enableAlarms_OutStr: + { + retVal = (ActiveScreen::Settings == currentlyActiveScreen); + } + break; + default: { retVal = true; @@ -918,57 +1078,72 @@ void SeederVtApplication::update_alarms() { bool updateShownMask = false; - if (0 == speedMessages.get_number_received_machine_selected_speed_command_sources()) + if ((VTClientInterface.get_is_connected()) && (alarmsEnabled)) { - if (isobus::SystemTiming::time_expired_ms(alarmConditions.at(static_cast(Alarm::NoMachineSpeed)).conditionTimestamp, alarmConditions.at(static_cast(Alarm::NoMachineSpeed)).conditionTimeout)) + if ((0 == speedMessages.get_number_received_machine_selected_speed_command_sources()) && + (0 == speedMessages.get_number_received_ground_based_speed_sources()) && + (0 == speedMessages.get_number_received_wheel_based_speed_sources())) { - updateShownMask = true; - alarmConditions.at(static_cast(Alarm::NoMachineSpeed)).active = true; + 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; } - } - 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)) + if (false == TCClientInterface.get_is_connected()) { - updateShownMask = true; - alarmConditions.at(static_cast(Alarm::NoTaskController)).active = true; + alarmConditions.at(static_cast(Alarm::NoTaskController)).conditionTimeout = 30000; // Wait slightly longer for TC... it can take some time. + 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; } - } - 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 (updateShownMask) { - if ((alarmConditions.at(i).active) && - (false == alarmConditions.at(i).acknowledged)) + for (std::size_t i = 0; i < alarmConditions.size(); i++) { - set_currently_active_screen(ALARM_SCREENS.at(i)); - break; + 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)); + } + else + { + for (auto &alarm : alarmConditions) + { + alarm.conditionTimestamp = isobus::SystemTiming::get_timestamp_ms(); + alarm.acknowledged = false; + alarm.active = false; + } } - // 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 09b80204..7216c093 100644 --- a/examples/seeder_example/vt_application.hpp +++ b/examples/seeder_example/vt_application.hpp @@ -66,11 +66,13 @@ class SeederVtApplication UpdateUtVersion_VarNum, UpdateUtAddress_VarNum, UpdateCurrentSpeedMeter_VarNum, + UpdateCurrentSpeedReadout_VarNum, UpdateTcVersion_VarNum, UpdateTcAddress_VarNum, UpdateTcNumberBoomsSupported_VarNum, UpdateTcSupportedSections_VarNum, UpdateTcControlChannels_VarNum, + UpdateEnableAlarms_VarNum, UpdateSection1Status_OutRect, UpdateSection2Status_OutRect, @@ -94,6 +96,14 @@ class SeederVtApplication Credits }; + /// @brief Enumerates our tolerated speed sources + enum class SpeedSources + { + MachineSelected, + GroundBased, + WheelBased + }; + /// @brief Stores information associated to if an alarm mask should be shown class AlarmMaskCondition { @@ -129,6 +139,17 @@ class SeederVtApplication /// @param[in] event The event data to process void handle_machine_selected_speed(const std::shared_ptr mssData, bool changed); + /// @brief A callback for handling ground based speed events, used to set appropriate VT flags + /// @param[in] event The event data to process + void handle_ground_based_speed(const std::shared_ptr mssData, bool changed); + + /// @brief A callback for handling wheel based speed events, used to set appropriate VT flags + /// @param[in] event The event data to process + void handle_wheel_based_speed(const std::shared_ptr mssData, bool changed); + + /// @brief Aggregates speeds and decides which speed to use + void process_new_speed(SpeedSources source, std::uint32_t speed); + /// @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 @@ -210,13 +231,17 @@ class SeederVtApplication 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 + SpeedSources currentSpeedSource = SpeedSources::MachineSelected; ///< Keeps track of which speed source is currently being used 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::shared_ptr &, const bool &)>> groundBasedSpeedEventHandle; ///< Handle for ground based speed events + std::shared_ptr &, const bool &)>> wheelBasedSpeedEventHandle; ///< Handle for wheel based speed events std::uint32_t slowUpdateTimestamp_ms = 0; ///< A timestamp to limit some polled data to 1Hz update rate + std::uint32_t lastMachineSpeed = 0; ///< Used to help track speed source timeouts 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 @@ -224,6 +249,7 @@ class SeederVtApplication 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 + bool alarmsEnabled = true; ///< Enables or disables showing alarms }; #endif // VT_APPLICATION_HPP