diff --git a/elasticlib/.clang-format b/elasticlib/.clang-format new file mode 100644 index 00000000..d424ff94 --- /dev/null +++ b/elasticlib/.clang-format @@ -0,0 +1,271 @@ +--- +Language: Cpp +BasedOnStyle: Google +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseColons: false +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAfterAttributes: Always +BreakAfterJavaFieldAnnotations: false +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Attach +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^<.*\.h>' + Priority: 1 + SortPriority: 0 + CaseSensitive: false + - Regex: '^<.*' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 3 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '([-_](test|unittest))?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: true +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 2 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +KeepEmptyLinesAtEOF: false +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +Macros: + - 'HAL_ENUM(name)=enum name' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: NextLine +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +PPIndentWidth: -1 +QualifierAlignment: Leave +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + - ParseTestProto + - ParsePartialTestProto + CanonicalDelimiter: pb + BasedOnStyle: google +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SortIncludes: false +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: c++20 +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseTab: Never +VerilogBreakBetweenInstancePorts: true +WhitespaceSensitiveMacros: + - BOOST_PP_STRINGIZE + - CF_SWIFT_NAME + - NS_SWIFT_NAME + - PP_STRINGIZE + - STRINGIZE +... diff --git a/elasticlib/.styleguide b/elasticlib/.styleguide new file mode 100644 index 00000000..425b8074 --- /dev/null +++ b/elasticlib/.styleguide @@ -0,0 +1,22 @@ +cppHeaderFileInclude { + \.h$ + \.hpp$ + \.inc$ + \.inl$ +} + +cppSrcFileInclude { + \.cpp$ +} + +modifiableFileExclude { + gradle/ + assets/ + macos/ + windows/ + linux/ + test_resources/ + screenshots/ + installer_setup_script.iss + README.md +} diff --git a/elasticlib/.styleguide-license b/elasticlib/.styleguide-license new file mode 100644 index 00000000..9575e60c --- /dev/null +++ b/elasticlib/.styleguide-license @@ -0,0 +1,4 @@ +// 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 diff --git a/elasticlib/Elastic.java b/elasticlib/Elastic.java index 09b2ef2b..d8ab4df0 100644 --- a/elasticlib/Elastic.java +++ b/elasticlib/Elastic.java @@ -1,3 +1,8 @@ +// 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 + package frc.robot.util; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/elasticlib/elasticlib.h b/elasticlib/elasticlib.h index 62664d0b..76854e88 100644 --- a/elasticlib/elasticlib.h +++ b/elasticlib/elasticlib.h @@ -1,3 +1,8 @@ +// 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 + #pragma once #include diff --git a/elasticlib/elasticlib.py b/elasticlib/elasticlib.py new file mode 100644 index 00000000..b9c160a8 --- /dev/null +++ b/elasticlib/elasticlib.py @@ -0,0 +1,340 @@ +import json +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: + INFO = "INFO" + WARNING = "WARNING" + ERROR = "ERROR" + + def __init__( + self, + level=NotificationLevel.INFO, + title: str = "", + description: str = "", + display_time: int = 3000, + width: float = 350, + height: float = -1, + ): + """ + Initializes an ElasticNotification object. + + Args: + level (str): The severity level of the notification. Default is 'INFO'. + title (str): The title of the notification. Default is an empty string. + description (str): The description of the notification. Default is an empty string. + display_time (int): Time in milliseconds for which the notification should be displayed. Default is 3000 ms. + width (float): Width of the notification display area. Default is 350. + height (float): Height of the notification display area. Default is -1 (automatic height). + """ + self.level = level + self.title = title + self.description = description + self.display_time = display_time + 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 + + 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: + """ + A class responsible for sending alert notifications to the Elastic dashboard. + + 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: + 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}")