diff --git a/wpilibc/src/main/native/cpp/ErrorBase.cpp b/wpilibc/src/main/native/cpp/ErrorBase.cpp index 947e53b38a..c7e87c5685 100644 --- a/wpilibc/src/main/native/cpp/ErrorBase.cpp +++ b/wpilibc/src/main/native/cpp/ErrorBase.cpp @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2008-2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2008-2019 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ @@ -17,13 +17,38 @@ #include #include -#define WPI_ERRORS_DEFINE_STRINGS #include "frc/WPIErrors.h" using namespace frc; -static wpi::mutex globalErrorsMutex; -static std::set globalErrors; +namespace { +struct GlobalErrors { + wpi::mutex mutex; + std::set errors; + const Error* lastError{nullptr}; + + static GlobalErrors& GetInstance(); + static void Insert(const Error& error); + static void Insert(Error&& error); +}; +} // namespace + +GlobalErrors& GlobalErrors::GetInstance() { + static GlobalErrors inst; + return inst; +} + +void GlobalErrors::Insert(const Error& error) { + GlobalErrors& inst = GetInstance(); + std::lock_guard lock(inst.mutex); + inst.lastError = &(*inst.errors.insert(error).first); +} + +void GlobalErrors::Insert(Error&& error) { + GlobalErrors& inst = GetInstance(); + std::lock_guard lock(inst.mutex); + inst.lastError = &(*inst.errors.insert(std::move(error)).first); +} ErrorBase::ErrorBase() { HAL_Initialize(500, 0); } @@ -51,8 +76,7 @@ void ErrorBase::SetErrnoError(const wpi::Twine& contextMessage, this); // Update the global error if there is not one already set. - std::lock_guard mutex(globalErrorsMutex); - globalErrors.insert(m_error); + GlobalErrors::Insert(m_error); } void ErrorBase::SetImaqError(int success, const wpi::Twine& contextMessage, @@ -65,8 +89,7 @@ void ErrorBase::SetImaqError(int success, const wpi::Twine& contextMessage, function, lineNumber, this); // Update the global error if there is not one already set. - std::lock_guard mutex(globalErrorsMutex); - globalErrors.insert(m_error); + GlobalErrors::Insert(m_error); } } @@ -79,8 +102,7 @@ void ErrorBase::SetError(Error::Code code, const wpi::Twine& contextMessage, m_error.Set(code, contextMessage, filename, function, lineNumber, this); // Update the global error if there is not one already set. - std::lock_guard mutex(globalErrorsMutex); - globalErrors.insert(m_error); + GlobalErrors::Insert(m_error); } } @@ -99,8 +121,7 @@ void ErrorBase::SetErrorRange(Error::Code code, int32_t minRange, filename, function, lineNumber, this); // Update the global error if there is not one already set. - std::lock_guard mutex(globalErrorsMutex); - globalErrors.insert(m_error); + GlobalErrors::Insert(m_error); } } @@ -113,8 +134,7 @@ void ErrorBase::SetWPIError(const wpi::Twine& errorMessage, Error::Code code, lineNumber, this); // Update the global error if there is not one already set. - std::lock_guard mutex(globalErrorsMutex); - globalErrors.insert(m_error); + GlobalErrors::Insert(m_error); } void ErrorBase::CloneError(const ErrorBase& rhs) const { @@ -129,11 +149,9 @@ void ErrorBase::SetGlobalError(Error::Code code, int lineNumber) { // If there was an error if (code != 0) { - std::lock_guard mutex(globalErrorsMutex); - // Set the current error information for this object. - globalErrors.emplace(code, contextMessage, filename, function, lineNumber, - nullptr); + GlobalErrors::Insert( + Error(code, contextMessage, filename, function, lineNumber, nullptr)); } } @@ -141,12 +159,28 @@ void ErrorBase::SetGlobalWPIError(const wpi::Twine& errorMessage, const wpi::Twine& contextMessage, wpi::StringRef filename, wpi::StringRef function, int lineNumber) { - std::lock_guard mutex(globalErrorsMutex); - globalErrors.emplace(-1, errorMessage + ": " + contextMessage, filename, - function, lineNumber, nullptr); + GlobalErrors::Insert(Error(-1, errorMessage + ": " + contextMessage, filename, + function, lineNumber, nullptr)); } -const Error& ErrorBase::GetGlobalError() { - std::lock_guard mutex(globalErrorsMutex); - return *globalErrors.begin(); +Error ErrorBase::GetGlobalError() { + auto& inst = GlobalErrors::GetInstance(); + std::lock_guard mutex(inst.mutex); + if (!inst.lastError) return Error{}; + return *inst.lastError; +} + +std::vector ErrorBase::GetGlobalErrors() { + auto& inst = GlobalErrors::GetInstance(); + std::lock_guard mutex(inst.mutex); + std::vector rv; + for (auto&& error : inst.errors) rv.push_back(error); + return rv; +} + +void ErrorBase::ClearGlobalErrors() { + auto& inst = GlobalErrors::GetInstance(); + std::lock_guard mutex(inst.mutex); + inst.errors.clear(); + inst.lastError = nullptr; } diff --git a/wpilibc/src/main/native/include/frc/ErrorBase.h b/wpilibc/src/main/native/include/frc/ErrorBase.h index 3be976570f..095578a887 100644 --- a/wpilibc/src/main/native/include/frc/ErrorBase.h +++ b/wpilibc/src/main/native/include/frc/ErrorBase.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2008-2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2008-2019 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ @@ -7,6 +7,8 @@ #pragma once +#include + #include #include #include @@ -49,16 +51,16 @@ } while (0) #define wpi_setGlobalError(code) wpi_setGlobalErrorWithContext(code, "") #define wpi_setWPIErrorWithContext(error, context) \ - this->SetWPIError((wpi_error_s_##error), (wpi_error_value_##error), \ + this->SetWPIError(wpi_error_s_##error(), wpi_error_value_##error(), \ (context), __FILE__, __FUNCTION__, __LINE__) #define wpi_setWPIError(error) (wpi_setWPIErrorWithContext(error, "")) #define wpi_setStaticWPIErrorWithContext(object, error, context) \ - object->SetWPIError((wpi_error_s_##error), (context), __FILE__, \ + object->SetWPIError(wpi_error_s_##error(), (context), __FILE__, \ __FUNCTION__, __LINE__) #define wpi_setStaticWPIError(object, error) \ wpi_setStaticWPIErrorWithContext(object, error, "") #define wpi_setGlobalWPIErrorWithContext(error, context) \ - ::frc::ErrorBase::SetGlobalWPIError((wpi_error_s_##error), (context), \ + ::frc::ErrorBase::SetGlobalWPIError(wpi_error_s_##error(), (context), \ __FILE__, __FUNCTION__, __LINE__) #define wpi_setGlobalWPIError(error) wpi_setGlobalWPIErrorWithContext(error, "") @@ -191,9 +193,19 @@ class ErrorBase { wpi::StringRef function, int lineNumber); /** - * Retrieve the current global error. + * Retrieve the last global error. */ - static const Error& GetGlobalError(); + static Error GetGlobalError(); + + /** + * Retrieve all global errors. + */ + static std::vector GetGlobalErrors(); + + /** + * Clear global errors. + */ + void ClearGlobalErrors(); protected: mutable Error m_error; diff --git a/wpilibc/src/main/native/include/frc/WPIErrors.h b/wpilibc/src/main/native/include/frc/WPIErrors.h index a7c4f16725..8325c92250 100644 --- a/wpilibc/src/main/native/include/frc/WPIErrors.h +++ b/wpilibc/src/main/native/include/frc/WPIErrors.h @@ -1,5 +1,5 @@ /*----------------------------------------------------------------------------*/ -/* Copyright (c) 2008-2018 FIRST. All Rights Reserved. */ +/* Copyright (c) 2008-2019 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ @@ -9,93 +9,87 @@ #include -#ifdef WPI_ERRORS_DEFINE_STRINGS -#define S(label, offset, message) \ - const char* wpi_error_s_##label = message; \ - constexpr int wpi_error_value_##label = offset -#else -#define S(label, offset, message) \ - extern const char* wpi_error_s_##label; \ - constexpr int wpi_error_value_##label = offset -#endif +#define S(label, offset, message) \ + constexpr inline const char* wpi_error_s_##label() { return message; } \ + constexpr inline int wpi_error_value_##label() { return offset; } // Fatal errors S(ModuleIndexOutOfRange, -1, - "Allocating module that is out of range or not found"); -S(ChannelIndexOutOfRange, -1, "Allocating channel that is out of range"); -S(NotAllocated, -2, "Attempting to free unallocated resource"); -S(ResourceAlreadyAllocated, -3, "Attempted to reuse an allocated resource"); -S(NoAvailableResources, -4, "No available resources to allocate"); -S(NullParameter, -5, "A pointer parameter to a method is nullptr"); -S(Timeout, -6, "A timeout has been exceeded"); -S(CompassManufacturerError, -7, "Compass manufacturer doesn't match HiTechnic"); + "Allocating module that is out of range or not found") +S(ChannelIndexOutOfRange, -1, "Allocating channel that is out of range") +S(NotAllocated, -2, "Attempting to free unallocated resource") +S(ResourceAlreadyAllocated, -3, "Attempted to reuse an allocated resource") +S(NoAvailableResources, -4, "No available resources to allocate") +S(NullParameter, -5, "A pointer parameter to a method is nullptr") +S(Timeout, -6, "A timeout has been exceeded") +S(CompassManufacturerError, -7, "Compass manufacturer doesn't match HiTechnic") S(CompassTypeError, -8, - "Compass type doesn't match expected type for HiTechnic compass"); -S(IncompatibleMode, -9, "The object is in an incompatible mode"); + "Compass type doesn't match expected type for HiTechnic compass") +S(IncompatibleMode, -9, "The object is in an incompatible mode") S(AnalogTriggerLimitOrderError, -10, - "AnalogTrigger limits error. Lower limit > Upper Limit"); + "AnalogTrigger limits error. Lower limit > Upper Limit") S(AnalogTriggerPulseOutputError, -11, - "Attempted to read AnalogTrigger pulse output."); -S(TaskError, -12, "Task can't be started"); -S(TaskIDError, -13, "Task error: Invalid ID."); -S(TaskDeletedError, -14, "Task error: Task already deleted."); -S(TaskOptionsError, -15, "Task error: Invalid options."); -S(TaskMemoryError, -16, "Task can't be started due to insufficient memory."); -S(TaskPriorityError, -17, "Task error: Invalid priority [1-255]."); -S(DriveUninitialized, -18, "RobotDrive not initialized for the C interface"); + "Attempted to read AnalogTrigger pulse output.") +S(TaskError, -12, "Task can't be started") +S(TaskIDError, -13, "Task error: Invalid ID.") +S(TaskDeletedError, -14, "Task error: Task already deleted.") +S(TaskOptionsError, -15, "Task error: Invalid options.") +S(TaskMemoryError, -16, "Task can't be started due to insufficient memory.") +S(TaskPriorityError, -17, "Task error: Invalid priority [1-255].") +S(DriveUninitialized, -18, "RobotDrive not initialized for the C interface") S(CompressorNonMatching, -19, - "Compressor slot/channel doesn't match previous instance"); -S(CompressorAlreadyDefined, -20, "Creating a second compressor instance"); + "Compressor slot/channel doesn't match previous instance") +S(CompressorAlreadyDefined, -20, "Creating a second compressor instance") S(CompressorUndefined, -21, - "Using compressor functions without defining compressor"); + "Using compressor functions without defining compressor") S(InconsistentArrayValueAdded, -22, "When packing data into an array to the dashboard, not all values added were " - "of the same type."); + "of the same type.") S(MismatchedComplexTypeClose, -23, "When packing data to the dashboard, a Close for a complex type was called " - "without a matching Open."); + "without a matching Open.") S(DashboardDataOverflow, -24, "When packing data to the dashboard, too much data was packed and the buffer " - "overflowed."); + "overflowed.") S(DashboardDataCollision, -25, - "The same buffer was used for packing data and for printing."); -S(EnhancedIOMissing, -26, "IO is not attached or Enhanced IO is not enabled."); + "The same buffer was used for packing data and for printing.") +S(EnhancedIOMissing, -26, "IO is not attached or Enhanced IO is not enabled.") S(LineNotOutput, -27, - "Cannot SetDigitalOutput for a line not configured for output."); -S(ParameterOutOfRange, -28, "A parameter is out of range."); -S(SPIClockRateTooLow, -29, "SPI clock rate was below the minimum supported"); -S(JaguarVersionError, -30, "Jaguar firmware version error"); -S(JaguarMessageNotFound, -31, "Jaguar message not found"); -S(NetworkTablesReadError, -40, "Error reading NetworkTables socket"); -S(NetworkTablesBufferFull, -41, "Buffer full writing to NetworkTables socket"); + "Cannot SetDigitalOutput for a line not configured for output.") +S(ParameterOutOfRange, -28, "A parameter is out of range.") +S(SPIClockRateTooLow, -29, "SPI clock rate was below the minimum supported") +S(JaguarVersionError, -30, "Jaguar firmware version error") +S(JaguarMessageNotFound, -31, "Jaguar message not found") +S(NetworkTablesReadError, -40, "Error reading NetworkTables socket") +S(NetworkTablesBufferFull, -41, "Buffer full writing to NetworkTables socket") S(NetworkTablesWrongType, -42, - "The wrong type was read from the NetworkTables entry"); -S(NetworkTablesCorrupt, -43, "NetworkTables data stream is corrupt"); -S(SmartDashboardMissingKey, -43, "SmartDashboard data does not exist"); -S(CommandIllegalUse, -50, "Illegal use of Command"); -S(UnsupportedInSimulation, -80, "Unsupported in simulation"); -S(CameraServerError, -90, "CameraServer error"); -S(InvalidParameter, -100, "Invalid parameter value"); + "The wrong type was read from the NetworkTables entry") +S(NetworkTablesCorrupt, -43, "NetworkTables data stream is corrupt") +S(SmartDashboardMissingKey, -43, "SmartDashboard data does not exist") +S(CommandIllegalUse, -50, "Illegal use of Command") +S(UnsupportedInSimulation, -80, "Unsupported in simulation") +S(CameraServerError, -90, "CameraServer error") +S(InvalidParameter, -100, "Invalid parameter value") // Warnings -S(SampleRateTooHigh, 1, "Analog module sample rate is too high"); +S(SampleRateTooHigh, 1, "Analog module sample rate is too high") S(VoltageOutOfRange, 2, - "Voltage to convert to raw value is out of range [-10; 10]"); -S(CompressorTaskError, 3, "Compressor task won't start"); -S(LoopTimingError, 4, "Digital module loop timing is not the expected value"); -S(NonBinaryDigitalValue, 5, "Digital output value is not 0 or 1"); + "Voltage to convert to raw value is out of range [-10; 10]") +S(CompressorTaskError, 3, "Compressor task won't start") +S(LoopTimingError, 4, "Digital module loop timing is not the expected value") +S(NonBinaryDigitalValue, 5, "Digital output value is not 0 or 1") S(IncorrectBatteryChannel, 6, - "Battery measurement channel is not correct value"); -S(BadJoystickIndex, 7, "Joystick index is out of range, should be 0-3"); -S(BadJoystickAxis, 8, "Joystick axis or POV is out of range"); -S(InvalidMotorIndex, 9, "Motor index is out of range, should be 0-3"); -S(DriverStationTaskError, 10, "Driver Station task won't start"); + "Battery measurement channel is not correct value") +S(BadJoystickIndex, 7, "Joystick index is out of range, should be 0-3") +S(BadJoystickAxis, 8, "Joystick axis or POV is out of range") +S(InvalidMotorIndex, 9, "Motor index is out of range, should be 0-3") +S(DriverStationTaskError, 10, "Driver Station task won't start") S(EnhancedIOPWMPeriodOutOfRange, 11, - "Driver Station Enhanced IO PWM Output period out of range."); -S(SPIWriteNoMOSI, 12, "Cannot write to SPI port with no MOSI output"); -S(SPIReadNoMISO, 13, "Cannot read from SPI port with no MISO input"); -S(SPIReadNoData, 14, "No data available to read from SPI"); + "Driver Station Enhanced IO PWM Output period out of range.") +S(SPIWriteNoMOSI, 12, "Cannot write to SPI port with no MOSI output") +S(SPIReadNoMISO, 13, "Cannot read from SPI port with no MISO input") +S(SPIReadNoData, 14, "No data available to read from SPI") S(IncompatibleState, 15, - "Incompatible State: The operation cannot be completed"); + "Incompatible State: The operation cannot be completed") #undef S