From 93a36a63887a23774629353e2797ee9cd6cfef0e Mon Sep 17 00:00:00 2001 From: Thad House Date: Thu, 22 Feb 2024 15:41:54 -0800 Subject: [PATCH] Add analyzer to ensure public API doesn't use Action or Func --- .editorconfig | 2 + WPILib.sln | 7 + .../Analyzer/ActionFuncDetector.cs | 108 +++++++++++++++ .../Analyzer/ActionFuncDiagnostics.cs | 21 +++ dev/Directory.Build.props | 20 ++- dev/desktopDev/desktopDev.csproj | 2 - dev/roboRIODev/Program.cs | 128 ++---------------- dev/roboRIODev/Robot.cs | 8 ++ dev/roboRIODev/roboRIODev.csproj | 28 ++-- src/Directory.Build.props | 1 + src/cscore/VideoListener.cs | 7 +- src/cscore/cscore.csproj | 1 - src/ntcore/INtSendableBuilder.cs | 3 +- src/ntcore/NetworkTable.cs | 14 +- src/ntcore/NetworkTableInstance.cs | 31 +++-- src/wpilibsharp/HalInitializationException.cs | 5 + src/wpilibsharp/RobotRunner.cs | 17 ++- src/wpiutil/Function/Delegates.cs | 17 +++ src/wpiutil/Sendable/ISendableBuilder.cs | 26 ++-- src/wpiutil/Sendable/SendableRegistery.cs | 7 +- .../Serialization/Protobuf/IProtobuf.cs | 5 +- 21 files changed, 269 insertions(+), 189 deletions(-) create mode 100644 codehelp/CodeHelpers/ActionFuncDetector/Analyzer/ActionFuncDetector.cs create mode 100644 codehelp/CodeHelpers/ActionFuncDetector/Analyzer/ActionFuncDiagnostics.cs create mode 100644 dev/roboRIODev/Robot.cs create mode 100644 src/wpilibsharp/HalInitializationException.cs create mode 100644 src/wpiutil/Function/Delegates.cs diff --git a/.editorconfig b/.editorconfig index 1723a2dd..6d1ed610 100644 --- a/.editorconfig +++ b/.editorconfig @@ -119,6 +119,8 @@ csharp_space_between_method_call_empty_parameter_list_parentheses = false # Wrapping preferences csharp_preserve_single_line_statements = true csharp_preserve_single_line_blocks = true +# enable internal WPILib diagnostics +dotnet_diagnostic.WPILIB1100.severity = warning ############################### # VB Coding Conventions # ############################### diff --git a/WPILib.sln b/WPILib.sln index 9cbcb9d7..a641003b 100644 --- a/WPILib.sln +++ b/WPILib.sln @@ -43,6 +43,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPILib.CodeHelpers", "codeh EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeHelpers.Test", "codehelp\CodeHelpers.Test\CodeHelpers.Test.csproj", "{42E0EFC6-4990-4395-A9D1-8683778751E7}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "roboRIODev", "dev\roboRIODev\roboRIODev.csproj", "{CC242C8A-93D4-4083-9C3C-D98A1FE687C1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -112,6 +114,10 @@ Global {42E0EFC6-4990-4395-A9D1-8683778751E7}.Debug|Any CPU.Build.0 = Debug|Any CPU {42E0EFC6-4990-4395-A9D1-8683778751E7}.Release|Any CPU.ActiveCfg = Release|Any CPU {42E0EFC6-4990-4395-A9D1-8683778751E7}.Release|Any CPU.Build.0 = Release|Any CPU + {CC242C8A-93D4-4083-9C3C-D98A1FE687C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CC242C8A-93D4-4083-9C3C-D98A1FE687C1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CC242C8A-93D4-4083-9C3C-D98A1FE687C1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CC242C8A-93D4-4083-9C3C-D98A1FE687C1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {8F38C25E-641E-47FC-AC0A-0717F2159E8F} = {DB664556-4BF0-4874-8CB6-DC24E60A67AF} @@ -130,5 +136,6 @@ Global {630D08FD-CD06-4674-BC5A-F1F211619E83} = {AD95ECD8-E708-4FB4-9B7E-A8A8EF3FCB3E} {76F4D0AE-2123-493B-B721-4118330C52BB} = {909FC1DB-3083-4F01-8496-B8C9DD4FEA13} {42E0EFC6-4990-4395-A9D1-8683778751E7} = {909FC1DB-3083-4F01-8496-B8C9DD4FEA13} + {CC242C8A-93D4-4083-9C3C-D98A1FE687C1} = {1CA9AB3B-4828-4F07-8C0E-88EF7C5A9ACD} EndGlobalSection EndGlobal diff --git a/codehelp/CodeHelpers/ActionFuncDetector/Analyzer/ActionFuncDetector.cs b/codehelp/CodeHelpers/ActionFuncDetector/Analyzer/ActionFuncDetector.cs new file mode 100644 index 00000000..f51090ea --- /dev/null +++ b/codehelp/CodeHelpers/ActionFuncDetector/Analyzer/ActionFuncDetector.cs @@ -0,0 +1,108 @@ +using System.Collections.Immutable; +using System.Diagnostics; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace WPILib.CodeHelpers.ActionFuncDetector.Analyzer; + +[DiagnosticAnalyzer(LanguageNames.CSharp)] +public sealed class ActionFuncDetector : DiagnosticAnalyzer +{ + public override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create([ + ActionFuncDiagnostics.UseOfActionFunc + ]); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method); + context.RegisterSymbolAction(AnalyzeProperty, SymbolKind.Property); + context.RegisterSymbolAction(AnalyzeField, SymbolKind.Field); + } + + private static void AnalyzeMethod(SymbolAnalysisContext context) + { + if (context.Symbol is not IMethodSymbol methodSymbol) + { + return; + } + + if (methodSymbol.DeclaredAccessibility != Accessibility.Public) + { + return; + } + + if (TypeCheck(methodSymbol.ReturnType)) + { + ReportDiagnostic(context, methodSymbol.ReturnType); + } + + foreach (var parameter in methodSymbol.Parameters) + { + if (TypeCheck(parameter.Type)) + { + ReportDiagnostic(context, parameter); + } + } + } + + private static void ReportDiagnostic(SymbolAnalysisContext context, ISymbol symbol) + { + foreach (var location in symbol.Locations) + { + context.ReportDiagnostic(Diagnostic.Create(ActionFuncDiagnostics.UseOfActionFunc, location)); + } + } + + private static void AnalyzeProperty(SymbolAnalysisContext context) + { + if (context.Symbol is not IPropertySymbol propertySymbol) + { + return; + } + + if (propertySymbol.DeclaredAccessibility != Accessibility.Public) + { + return; + } + + if (TypeCheck(propertySymbol.Type)) + { + ReportDiagnostic(context, propertySymbol.Type); + } + } + + private static bool TypeCheck(ITypeSymbol typeSymbol) + { + var containingNamespace = typeSymbol.ContainingNamespace; + if (containingNamespace is null) + { + return false; + } + if (containingNamespace.Name != "System") + { + return false; + } + var name = typeSymbol.Name; + return name == "Action" || name == "Func"; + } + + private static void AnalyzeField(SymbolAnalysisContext context) + { + if (context.Symbol is not IFieldSymbol fieldSymbol) + { + return; + } + + if (fieldSymbol.DeclaredAccessibility != Accessibility.Public) + { + return; + } + + if (TypeCheck(fieldSymbol.Type)) + { + ReportDiagnostic(context, fieldSymbol.Type); + } + } +} diff --git a/codehelp/CodeHelpers/ActionFuncDetector/Analyzer/ActionFuncDiagnostics.cs b/codehelp/CodeHelpers/ActionFuncDetector/Analyzer/ActionFuncDiagnostics.cs new file mode 100644 index 00000000..40f5bb8d --- /dev/null +++ b/codehelp/CodeHelpers/ActionFuncDetector/Analyzer/ActionFuncDiagnostics.cs @@ -0,0 +1,21 @@ +namespace WPILib.CodeHelpers.ActionFuncDetector.Analyzer; + +#pragma warning disable RS2008 // Enable analyzer release tracking + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using WPILib.CodeHelpers; + +public static class ActionFuncDiagnostics +{ + public class Ids + { + public const string Prefix = "WPILIB"; + public const string UseOfActionFunc = Prefix + "1100"; + } + + private const string Category = "AllowedPublicTypes"; + + public static readonly DiagnosticDescriptor UseOfActionFunc = new( + Ids.UseOfActionFunc, "Action/Func is used in a public API", "Use of Action or Func in a public API is disallowed", Category, DiagnosticSeverity.Warning, false); +} diff --git a/dev/Directory.Build.props b/dev/Directory.Build.props index a2119370..20d1190d 100644 --- a/dev/Directory.Build.props +++ b/dev/Directory.Build.props @@ -1,5 +1,15 @@ - - - false - - \ No newline at end of file + + + net8.0 + false + enable + Exe + + + + + + + + + diff --git a/dev/desktopDev/desktopDev.csproj b/dev/desktopDev/desktopDev.csproj index 96822b61..d444538a 100644 --- a/dev/desktopDev/desktopDev.csproj +++ b/dev/desktopDev/desktopDev.csproj @@ -2,8 +2,6 @@ Exe - net8.0 - true diff --git a/dev/roboRIODev/Program.cs b/dev/roboRIODev/Program.cs index b12bb7c2..f396d89f 100644 --- a/dev/roboRIODev/Program.cs +++ b/dev/roboRIODev/Program.cs @@ -1,123 +1,23 @@ -using System; +using System.Runtime.CompilerServices; using WPILib; -using WPILib.Counters; -using WPILib.SmartDashboardNS; -namespace roboRIODev +namespace TestRobot; + +internal class Program { - public class Robot : TimedRobot + private static void Main(string[] args) { - //private TimeSpan lastTime = Timer.FPGATimestamp; - //double[] buffer = new double[50]; - //int idx = 0; - - //private int can = CANAPI.Initialize(CANManufacturer.kTeamUse, 1, CANDeviceType.kMiscellaneous); - - private readonly PWMSparkMax sparkMax = new PWMSparkMax(0); - private readonly DigitalInput di = new DigitalInput(0); - private Tachometer absoluteTach; - private readonly DigitalInput quadDI = new DigitalInput(1); - private readonly DigitalInput quadDIB = new DigitalInput(2); - private Tachometer quadratureTach; - private UpDownCounter quadratureCounter; - - private ExternalDirectionCounter externalDirectionCounter; - private ExternalDirectionCounter externalDirectionCounter2x; - - AsynchronousInterrupt interrupt; - - int count = 0; - - public override void RobotInit() - { - absoluteTach = new Tachometer(di); - quadratureTach = new Tachometer(quadDI); - quadratureTach.EdgesPerRevolution = 2048; - quadratureCounter = new UpDownCounter(); - quadratureCounter.UpSource = quadDI; - quadratureCounter.UpEdgeConfiguration = EdgeConfiguration.kRisingEdge; - - externalDirectionCounter = new ExternalDirectionCounter(quadDI, quadDIB); - - externalDirectionCounter2x = new ExternalDirectionCounter(quadDI, quadDIB); - externalDirectionCounter2x.EdgeConfiguration = EdgeConfiguration.kBothEdges; - externalDirectionCounter2x.ReverseDirection = true; - - DigitalGlitchFilter filter = new DigitalGlitchFilter(); - filter.SetPeriod(TimeSpan.FromSeconds(1)); - //filter.Add(di); - - interrupt = new AsynchronousInterrupt(di, (r, f) => - { - Console.WriteLine("Interrupt Occured " + count); - count++; - //Thread.Sleep(20); - }); - - interrupt.SetInterruptEdges(true, false); - - interrupt.Enable(); - } - - - - public override unsafe void RobotPeriodic() - { - SmartDashboard.PutNumber("External Dir Tach", externalDirectionCounter.Count); - - SmartDashboard.PutNumber("External Dir Tach 2x", externalDirectionCounter2x.Count); - - - SmartDashboard.PutNumber("Absolute Tach", absoluteTach.RotationalSpeed?.RevolutionsPerSecond ?? double.MaxValue); - - SmartDashboard.PutNumber("Quadrature Tach", quadratureTach.RotationalSpeed?.RevolutionsPerSecond ?? 0); - - SmartDashboard.PutNumber("Quadrature Counter", quadratureCounter.Count); - - SmartDashboard.PutNumber("Joystick", DriverStation.Instance.GetStickAxis(0, 1)); - sparkMax.Set(DriverStation.Instance.GetStickAxis(0, 1)); - - if (DriverStation.Instance.GetStickButton(0, 1)) - { - Console.WriteLine("Disable"); - interrupt.Disable(); - } - - if (DriverStation.Instance.GetStickButton(0, 2)) - { - Console.WriteLine("Enable"); - interrupt.Enable(); - } - //sparkMax.SetVoltage(ElectricPotential.FromVolts(5)); - //int idxLocal = idx; - //try - //{ - // CANAPI.WritePacket(can, new Span(&idxLocal, 4), 42); - //} - //catch (UncleanStatusException ex) - //{ - // ; - //} - - //var current = Timer.FPGATimestamp; - //var delta = current - lastTime; - //lastTime = current; - //buffer[idx] = delta.TotalMilliseconds; - //idx++; - //if (idx == 50) - //{ - // Console.WriteLine(buffer.Average()); - // idx = 0; - //} - //base.RobotPeriodic(); - } + RobotRunner.StartRobot(); } +} - class Program +internal static class RobotInitializer +{ + [ModuleInitializer] + public static void Initialize() { - static void Main(string[] args) - { - RobotBase.StartRobot(); - } + // Force load the HAL, because we can't guarantee it won't be + // accidentally used too early later + RobotRunner.InitializeHAL(); } } diff --git a/dev/roboRIODev/Robot.cs b/dev/roboRIODev/Robot.cs new file mode 100644 index 00000000..ea3a58fc --- /dev/null +++ b/dev/roboRIODev/Robot.cs @@ -0,0 +1,8 @@ +using WPILib; + +namespace TestRobot; + +public class Robot : RobotBase +{ + +} diff --git a/dev/roboRIODev/roboRIODev.csproj b/dev/roboRIODev/roboRIODev.csproj index 3569fa07..938bcaef 100644 --- a/dev/roboRIODev/roboRIODev.csproj +++ b/dev/roboRIODev/roboRIODev.csproj @@ -1,20 +1,8 @@ - - - - Exe - net8.0 - true - false - - - - - - - - - - - - + + + + Exe + TestRobot + + + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 1f0508cc..86993c18 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -15,5 +15,6 @@ + diff --git a/src/cscore/VideoListener.cs b/src/cscore/VideoListener.cs index 203eab17..8ab151bd 100644 --- a/src/cscore/VideoListener.cs +++ b/src/cscore/VideoListener.cs @@ -1,12 +1,13 @@ using CsCore.Handles; using CsCore.Natives; +using WPIUtil.Function; namespace CsCore; public class VideoListener : IDisposable, IEquatable { private static readonly object s_listenerLock = new(); - private static readonly Dictionary> s_listeners = []; + private static readonly Dictionary> s_listeners = []; private static Thread? s_thread; private static CsListenerPoller s_poller; @@ -26,7 +27,7 @@ private static void StartThread() } foreach (var videoEvent in events) { - Action? listener = null; + Consumer? listener = null; lock (s_listenerLock) { s_listeners.TryGetValue(videoEvent.Listener, out listener); @@ -66,7 +67,7 @@ private static void StartThread() public CsListener Handle { get; private set; } - public VideoListener(Action listener, EventKind eventMask, bool immediateNotify) + public VideoListener(Consumer listener, EventKind eventMask, bool immediateNotify) { lock (s_listenerLock) { diff --git a/src/cscore/cscore.csproj b/src/cscore/cscore.csproj index 9eb338d0..e09f3b76 100644 --- a/src/cscore/cscore.csproj +++ b/src/cscore/cscore.csproj @@ -21,7 +21,6 @@ - diff --git a/src/ntcore/INtSendableBuilder.cs b/src/ntcore/INtSendableBuilder.cs index 8327f836..cba6f8fe 100644 --- a/src/ntcore/INtSendableBuilder.cs +++ b/src/ntcore/INtSendableBuilder.cs @@ -1,10 +1,11 @@ +using WPIUtil.Function; using WPIUtil.Sendable; namespace NetworkTables; public interface INtSendableBuilder : ISendableBuilder { - Action UpdateTable { set; } + Runnable UpdateTable { set; } Topic GetTopic(string key); diff --git a/src/ntcore/NetworkTable.cs b/src/ntcore/NetworkTable.cs index 022cc970..51a63380 100644 --- a/src/ntcore/NetworkTable.cs +++ b/src/ntcore/NetworkTable.cs @@ -192,7 +192,9 @@ public NetworkTableValue GetValue(string key) public string Path { get; } - public NtListener AddListener(EventFlags eventKinds, Action listener) + public delegate void TableEventListener(NetworkTable table, string key, NetworkTableEvent tableEvent); + + public NtListener AddListener(EventFlags eventKinds, TableEventListener listener) { int prefixLex = Path.Length + 1; return Instance.AddListener([m_pathWithSep], eventKinds, ntEvent => @@ -221,17 +223,19 @@ public NtListener AddListener(EventFlags eventKinds, Action listener) + public NtListener AddListener(string key, EventFlags eventKinds, TableEventListener listener) { var entry = GetEntry(key); return Instance.AddListener(entry, eventKinds, ntEvent => listener(this, key, ntEvent)); } - private class SubTableListenerHolder(int prefixLen, NetworkTable parent, Action listener) + public delegate void SubTableListener(NetworkTable parent, string name, NetworkTable table); + + private class SubTableListenerHolder(int prefixLen, NetworkTable parent, SubTableListener listener) { private readonly int m_prefixLen = prefixLen; private readonly NetworkTable m_parent = parent; - private readonly Action m_listener = listener; + private readonly SubTableListener m_listener = listener; private readonly HashSet m_notifiedTables = []; public void OnEvent(NetworkTableEvent ntEvent) @@ -256,7 +260,7 @@ public void OnEvent(NetworkTableEvent ntEvent) } } - public NtListener AddSubTableListener(Action listener) + public NtListener AddSubTableListener(SubTableListener listener) { int prefixLen = Path.Length + 1; SubTableListenerHolder holder = new(prefixLen, this, listener); diff --git a/src/ntcore/NetworkTableInstance.cs b/src/ntcore/NetworkTableInstance.cs index ed56bb02..3f04521c 100644 --- a/src/ntcore/NetworkTableInstance.cs +++ b/src/ntcore/NetworkTableInstance.cs @@ -12,6 +12,7 @@ using NetworkTables.Handles; using NetworkTables.Natives; using WPIUtil.Concurrent; +using WPIUtil.Function; using WPIUtil.Handles; using WPIUtil.Logging; using WPIUtil.Natives; @@ -205,14 +206,14 @@ public bool WaitForListenerQueue(TimeSpan? timeout) private class ListenerStorage(NtInst inst) : IDisposable { private readonly object m_lock = new(); - private readonly Dictionary> m_listeners = []; + private readonly Dictionary> m_listeners = []; private Thread? m_thread; private NtListenerPoller m_poller; private bool m_waitQueue; private readonly Event m_waitQueueEvent = new(); private readonly NtInst m_inst = inst; - public NtListener Add(ReadOnlySpan prefixes, EventFlags eventKinds, Action listener) + public NtListener Add(ReadOnlySpan prefixes, EventFlags eventKinds, Consumer listener) { lock (m_lock) { @@ -227,7 +228,7 @@ public NtListener Add(ReadOnlySpan prefixes, EventFlags eventKinds, Acti } } - public NtListener Add(NtInst handle, EventFlags eventKinds, Action listener) + public NtListener Add(NtInst handle, EventFlags eventKinds, Consumer listener) { lock (m_lock) { @@ -242,7 +243,7 @@ public NtListener Add(NtInst handle, EventFlags eventKinds, Action listener) + public NtListener Add(NtTopic handle, EventFlags eventKinds, Consumer listener) { lock (m_lock) { @@ -257,7 +258,7 @@ public NtListener Add(NtTopic handle, EventFlags eventKinds, Action listener) + public NtListener Add(NtMultiSubscriber handle, EventFlags eventKinds, Consumer listener) { lock (m_lock) { @@ -272,7 +273,7 @@ public NtListener Add(NtMultiSubscriber handle, EventFlags eventKinds, Action(T handle, EventFlags eventKinds, Action listener) where T : struct, INtEntryHandle + public NtListener Add(T handle, EventFlags eventKinds, Consumer listener) where T : struct, INtEntryHandle { lock (m_lock) { @@ -287,7 +288,7 @@ public NtListener Add(T handle, EventFlags eventKinds, Action listener) + public NtListener AddLogger(int minLevel, int maxLevel, Consumer listener) { lock (m_lock) { @@ -345,7 +346,7 @@ private void StartThread() } foreach (NetworkTableEvent evnt in NtCore.ReadListenerQueue(m_poller)) { - Action? listener; + Consumer? listener; lock (m_lock) { if (!m_listeners.TryGetValue(evnt.ListenerHandle, out listener)) @@ -413,7 +414,7 @@ public bool WaitForQueue(TimeSpan? timeout) } } - public NtListener AddConnectionListener(bool immediateNotify, Action listener) + public NtListener AddConnectionListener(bool immediateNotify, Consumer listener) { EventFlags flags = EventFlags.Connection; if (immediateNotify) @@ -423,7 +424,7 @@ public NtListener AddConnectionListener(bool immediateNotify, Action listener) + public NtListener AddTimeSyncListener(bool immediateNotify, Consumer listener) { EventFlags flags = EventFlags.TimeSync; if (immediateNotify) @@ -433,7 +434,7 @@ public NtListener AddTimeSyncListener(bool immediateNotify, Action listener) + public NtListener AddListener(Topic topic, EventFlags eventKinds, Consumer listener) { if (topic.Instance.Handle != Handle) { @@ -442,7 +443,7 @@ public NtListener AddListener(Topic topic, EventFlags eventKinds, Action listener) + public NtListener AddListener(ISubscriber subscriber, EventFlags eventKinds, Consumer listener) { if (subscriber.Topic.Instance.Handle != Handle) { @@ -451,7 +452,7 @@ public NtListener AddListener(ISubscriber subscriber, EventFlags eventKinds, Act return m_listeners.Add(subscriber.Handle, eventKinds, listener); } - public NtListener AddListener(MultiSubscriber subscriber, EventFlags eventKinds, Action listener) + public NtListener AddListener(MultiSubscriber subscriber, EventFlags eventKinds, Consumer listener) { if (subscriber.Instance.Handle != Handle) { @@ -461,7 +462,7 @@ public NtListener AddListener(MultiSubscriber subscriber, EventFlags eventKinds, return m_listeners.Add(subscriber.Handle, eventKinds, listener); } - public NtListener AddListener(ReadOnlySpan prefixes, EventFlags eventKinds, Action listener) + public NtListener AddListener(ReadOnlySpan prefixes, EventFlags eventKinds, Consumer listener) { return m_listeners.Add(prefixes, eventKinds, listener); } @@ -591,7 +592,7 @@ public static void StopConnectionDataLog(NtConnectionDataLogger logger) NtCore.StopConnectionDataLog(logger); } - public NtListener AddLogger(int minLevel, int maxLevel, Action callback) + public NtListener AddLogger(int minLevel, int maxLevel, Consumer callback) { return m_listeners.AddLogger(minLevel, maxLevel, callback); } diff --git a/src/wpilibsharp/HalInitializationException.cs b/src/wpilibsharp/HalInitializationException.cs new file mode 100644 index 00000000..d32ed31e --- /dev/null +++ b/src/wpilibsharp/HalInitializationException.cs @@ -0,0 +1,5 @@ +namespace WPILib; + +public class HalInitializationException(string msg) : Exception(msg) +{ +} diff --git a/src/wpilibsharp/RobotRunner.cs b/src/wpilibsharp/RobotRunner.cs index 4ec5b27d..d59a049e 100644 --- a/src/wpilibsharp/RobotRunner.cs +++ b/src/wpilibsharp/RobotRunner.cs @@ -2,22 +2,27 @@ namespace WPILib; -public static class RobotRunner where T : RobotBase, new() +public static class RobotRunner { - private static void RunRobot() + private static void RunRobot() where T : RobotBase, new() { Console.WriteLine("********** Robot program starting **********"); T robot = new(); } - public static void StartRobot() + public static void StartRobot() where T : RobotBase, new() + { + InitializeHAL(); + + RunRobot(); + } + + public static void InitializeHAL() { if (!HalBase.Initialize(500, 0)) { - throw new InvalidOperationException("Failed to initialize. Terminating"); + throw new HalInitializationException("Failed to initialize. Terminating"); } - - RunRobot(); } } diff --git a/src/wpiutil/Function/Delegates.cs b/src/wpiutil/Function/Delegates.cs new file mode 100644 index 00000000..8285a782 --- /dev/null +++ b/src/wpiutil/Function/Delegates.cs @@ -0,0 +1,17 @@ +namespace WPIUtil.Function; + +public delegate void BiConsumer(T t, U u); + +public delegate R BiFunction(T t, U u); + +public delegate bool BiPredicate(T t, U u); + +public delegate T Supplier(); + +public delegate void Consumer(T t); + +public delegate R Function(T t); + +public delegate bool Predicate(T t); + +public delegate void Runnable(); diff --git a/src/wpiutil/Sendable/ISendableBuilder.cs b/src/wpiutil/Sendable/ISendableBuilder.cs index 9448fca5..ae23b466 100644 --- a/src/wpiutil/Sendable/ISendableBuilder.cs +++ b/src/wpiutil/Sendable/ISendableBuilder.cs @@ -1,3 +1,5 @@ +using WPIUtil.Function; + namespace WPIUtil.Sendable; public interface ISendableBuilder : IDisposable @@ -16,51 +18,51 @@ enum BackingKind void SetActuator(bool value); - void SetSafeState(Action func); + void SetSafeState(Runnable func); - void AddBooleanProperty(string key, Func getter, Action setter); + void AddBooleanProperty(string key, Supplier getter, Consumer setter); void PublishConstBoolean(string key, bool value); - void AddIntegerProperty(string key, Func getter, Action setter); + void AddIntegerProperty(string key, Supplier getter, Consumer setter); void PublishConstInteger(string key, long value); - void AddFloatProperty(string key, Func getter, Action setter); + void AddFloatProperty(string key, Supplier getter, Consumer setter); void PublishConstFloat(string key, float value); - void AddDoubleProperty(string key, Func getter, Action setter); + void AddDoubleProperty(string key, Supplier getter, Consumer setter); void PublishConstDouble(string key, double value); - void AddStringProperty(string key, Func getter, Action setter); + void AddStringProperty(string key, Supplier getter, Consumer setter); void PublishConstString(string key, string value); void PublishConstString(string key, ReadOnlySpan value); - void AddBooleanArrayProperty(string key, Func getter, Action setter); + void AddBooleanArrayProperty(string key, Supplier getter, Consumer setter); void PublishConstBooleanArray(string key, ReadOnlySpan value); - void AddIntegerArrayProperty(string key, Func getter, Action setter); + void AddIntegerArrayProperty(string key, Supplier getter, Consumer setter); void PublishConstIntegerArray(string key, ReadOnlySpan value); - void AddFloatArrayProperty(string key, Func getter, Action setter); + void AddFloatArrayProperty(string key, Supplier getter, Consumer setter); void PublishConstFloatArray(string key, ReadOnlySpan value); - void AddDoubleArrayProperty(string key, Func getter, Action setter); + void AddDoubleArrayProperty(string key, Supplier getter, Consumer setter); void PublishConstDoubleArray(string key, ReadOnlySpan value); - void AddStringArrayProperty(string key, Func getter, Action setter); + void AddStringArrayProperty(string key, Supplier getter, Consumer setter); void PublishConstStringArray(string key, ReadOnlySpan value); - void AddRawProperty(string key, string typeString, Func getter, Action setter); + void AddRawProperty(string key, string typeString, Supplier getter, Consumer setter); void PublishConstRaw(string key, string typeString, ReadOnlySpan value); diff --git a/src/wpiutil/Sendable/SendableRegistery.cs b/src/wpiutil/Sendable/SendableRegistery.cs index 73dbd9fa..28875394 100644 --- a/src/wpiutil/Sendable/SendableRegistery.cs +++ b/src/wpiutil/Sendable/SendableRegistery.cs @@ -1,4 +1,5 @@ using System.Runtime.CompilerServices; +using WPIUtil.Function; namespace WPIUtil.Sendable; @@ -46,7 +47,7 @@ public void SetName(string moduleType, int moduleNumber, int channel) } } - private static Func? liveWindowFactory; + private static Supplier? liveWindowFactory; private static readonly ConditionalWeakTable components = []; private static int nextDataHandle; @@ -64,7 +65,7 @@ private static Component GetOrAdd(ISendable sendable) return comp; } - public static void SetLiveWindowBuilderFactory(Func factory) + public static void SetLiveWindowBuilderFactory(Supplier factory) { lock (s_lockObject) { @@ -447,7 +448,7 @@ public class CallbackData private static readonly List ForeachComponents = []; - public static void ForeachLiveWindow(int dataHandle, Action callback) + public static void ForeachLiveWindow(int dataHandle, Consumer callback) { lock (s_lockObject) { diff --git a/src/wpiutil/Serialization/Protobuf/IProtobuf.cs b/src/wpiutil/Serialization/Protobuf/IProtobuf.cs index 372334ed..4a888d96 100644 --- a/src/wpiutil/Serialization/Protobuf/IProtobuf.cs +++ b/src/wpiutil/Serialization/Protobuf/IProtobuf.cs @@ -1,5 +1,6 @@ using Google.Protobuf; using Google.Protobuf.Reflection; +using WPIUtil.Function; namespace WPIUtil.Serialization.Protobuf; @@ -9,12 +10,12 @@ public interface IProtobuf MessageDescriptor Descriptor { get; } IProtobuf[] Nested => []; - void ForEachDescriptor(Func exists, Action fn) + void ForEachDescriptor(Function exists, BiConsumer fn) { ForEachDescriptorImpl(Descriptor.File, exists, fn); } - private static void ForEachDescriptorImpl(FileDescriptor desc, Func exists, Action fn) + private static void ForEachDescriptorImpl(FileDescriptor desc, Function exists, BiConsumer fn) { string name = $"proto:{desc.Name}"; if (exists(name))