diff --git a/README.md b/README.md index 592bd0a..54afece 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,8 @@ Supported features: - Picture graphics (with and without run-length encoding) - Output Strings (partial - font clipping is not compliant) - Input lists (partial - drawing the selector needs work) -- Some CAN messages (Object pool upload state machine and handshake messages, maintenance messages, change active mask response, change child location response, change numeric value and response, key/button activation and release messages, change numeric value, select input object) +- Most relevant VT server CAN messages +- Logging Unimplemented features (for now) @@ -46,7 +47,6 @@ Unimplemented features (for now) - Animations - Output Lists - Output arched bar graph -- Logging - Graphics contexts - Pointing events - TAN diff --git a/include/LoggerComponent.hpp b/include/LoggerComponent.hpp index e69de29..dea1d6b 100644 --- a/include/LoggerComponent.hpp +++ b/include/LoggerComponent.hpp @@ -0,0 +1,41 @@ +//================================================================================================ +/// @file LoggerComponent.hpp +/// +/// @brief Defines a GUI component to draw log output. +/// @author Adrian Del Grosso +/// +/// @copyright 2023 Adrian Del Grosso +//================================================================================================ +#ifndef LOGGER_COMPONENT_HPP +#define LOGGER_COMPONENT_HPP + +#include "isobus/isobus/can_stack_logger.hpp" +#include "isobus/isobus/isobus_virtual_terminal_objects.hpp" +#include "isobus/isobus/isobus_virtual_terminal_server_managed_working_set.hpp" + +#include "JuceHeader.h" + +/// @brief Defines a GUI component that will draw log info sunk from the stack +class LoggerComponent : public Component + , public isobus::CANStackLogger +{ +public: + LoggerComponent(); + + void paint(Graphics &g) override; + + void sink_CAN_stack_log(LoggingLevel level, const std::string &logText) override; + +private: + struct LogData + { + String logText; + isobus::CANStackLogger::LoggingLevel logLevel; + }; + static constexpr std::size_t MAX_NUMBER_MESSAGES = 3000; + std::deque loggedMessages; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(LoggerComponent); +}; + +#endif // LOGGER_COMPONENT_HPP diff --git a/include/ServerMainComponent.hpp b/include/ServerMainComponent.hpp index 0b035bc..1ebff1b 100644 --- a/include/ServerMainComponent.hpp +++ b/include/ServerMainComponent.hpp @@ -1,6 +1,7 @@ #pragma once #include "DataMaskRenderAreaComponent.hpp" +#include "LoggerComponent.hpp" #include "SoftKeyMaskRenderAreaComponent.hpp" #include "WorkingSetSelectorComponent.hpp" #include "isobus/isobus/isobus_virtual_terminal_server.hpp" @@ -75,6 +76,8 @@ class ServerMainComponent : public juce::Component WorkingSetSelectorComponent workingSetSelector; DataMaskRenderAreaComponent dataMaskRenderer; SoftKeyMaskRenderAreaComponent softKeyMaskRenderer; + LoggerComponent logger; + Viewport loggerViewport; SoundPlayer mSoundPlayer; AudioDeviceManager mAudioDeviceManager; std::uint8_t numberOfPoolsToRender = 0; diff --git a/src/LoggerComponent.cpp b/src/LoggerComponent.cpp index e69de29..74dd58d 100644 --- a/src/LoggerComponent.cpp +++ b/src/LoggerComponent.cpp @@ -0,0 +1,84 @@ +//================================================================================================ +/// @file LoggerComponent.cpp +/// +/// @brief Implements a GUI component to draw log output. +/// @author Adrian Del Grosso +/// +/// @copyright 2023 Adrian Del Grosso +//================================================================================================ +#include "LoggerComponent.hpp" + +LoggerComponent::LoggerComponent() +{ + auto bounds = getLocalBounds(); + setBounds(10, 10, bounds.getWidth() - 10, bounds.getHeight() - 10); +} + +void LoggerComponent::paint(Graphics &g) +{ + g.fillAll(Colours::black); + g.setFont(14.0f); + + int numberOfLinesFitted = getHeight() / 14; + + for (std::size_t i = 0; i < loggedMessages.size() && i < numberOfLinesFitted; i++) + { + const auto &message = loggedMessages.at(i); + + switch (message.logLevel) + { + case LoggingLevel::Info: + { + g.setColour(Colours::white); + } + break; + + case LoggingLevel::Warning: + { + g.setColour(Colours::yellow); + } + break; + + case LoggingLevel::Error: + case LoggingLevel::Critical: + { + g.setColour(Colours::red); + } + break; + + case LoggingLevel::Debug: + { + g.setColour(Colours::blueviolet); + } + break; + + default: + { + g.setColour(Colours::white); + } + break; + } + g.drawFittedText(message.logText, 0, i * 14, getWidth(), 14, Justification::centredLeft, 1); + } +} + +void LoggerComponent::sink_CAN_stack_log(LoggingLevel level, const std::string &logText) +{ + const auto mmLock = MessageManagerLock(); + + loggedMessages.push_front({ logText, level }); + + if (loggedMessages.size() > MAX_NUMBER_MESSAGES) + { + loggedMessages.pop_back(); + } + + int newSize = loggedMessages.size() * 14; + + if (newSize < 200) + { + newSize = 200; + } + setSize(getWidth(), newSize); + repaint(); +} diff --git a/src/Main.cpp b/src/Main.cpp index e06c180..9133d98 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -88,7 +88,6 @@ class AgISOUniversalTerminalApplication : public juce::JUCEApplication jassert(nullptr != canDriver); // You need some kind of CAN interface to run this program! isobus::CANHardwareInterface::set_number_of_can_channels(1); isobus::CANHardwareInterface::assign_can_channel_frame_handler(0, canDriver); - isobus::CANHardwareInterface::start(); isobus::NAME serverNAME(0); serverNAME.set_arbitrary_address_capable(true); @@ -98,6 +97,7 @@ class AgISOUniversalTerminalApplication : public juce::JUCEApplication serverInternalControlFunction = isobus::InternalControlFunction::create(serverNAME, 0x26, 0); setUsingNativeTitleBar(true); setContentOwned(new ServerMainComponent(serverInternalControlFunction), true); + isobus::CANHardwareInterface::start(); #if JUCE_IOS || JUCE_ANDROID setFullScreen(true); diff --git a/src/ServerMainComponent.cpp b/src/ServerMainComponent.cpp index 4094ab6..3955c7e 100644 --- a/src/ServerMainComponent.cpp +++ b/src/ServerMainComponent.cpp @@ -27,9 +27,18 @@ ServerMainComponent::ServerMainComponent(std::shared_ptr ServerMainComponent::get_client_control_function_for_working_set(std::shared_ptr workingSet) const @@ -307,11 +318,11 @@ void ServerMainComponent::on_change_active_mask_callback(std::shared_ptrget_object_by_id(newMask); - + if (activeWorkingSetDataMaskObjectID != newMask) { activeWorkingSetDataMaskObjectID = newMask; - + if (send_status_message()) { statusMessageTimestamp_ms = isobus::SystemTiming::get_timestamp_ms();