diff --git a/elasticlib/Elastic.java b/elasticlib/Elastic.java index d8ab4df0..b124759a 100644 --- a/elasticlib/Elastic.java +++ b/elasticlib/Elastic.java @@ -21,25 +21,25 @@ public final class Elastic { private static final ObjectMapper objectMapper = new ObjectMapper(); /** - * Sends an alert notification to the Elastic dashboard. The alert is serialized as a JSON string + * Sends an notification to the Elastic dashboard. The notification is serialized as a JSON string * before being published. * - * @param alert the {@link ElasticNotification} object containing alert details + * @param notification the {@link Notification} object containing notification details */ - public static void sendAlert(ElasticNotification alert) { + public static void sendNotification(Notification notification) { try { - publisher.set(objectMapper.writeValueAsString(alert)); + publisher.set(objectMapper.writeValueAsString(notification)); } catch (JsonProcessingException e) { e.printStackTrace(); } } /** - * Represents a notification object to be sent to the Elastic dashboard. This object holds + * Represents an notification object to be sent to the Elastic dashboard. This object holds * properties such as level, title, description, display time, and dimensions to control how the - * alert is displayed on the dashboard. + * notification is displayed on the dashboard. */ - public static class ElasticNotification { + public static class Notification { @JsonProperty("level") private NotificationLevel level; @@ -59,17 +59,17 @@ public static class ElasticNotification { private double height; /** - * Creates a new ElasticNotification with all default parameters. This constructor is intended + * Creates a new Notification with all default parameters. This constructor is intended * to be used with the chainable decorator methods * *

Title and description fields are empty. */ - public ElasticNotification() { + public Notification() { this(NotificationLevel.INFO, "", ""); } /** - * Creates a new ElasticNotification with all properties specified. + * Creates a new Notification with all properties specified. * * @param level the level of the notification (e.g., INFO, WARNING, ERROR) * @param title the title text of the notification @@ -78,7 +78,7 @@ public ElasticNotification() { * @param width the width of the notification display area * @param height the height of the notification display area, inferred if below zero */ - public ElasticNotification( + public Notification( NotificationLevel level, String title, String description, @@ -94,32 +94,32 @@ public ElasticNotification( } /** - * Creates a new ElasticNotification with default display time and dimensions. + * Creates a new Notification with default display time and dimensions. * * @param level the level of the notification * @param title the title text of the notification * @param description the descriptive text of the notification */ - public ElasticNotification(NotificationLevel level, String title, String description) { + public Notification(NotificationLevel level, String title, String description) { this(level, title, description, 3000, 350, -1); } /** - * Creates a new ElasticNotification with a specified display time and default dimensions. + * Creates a new Notification with a specified display time and default dimensions. * * @param level the level of the notification * @param title the title text of the notification * @param description the descriptive text of the notification * @param displayTimeMillis the display time in milliseconds */ - public ElasticNotification( + public Notification( NotificationLevel level, String title, String description, int displayTimeMillis) { this(level, title, description, displayTimeMillis, 350, -1); } /** - * Creates a new ElasticNotification with specified dimensions and default display time. If the - * height is below zero, it is automatically inferred based on screen size. + * Creates a new Notification with specified dimensions and default display time. If the height + * is below zero, it is automatically inferred based on screen size. * * @param level the level of the notification * @param title the title text of the notification @@ -127,7 +127,7 @@ public ElasticNotification( * @param width the width of the notification display area * @param height the height of the notification display area, inferred if below zero */ - public ElasticNotification( + public Notification( NotificationLevel level, String title, String description, double width, double height) { this(level, title, description, 3000, width, height); } @@ -250,7 +250,7 @@ public double getHeight() { * @param level the level to set the notification to * @return the current notification */ - public ElasticNotification withLevel(NotificationLevel level) { + public Notification withLevel(NotificationLevel level) { this.level = level; return this; } @@ -261,7 +261,7 @@ public ElasticNotification withLevel(NotificationLevel level) { * @param title the title to set the notification to * @return the current notification */ - public ElasticNotification withTitle(String title) { + public Notification withTitle(String title) { setTitle(title); return this; } @@ -272,7 +272,7 @@ public ElasticNotification withTitle(String title) { * @param description the description to set the notification to * @return the current notification */ - public ElasticNotification withDescription(String description) { + public Notification withDescription(String description) { setDescription(description); return this; } @@ -283,7 +283,7 @@ public ElasticNotification withDescription(String description) { * @param seconds the number of seconds to display the notification for * @return the current notification */ - public ElasticNotification withDisplaySeconds(double seconds) { + public Notification withDisplaySeconds(double seconds) { return withDisplayMilliseconds((int) Math.round(seconds * 1000)); } @@ -293,7 +293,7 @@ public ElasticNotification withDisplaySeconds(double seconds) { * @param displayTimeMillis the number of milliseconds to display the notification for * @return the current notification */ - public ElasticNotification withDisplayMilliseconds(int displayTimeMillis) { + public Notification withDisplayMilliseconds(int displayTimeMillis) { setDisplayTimeMillis(displayTimeMillis); return this; } @@ -304,7 +304,7 @@ public ElasticNotification withDisplayMilliseconds(int displayTimeMillis) { * @param width the width to set the notification to * @return the current notification */ - public ElasticNotification withWidth(double width) { + public Notification withWidth(double width) { setWidth(width); return this; } @@ -315,7 +315,7 @@ public ElasticNotification withWidth(double width) { * @param height the height to set the notification to * @return the current notification */ - public ElasticNotification withHeight(double height) { + public Notification withHeight(double height) { setHeight(height); return this; } @@ -327,7 +327,7 @@ public ElasticNotification withHeight(double height) { * * @return the current notification */ - public ElasticNotification withAutomaticHeight() { + public Notification withAutomaticHeight() { setHeight(-1); return this; } @@ -342,7 +342,7 @@ public ElasticNotification withAutomaticHeight() { * * @return the current notification */ - public ElasticNotification withNoAutoDismiss() { + public Notification withNoAutoDismiss() { setDisplayTimeMillis(0); return this; } diff --git a/elasticlib/elasticlib.cpp b/elasticlib/elasticlib.cpp new file mode 100644 index 00000000..065322a3 --- /dev/null +++ b/elasticlib/elasticlib.cpp @@ -0,0 +1,53 @@ +// Copyright (c) 2023-2024 Gold87 and other Elastic contributors +// This software can be modified and/or shared under the terms +// defined by the Elastic license: +// https://github.com/Gold872/elastic-dashboard/blob/main/LICENSE + +#include "elasticlib.h" + +#include + +#include +#include +#include +#include + +namespace elastic { + +void SendNotification(const Notification& notification) { + static nt::StringTopic topic = + nt::NetworkTableInstance::GetDefault().GetStringTopic( + "/Elastic/RobotNotifications"); + static nt::StringPublisher publisher = + topic.Publish({.sendAll = true, .keepDuplicates = true}); + + try { + // Convert Notification to JSON string + wpi::json jsonData; + + if (notification.level == NotificationLevel::INFO) { + jsonData["level"] = "INFO"; + } else if (notification.level == NotificationLevel::WARNING) { + jsonData["level"] = "WARNING"; + } else if (notification.level == NotificationLevel::ERROR) { + jsonData["level"] = "ERROR"; + } else { + jsonData["level"] = "UNKNOWN"; + } + + jsonData["title"] = notification.title; + jsonData["description"] = notification.description; + jsonData["displayTime"] = notification.displayTime.value(); + jsonData["width"] = notification.width; + jsonData["height"] = notification.height; + + // Publish the JSON string + publisher.Set(jsonData.dump()); + } catch (const std::exception& e) { + fmt::println(stderr, "Error processing JSON: {}", e.what()); + } catch (...) { + fmt::println(stderr, "Unknown error occurred while processing JSON."); + } +} + +} // namespace elastic diff --git a/elasticlib/elasticlib.h b/elasticlib/elasticlib.h index 76854e88..d3697230 100644 --- a/elasticlib/elasticlib.h +++ b/elasticlib/elasticlib.h @@ -5,352 +5,52 @@ #pragma once -#include -#include -#include - -#include -#include -#include #include -/** - * @class Elastic - * @brief Handles publishing notifications to the Elastic Robot Notifications - * topic on NetworkTables. - */ -class Elastic { - public: - /** - * @struct Notification - * @brief Represents a notification with various display properties. - */ - struct Notification { - /** - * @enum Level - * @brief Defines severity levels for notifications. - */ - enum class Level { INFO, WARNING, ERROR }; - - Notification() : level(Level::INFO), title(""), description("") {} - - /** - * @brief Constructs a Notification with default display time and - * dimensions. - * @param level The severity level of the notification. - * @param title The title of the notification. - * @param description The description of the notification. - */ - Notification(Level level, const std::string& title, - const std::string& description) - : level(level), - title(title), - description(description), - displayTimeMillis(3000), - width(350), - height(-1) {} - - /** - * @brief Constructs a Notification with specified display time. - * @param level The severity level of the notification. - * @param title The title of the notification. - * @param description The description of the notification. - * @param displayTimeInMillis Duration to display the notification, in - * milliseconds. - */ - Notification(Level level, const std::string& title, - const std::string& description, int displayTimeInMillis) - : level(level), - title(title), - description(description), - displayTimeMillis(displayTimeInMillis), - width(350), - height(-1) {} - - /** - * @brief Constructs a Notification with specified display time and - * dimensions. - * @param level The severity level of the notification. - * @param title The title of the notification. - * @param description The description of the notification. - * @param displayTimeInMillis Duration to display the notification, in - * milliseconds. - * @param width Width of the notification display. - * @param height Height of the notification display. - */ - Notification(Level level, const std::string& title, - const std::string& description, int displayTimeInMillis, - double width, double height) - : level(level), - title(title), - description(description), - displayTimeMillis(displayTimeInMillis), - width(width), - height(height) {} - - /** - * @brief Sets the notification level. - * @param level The new severity level. - */ - void SetLevel(Level level) { this->level = level; } - - /** - * @brief Gets the notification level. - * @return The current severity level. - */ - Level GetLevel() const { return level; } - - /** - * @brief Sets the title of the notification. - * @param title The new title. - */ - void SetTitle(const std::string& title) { this->title = title; } - - /** - * @brief Gets the title of the notification. - * @return The current title. - */ - std::string GetTitle() const { return title; } - - /** - * @brief Sets the description of the notification. - * @param description The new description. - */ - void SetDescription(const std::string& description) { - this->description = description; - } - - /** - * @brief Gets the description of the notification. - * @return The current description. - */ - std::string GetDescription() const { return description; } - - /** - * @brief Gets the display duration of the notification. - * @return Display time in milliseconds. - */ - int GetDisplayTimeMillis() const { return displayTimeMillis; } - - /** - * @brief Sets the display duration of the notification. - * @param newDisplayTimeMillis New display time in milliseconds. - */ - void SetDisplayTimeMillis(int newDisplayTimeMillis) { - this->displayTimeMillis = newDisplayTimeMillis; - } - - void SetDisplayTimeSeconds(double seconds) { - SetDisplayTimeMillis((int)std::round(seconds * 1000)); - } +#include - /** - * @brief Gets the display width of the notification. - * @return The width in pixels. - */ - double GetWidth() const { return width; } +namespace elastic { - /** - * @brief Sets the display width of the notification. - * @param width The new width in pixels. - */ - void SetWidth(double width) { this->width = width; } - - /** - * @brief Gets the display height of the notification. - * @return The height in pixels. - */ - double GetHeight() const { return height; } - - /** - * @brief Sets the display height of the notification. - * @param height The new height in pixels. - */ - void SetHeight(double height) { this->height = height; } - - /** - * Modifies the notification's level and returns itself to allow for method - * chaining. - * - * @param level The level to set the notification to. - * @return A reference to the current notification. - */ - Notification& WithLevel(Level level) { - this->level = level; - return *this; - } - - /** - * Modifies the notification's title and returns itself to allow for method - * chaining. - * - * @param title The title to set the notification to. - * @return A reference to the current notification. - */ - Notification& WithTitle(const std::string& title) { - SetTitle(title); - return *this; - } - - /** - * Modifies the notification's description and returns itself to allow for - * method chaining. - * - * @param description The description to set the notification to. - * @return A reference to the current notification. - */ - Notification& WithDescription(const std::string& description) { - SetDescription(description); - return *this; - } - - /** - * Modifies the notification's display time and returns itself to allow for - * method chaining. - * - * @param seconds The number of seconds to display the notification for. - * @return A reference to the current notification. - */ - Notification& WithDisplaySeconds(double seconds) { - return WithDisplayMilliseconds( - static_cast(std::round(seconds * 1000))); - } - - /** - * Modifies the notification's display time and returns itself to allow for - * method chaining. - * - * @param displayTimeMillis The number of milliseconds to display the - * notification for. - * @return A reference to the current notification. - */ - Notification& WithDisplayMilliseconds(int displayTimeMillis) { - SetDisplayTimeMillis(displayTimeMillis); - return *this; - } - - /** - * Modifies the notification's width and returns itself to allow for method - * chaining. - * - * @param width The width to set the notification to. - * @return A reference to the current notification. - */ - Notification& WithWidth(double width) { - SetWidth(width); - return *this; - } - - /** - * Modifies the notification's height and returns itself to allow for method - * chaining. - * - * @param height The height to set the notification to. - * @return A reference to the current notification. - */ - Notification& WithHeight(double height) { - SetHeight(height); - return *this; - } +/** + * Defines severity levels for notifications. + */ +enum class NotificationLevel { INFO, WARNING, ERROR }; - /** - * Modifies the notification's height and returns itself to allow for method - * chaining. - * - *

This will set the height to -1 to have it automatically determined by - * the dashboard. - * - * @return A reference to the current notification. - */ - Notification& WithAutomaticHeight() { - SetHeight(-1); - return *this; - } +/** + * Represents an notification with various display properties. + */ +struct Notification { + /// Set the display time to this value to disable the auto-dismiss behavior. + static constexpr units::millisecond_t NO_AUTO_DISMISS{0_s}; - /** - * Modifies the notification to disable the auto-dismiss behavior. - * - *

This sets the display time to 0 milliseconds. - * - *

The auto-dismiss behavior can be re-enabled by setting the display - * time to a number greater than 0. - * - * @return A reference to the current notification. - */ - Notification& WithNoAutoDismiss() { - SetDisplayTimeMillis(0); - return *this; - } - /** - * @brief Converts the notification to a JSON string for publishing. - * @return JSON string representing the notification. - */ - std::string ToJson() const { - wpi::json jsonData; - jsonData["level"] = LevelToString(level); - jsonData["title"] = title; - jsonData["description"] = description; - jsonData["displayTime"] = displayTimeMillis; - jsonData["width"] = width; - jsonData["height"] = height; - return jsonData.dump(); - } + // Set the height to this value to have the dashboard automatically determine + // the height. + static constexpr int AUTOMATIC_HEIGHT = -1; - /** - * @brief Converts a notification level to its string representation. - * @param level The notification level. - * @return The string representation of the level. - */ - static std::string LevelToString(Level level) { - switch (level) { - case Level::INFO: - return "INFO"; - case Level::WARNING: - return "WARNING"; - case Level::ERROR: - return "ERROR"; - default: - return "UNKNOWN"; - } - } + /// Notification severity level. + NotificationLevel level = NotificationLevel::INFO; - private: - Level level; ///< Notification severity level. - std::string title; ///< Title of the notification. - std::string description; ///< Description of the notification. - int displayTimeMillis; ///< Display time in milliseconds. - double width; ///< Display width in pixels. - double height; ///< Display height in pixels. - }; + /// Title of the notification. + std::string title; - /** - * @brief Publishes a notification as a JSON string to the NetworkTables - * topic. - * @param alert The notification to send. - */ - static void SendAlert(const Notification& alert) { - try { - std::string jsonString = - alert.ToJson(); // Convert Notification to JSON string - GetPublisher().Set(jsonString); // Publish the JSON string - } catch (const std::exception& e) { - std::cerr << "Error processing JSON: " << e.what() << std::endl; - } catch (...) { - std::cerr << "Unknown error occurred while processing JSON." << std::endl; - } - } + /// Description of the notification. + std::string description; - private: - static nt::StringPublisher& GetPublisher() { - static nt::StringTopic topic = - nt::NetworkTableInstance::GetDefault().GetStringTopic( - "/Elastic/RobotNotifications"); + /// Display time. + units::millisecond_t displayTime{3_s}; - static nt::StringPublisher publisher = - topic.Publish({.sendAll = true, .keepDuplicates = true}); + /// Display width in pixels. + int width = 350; - return publisher; - } + /// Display height in pixels. + int height = AUTOMATIC_HEIGHT; }; -using ElasticNotification = Elastic::Notification; +/** + * Publishes an notification as a JSON string to the NetworkTables topic. + * + * @param notification The notification to send. + */ +void SendNotification(const Notification& notification); + +} // namespace elastic diff --git a/elasticlib/elasticlib.py b/elasticlib/elasticlib.py index b9c160a8..adaf8856 100644 --- a/elasticlib/elasticlib.py +++ b/elasticlib/elasticlib.py @@ -1,19 +1,18 @@ import json +from enum import Enum from typing import Dict + from ntcore import NetworkTableInstance, PubSubOptions -class ElasticNotification: - """ - Represents a notification object to be sent to the Elastic dashboard. - This object holds properties such as level, title, description, display time, - and dimensions to control how the alert is displayed on the dashboard. - """ +class NotificationLevel(Enum): + INFO = "INFO" + WARNING = "WARNING" + ERROR = "ERROR" - class NotificationLevel: - INFO = "INFO" - WARNING = "WARNING" - ERROR = "ERROR" + +class Notification: + """Represents an notification with various display properties.""" def __init__( self, @@ -42,299 +41,44 @@ def __init__( self.width = width self.height = height - def to_dict(self) -> Dict[str, str | float | int | NotificationLevel]: - """ - Converts the notification to a dictionary for JSON serialization. - - Returns: - dict: A dictionary representation of the notification object. - """ - return { - "level": self.level, - "title": self.title, - "description": self.description, - "displayTime": self.display_time, - "width": self.width, - "height": self.height, - } - - def with_level(self, level: str): - """ - Sets the notification level and returns the object for chaining. - - Args: - level (str): The level to set the notification to. - - Returns: - ElasticNotification: The current notification object with the updated level. - """ - self.level = level - return self - - def with_title(self, title: str): - """ - Sets the title and returns the object for chaining. - - Args: - title (str): The title to set for the notification. - - Returns: - ElasticNotification: The current notification object with the updated title. - """ - self.title = title - return self - - def with_description(self, description: str): - """ - Sets the description and returns the object for chaining. - - Args: - description (str): The description to set for the notification. - - Returns: - ElasticNotification: The current notification object with the updated description. - """ - self.description = description - return self - - def with_display_seconds(self, seconds: float): - """ - Sets the display time in seconds and returns the object for chaining. - - Args: - seconds (float): The number of seconds the notification should be displayed for. - - Returns: - ElasticNotification: The current notification object with the updated display time. - """ - self.display_time = int(round(seconds * 1000)) - return self - - def with_display_milliseconds(self, display_time: int): - """ - Sets the display time in milliseconds and returns the object for chaining. - - Args: - display_time (int): The display time in milliseconds. - - Returns: - ElasticNotification: The current notification object with the updated display time. - """ - self.display_time = display_time - return self - - def with_width(self, width: float): - """ - Sets the display width and returns the object for chaining. - - Args: - width (float): The width to set for the notification. - - Returns: - ElasticNotification: The current notification object with the updated width. - """ - self.width = width - return self - - def with_height(self, height: float): - """ - Sets the display height and returns the object for chaining. - - Args: - height (float): The height to set for the notification. - - Returns: - ElasticNotification: The current notification object with the updated height. - """ - self.height = height - return self - - def with_automatic_height(self): - """ - Sets the height to automatic and returns the object for chaining. - - Returns: - ElasticNotification: The current notification object with automatic height. - """ - self.height = -1 - return self - - def with_no_auto_dismiss(self): - """ - Sets the display time to 0 to prevent automatic dismissal. - - This method prevents the notification from disappearing automatically. - - Returns: - None - """ - self.display_time = 0 - - def get_level(self) -> str: - """ - Returns the level of this notification. - - Returns: - str: The current level of the notification. - """ - return self.level - - def set_level(self, level: str): - """ - Updates the level of this notification. - - Args: - level (str): The level to set the notification to. - - Returns: - None - """ - self.level = level - - def get_title(self) -> str: - """ - Returns the title of this notification. - - Returns: - str: The current title of the notification. - """ - return self.title - - def set_title(self, title: str): - """ - Updates the title of this notification. - - Args: - title (str): The title to set the notification to. - - Returns: - None - """ - self.title = title - - def get_description(self) -> str: - """ - Returns the description of this notification. - - Returns: - str: The current description of the notification. - """ - return self.description - - def set_description(self, description: str): - """ - Updates the description of this notification. - - Args: - description (str): The description to set the notification to. - - Returns: - None - """ - self.description = description - - def get_display_time_millis(self) -> int: - """ - Returns the number of milliseconds the notification is displayed for. - Returns: - int: The display time in milliseconds. - """ - return self.display_time +__topic = None +__publisher = None - def set_display_time_seconds(self, seconds: float): - """ - Updates the display time of the notification in seconds. - - Args: - seconds (float): The number of seconds to display the notification for. - - Returns: - None - """ - self.display_time = int(round(seconds * 1000)) - - def set_display_time_millis(self, display_time_millis: int): - """ - Updates the display time of the notification in milliseconds. - Args: - display_time_millis (int): The number of milliseconds to display the notification for. - - Returns: - None - """ - self.display_time = display_time_millis - - def get_width(self) -> float: - """ - Returns the width of this notification. - - Returns: - float: The current width of the notification. - """ - return self.width - - def set_width(self, width: float): - """ - Updates the width of this notification. - - Args: - width (float): The width to set for the notification. - - Returns: - None - """ - self.width = width - - def get_height(self) -> float: - """ - Returns the height of this notification. - - Returns: - float: The current height of the notification. - """ - return self.height - - def set_height(self, height: float): - """ - Updates the height of this notification. - - Args: - height (float): The height to set for the notification. If height is -1, it indicates automatic height. - - Returns: - None - """ - self.height = height - - -class Elastic: +def send_notification(notification: Notification): """ - A class responsible for sending alert notifications to the Elastic dashboard. + Sends an notification notification to the Elastic dashboard. + The notification is serialized as a JSON string before being published. - This class uses NetworkTables to publish notifications to the dashboard. - The alerts are serialized as JSON strings before being sent. - """ - - _topic = NetworkTableInstance.getDefault().getStringTopic( - "/Elastic/RobotNotifications" - ) - _publisher = _topic.publish(PubSubOptions(sendAll=True, keepDuplicates=True)) - - @staticmethod - def send_alert(alert: ElasticNotification): - """ - Sends an alert notification to the Elastic dashboard. - The alert is serialized as a JSON string before being published. + Args: + notification (ElasticNotification): The notification object containing the notification details. - Args: - alert (ElasticNotification): The notification object containing the alert details. - - Raises: - Exception: If there is an error during serialization or publishing the alert. - """ - try: - Elastic._publisher.set(json.dumps(alert.to_dict())) - except Exception as e: - print(f"Error serializing alert: {e}") + Raises: + Exception: If there is an error during serialization or publishing the notification. + """ + global __topic + global __publisher + + if not __topic: + __topic = NetworkTableInstance.getDefault().getStringTopic( + "/Elastic/RobotNotifications" + ) + if not __publisher: + __publisher = __topic.publish(PubSubOptions(sendAll=True, keepDuplicates=True)) + + try: + __publisher.set( + json.dumps( + { + "level": notification.level, + "title": notification.title, + "description": notification.description, + "displayTime": notification.display_time, + "width": notification.width, + "height": notification.height, + } + ) + ) + except Exception as e: + print(f"Error serializing notification: {e}")