diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..17e5e66 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +*.pyc +*.so +__pycache__ + +.project +.pydevproject +.vscode + +*.egg-info +build +dist + +rev/version.py +rev/_impl/autogen + + +docs/_build +docs/_sidebar.rst.inc diff --git a/LICENSE.txt b/LICENSE similarity index 100% rename from LICENSE.txt rename to LICENSE diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..87f868d --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,5 @@ +include LICENSE +include gen/*.j2 gen/*.yml gen/hooks.py +include rev/_impl/rev_roborio.cpp +include rev/_impl/wpilibc/frc/*.cpp rev/_impl/wpilibc/frc/*.h +include rev/_impl/autogen/*.cpp rev/_impl/autogen/*.hpp rev/_impl/autogen/*.inc diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..c668f21 --- /dev/null +++ b/README.rst @@ -0,0 +1,32 @@ +robotpy-rev +=========== + +This is a python wrapper around the REV Spark MAX API. The RobotPy project +is not associated with or endorsed by REV Robotics. + +**WARNING**: THESE BINDINGS DO NOT WORK YET!! + +Documentation +============= + +* `Installation `_ +* `Python API Documentation `_ +* `REV's SparkMAX software `_ + + +Developers Notes +================ + +Notes for advanced users and developers can be found in develop.md in the git +repository. + +License +======= + +RobotPy specific bits are available under the Apache 2.0 license. The REV +specific pieces are available under a BSD license. + +Author +====== + +Dustin Spicuzza (dustin@virtualroadside.com) diff --git a/rev/__init__.py b/rev/__init__.py new file mode 100644 index 0000000..29def97 --- /dev/null +++ b/rev/__init__.py @@ -0,0 +1,6 @@ +from ._impl import CANSparkMax, MotorType, CANError + +try: + from .version import __version__ +except ImportError: # pragma: nocover + __version__ = "master" diff --git a/rev/_impl/__init__.py b/rev/_impl/__init__.py new file mode 100644 index 0000000..f33369e --- /dev/null +++ b/rev/_impl/__init__.py @@ -0,0 +1,29 @@ +import hal + + +if hal.isSimulation(): + + import enum + + class MotorType(enum.IntEnum): + kBrushed = 0 + kBrushless = 1 + + class CANError(enum.IntEnum): + kOK = 0 + kError = 1 + kTimeout = 2 + + class CANSparkMax: + def __init__(self, value, type): + pass + + def set(self, value): + raise NotImplementedError + + def get(self): + raise NotImplementedError + + +else: + from .rev_roborio import CANSparkMax, MotorType, CANError diff --git a/rev/_impl/info.py b/rev/_impl/info.py new file mode 100644 index 0000000..03ad636 --- /dev/null +++ b/rev/_impl/info.py @@ -0,0 +1,5 @@ +class Info: + def module_info(self): + from .. import version + + return "REV bindings", version.__version__, version.__rev_version__ diff --git a/rev/_impl/rev_roborio.cpp b/rev/_impl/rev_roborio.cpp new file mode 100644 index 0000000..08c09d5 --- /dev/null +++ b/rev/_impl/rev_roborio.cpp @@ -0,0 +1,27 @@ + +#include +#include + +namespace py = pybind11; + +// Use this to release the gil +typedef py::call_guard release_gil; + +// REV includes +#include "rev/CANSparkMax.h" + +PYBIND11_MODULE(rev_roborio, m) { + py::enum_(m, "MotorType") + .value("kBrushed", rev::CANSparkMax::MotorType::kBrushed) + .value("kBrushless", rev::CANSparkMax::MotorType::kBrushless); + + py::enum_(m, "CANError") + .value("kOK", rev::CANError::kOK) + .value("kError", rev::CANError::kError) + .value("kTimeout", rev::CANError::kTimeout); + + py::class_ cls(m, "CANSparkMax"); + cls.def(py::init(), release_gil()) + .def("set", &rev::CANSparkMax::Set, release_gil()) + .def("get", &rev::CANSparkMax::Get, release_gil()); +} \ No newline at end of file diff --git a/rev/_impl/wpilibc/SpeedController.h b/rev/_impl/wpilibc/SpeedController.h new file mode 100644 index 0000000..49a828b --- /dev/null +++ b/rev/_impl/wpilibc/SpeedController.h @@ -0,0 +1,60 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2008-2018 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. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include "frc/PIDOutput.h" + +namespace frc { + +/** + * Interface for speed controlling devices. + */ +class SpeedController : public PIDOutput { + public: + virtual ~SpeedController() = default; + + /** + * Common interface for setting the speed of a speed controller. + * + * @param speed The speed to set. Value should be between -1.0 and 1.0. + */ + virtual void Set(double speed) = 0; + + /** + * Common interface for getting the current set speed of a speed controller. + * + * @return The current set speed. Value is between -1.0 and 1.0. + */ + virtual double Get() const = 0; + + /** + * Common interface for inverting direction of a speed controller. + * + * @param isInverted The state of inversion, true is inverted. + */ + virtual void SetInverted(bool isInverted) = 0; + + /** + * Common interface for returning the inversion state of a speed controller. + * + * @return isInverted The state of inversion, true is inverted. + */ + virtual bool GetInverted() const = 0; + + /** + * Common interface for disabling a motor. + */ + virtual void Disable() = 0; + + /** + * Common interface to stop the motor until Set is called again. + */ + virtual void StopMotor() = 0; +}; + +} // namespace frc diff --git a/rev/_impl/wpilibc/frc/Base.h b/rev/_impl/wpilibc/frc/Base.h new file mode 100644 index 0000000..1fdbe5d --- /dev/null +++ b/rev/_impl/wpilibc/frc/Base.h @@ -0,0 +1,35 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2008-2018 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. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ < 5 +static_assert(0, + "GCC must be 5 or greater. If building for the roboRIO, please " + "update to the 2018 toolchains."); +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1900 +static_assert(0, "Visual Studio 2015 or greater required."); +#endif + +/** WPILib FRC namespace */ +namespace frc { + +// A struct to use as a deleter when a std::shared_ptr must wrap a raw pointer +// that is being deleted by someone else. +template +struct NullDeleter { + void operator()(T*) const noexcept {}; +}; + +} // namespace frc + +// For backwards compatibility +#ifdef NO_NAMESPACED_WPILIB +using namespace frc; // NOLINT +#endif diff --git a/rev/_impl/wpilibc/frc/CAN.cpp b/rev/_impl/wpilibc/frc/CAN.cpp new file mode 100644 index 0000000..f01eb3f --- /dev/null +++ b/rev/_impl/wpilibc/frc/CAN.cpp @@ -0,0 +1,147 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2018 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. */ +/*----------------------------------------------------------------------------*/ + +#include "frc/CAN.h" + +#include + +#include +#include +#include +#include +#include + +using namespace frc; + +CAN::CAN(int deviceId) { + int32_t status = 0; + m_handle = + HAL_InitializeCAN(kTeamManufacturer, deviceId, kTeamDeviceType, &status); + if (status != 0) { + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + m_handle = HAL_kInvalidHandle; + return; + } + + HAL_Report(HALUsageReporting::kResourceType_CAN, deviceId); +} + +CAN::CAN(int deviceId, int deviceManufacturer, int deviceType) { + int32_t status = 0; + m_handle = HAL_InitializeCAN( + static_cast(deviceManufacturer), deviceId, + static_cast(deviceType), &status); + if (status != 0) { + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + m_handle = HAL_kInvalidHandle; + return; + } + + HAL_Report(HALUsageReporting::kResourceType_CAN, deviceId); +} + +CAN::~CAN() { + if (StatusIsFatal()) return; + if (m_handle != HAL_kInvalidHandle) { + HAL_CleanCAN(m_handle); + m_handle = HAL_kInvalidHandle; + } +} + +CAN::CAN(CAN&& rhs) : ErrorBase(std::move(rhs)) { + std::swap(m_handle, rhs.m_handle); +} + +CAN& CAN::operator=(CAN&& rhs) { + ErrorBase::operator=(std::move(rhs)); + + std::swap(m_handle, rhs.m_handle); + + return *this; +} + +void CAN::WritePacket(const uint8_t* data, int length, int apiId) { + int32_t status = 0; + HAL_WriteCANPacket(m_handle, data, length, apiId, &status); + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); +} + +void CAN::WritePacketRepeating(const uint8_t* data, int length, int apiId, + int repeatMs) { + int32_t status = 0; + HAL_WriteCANPacketRepeating(m_handle, data, length, apiId, repeatMs, &status); + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); +} + +void CAN::StopPacketRepeating(int apiId) { + int32_t status = 0; + HAL_StopCANPacketRepeating(m_handle, apiId, &status); + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); +} + +bool CAN::ReadPacketNew(int apiId, CANData* data) { + int32_t status = 0; + HAL_ReadCANPacketNew(m_handle, apiId, data->data, &data->length, + &data->timestamp, &status); + if (status == HAL_ERR_CANSessionMux_MessageNotFound) { + return false; + } + if (status != 0) { + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + return false; + } else { + return true; + } +} + +bool CAN::ReadPacketLatest(int apiId, CANData* data) { + int32_t status = 0; + HAL_ReadCANPacketLatest(m_handle, apiId, data->data, &data->length, + &data->timestamp, &status); + if (status == HAL_ERR_CANSessionMux_MessageNotFound) { + return false; + } + if (status != 0) { + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + return false; + } else { + return true; + } +} + +bool CAN::ReadPacketTimeout(int apiId, int timeoutMs, CANData* data) { + int32_t status = 0; + HAL_ReadCANPacketTimeout(m_handle, apiId, data->data, &data->length, + &data->timestamp, timeoutMs, &status); + if (status == HAL_CAN_TIMEOUT || + status == HAL_ERR_CANSessionMux_MessageNotFound) { + return false; + } + if (status != 0) { + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + return false; + } else { + return true; + } +} + +bool CAN::ReadPeriodicPacket(int apiId, int timeoutMs, int periodMs, + CANData* data) { + int32_t status = 0; + HAL_ReadCANPeriodicPacket(m_handle, apiId, data->data, &data->length, + &data->timestamp, timeoutMs, periodMs, &status); + if (status == HAL_CAN_TIMEOUT || + status == HAL_ERR_CANSessionMux_MessageNotFound) { + return false; + } + if (status != 0) { + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + return false; + } else { + return true; + } +} diff --git a/rev/_impl/wpilibc/frc/CAN.h b/rev/_impl/wpilibc/frc/CAN.h new file mode 100644 index 0000000..4cd06e9 --- /dev/null +++ b/rev/_impl/wpilibc/frc/CAN.h @@ -0,0 +1,149 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2018 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. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include + +#include +#include + +#include "frc/ErrorBase.h" + +namespace frc { +struct CANData { + uint8_t data[8]; + int32_t length; + uint64_t timestamp; +}; + +/** + * High level class for interfacing with CAN devices conforming to + * the standard CAN spec. + * + * No packets that can be sent gets blocked by the RoboRIO, so all methods + * work identically in all robot modes. + * + * All methods are thread save, however the buffer objects passed in + * by the user need to not be modified for the duration of their calls. + */ +class CAN : public ErrorBase { + public: + /** + * Create a new CAN communication interface with the specific device ID. + * This uses the team manufacturer and device types. + * The device ID is 6 bits (0-63) + * + * @param deviceId The device id + */ + explicit CAN(int deviceId); + + /** + * Create a new CAN communication interface with a specific device ID, + * manufacturer and device type. The device ID is 6 bits, the + * manufacturer is 8 bits, and the device type is 5 bits. + * + * @param deviceId The device ID + * @param deviceManufacturer The device manufacturer + * @param deviceType The device type + */ + CAN(int deviceId, int deviceManufacturer, int deviceType); + + /** + * Closes the CAN communication. + */ + ~CAN() override; + + CAN(CAN&& rhs); + CAN& operator=(CAN&& rhs); + + /** + * Write a packet to the CAN device with a specific ID. This ID is 10 bits. + * + * @param data The data to write (8 bytes max) + * @param length The data length to write + * @param apiId The API ID to write. + */ + void WritePacket(const uint8_t* data, int length, int apiId); + + /** + * Write a repeating packet to the CAN device with a specific ID. This ID is + * 10 bits. The RoboRIO will automatically repeat the packet at the specified + * interval + * + * @param data The data to write (8 bytes max) + * @param length The data length to write + * @param apiId The API ID to write. + * @param repeatMs The period to repeat the packet at. + */ + void WritePacketRepeating(const uint8_t* data, int length, int apiId, + int repeatMs); + + /** + * Stop a repeating packet with a specific ID. This ID is 10 bits. + * + * @param apiId The API ID to stop repeating + */ + void StopPacketRepeating(int apiId); + + /** + * Read a new CAN packet. This will only return properly once per packet + * received. Multiple calls without receiving another packet will return + * false. + * + * @param apiId The API ID to read. + * @param data Storage for the received data. + * @return True if the data is valid, otherwise false. + */ + bool ReadPacketNew(int apiId, CANData* data); + + /** + * Read a CAN packet. The will continuously return the last packet received, + * without accounting for packet age. + * + * @param apiId The API ID to read. + * @param data Storage for the received data. + * @return True if the data is valid, otherwise false. + */ + bool ReadPacketLatest(int apiId, CANData* data); + + /** + * Read a CAN packet. The will return the last packet received until the + * packet is older then the requested timeout. Then it will return false. + * + * @param apiId The API ID to read. + * @param timeoutMs The timeout time for the packet + * @param data Storage for the received data. + * @return True if the data is valid, otherwise false. + */ + bool ReadPacketTimeout(int apiId, int timeoutMs, CANData* data); + + /** + * Read a CAN packet. The will return the last packet received until the + * packet is older then the requested timeout. Then it will return false. The + * period parameter is used when you know the packet is sent at specific + * intervals, so calls will not attempt to read a new packet from the network + * until that period has passed. We do not recommend users use this API unless + * they know the implications. + * + * @param apiId The API ID to read. + * @param timeoutMs The timeout time for the packet + * @param periodMs The usual period for the packet + * @param data Storage for the received data. + * @return True if the data is valid, otherwise false. + */ + bool ReadPeriodicPacket(int apiId, int timeoutMs, int periodMs, + CANData* data); + + static constexpr HAL_CANManufacturer kTeamManufacturer = HAL_CAN_Man_kTeamUse; + static constexpr HAL_CANDeviceType kTeamDeviceType = + HAL_CAN_Dev_kMiscellaneous; + + private: + HAL_CANHandle m_handle = HAL_kInvalidHandle; +}; +} // namespace frc diff --git a/rev/_impl/wpilibc/frc/DriverStation.cpp b/rev/_impl/wpilibc/frc/DriverStation.cpp new file mode 100644 index 0000000..e8092e9 --- /dev/null +++ b/rev/_impl/wpilibc/frc/DriverStation.cpp @@ -0,0 +1,70 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2008-2018 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. */ +/*----------------------------------------------------------------------------*/ + +#include "frc/DriverStation.h" + +#include + +#include +#include +#include +#include +#include + +#include "frc/Timer.h" +#include "frc/Utility.h" +#include "frc/WPIErrors.h" + + +using namespace frc; + +static constexpr double kJoystickUnpluggedMessageInterval = 1.0; + +DriverStation::~DriverStation() { +} + +DriverStation& DriverStation::GetInstance() { + static DriverStation instance; + return instance; +} + +void DriverStation::ReportError(const wpi::Twine& error) { + wpi::SmallString<128> temp; + HAL_SendError(1, 1, 0, error.toNullTerminatedStringRef(temp).data(), "", "", + 1); +} + +void DriverStation::ReportWarning(const wpi::Twine& error) { + wpi::SmallString<128> temp; + HAL_SendError(0, 1, 0, error.toNullTerminatedStringRef(temp).data(), "", "", + 1); +} + +void DriverStation::ReportError(bool isError, int32_t code, + const wpi::Twine& error, + const wpi::Twine& location, + const wpi::Twine& stack) { + wpi::SmallString<128> errorTemp; + wpi::SmallString<128> locationTemp; + wpi::SmallString<128> stackTemp; + HAL_SendError(isError, code, 0, + error.toNullTerminatedStringRef(errorTemp).data(), + location.toNullTerminatedStringRef(locationTemp).data(), + stack.toNullTerminatedStringRef(stackTemp).data(), 1); +} + +bool DriverStation::IsSysActive() const { + int32_t status = 0; + bool retVal = HAL_GetSystemActive(&status); + wpi_setErrorWithContext(status, HAL_GetErrorMessage(status)); + return retVal; +} + +double DriverStation::GetMatchTime() const { + int32_t status; + return HAL_GetMatchTime(&status); +} \ No newline at end of file diff --git a/rev/_impl/wpilibc/frc/DriverStation.h b/rev/_impl/wpilibc/frc/DriverStation.h new file mode 100644 index 0000000..014ba07 --- /dev/null +++ b/rev/_impl/wpilibc/frc/DriverStation.h @@ -0,0 +1,97 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2008-2018 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. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "frc/ErrorBase.h" + +namespace frc { + + +/** + * Provide access to the network communication data to / from the Driver + * Station. + */ +class DriverStation : public ErrorBase { + public: + + ~DriverStation() override; + + + /** + * Return a reference to the singleton DriverStation. + * + * @return Reference to the DS instance + */ + static DriverStation& GetInstance(); + + /** + * Report an error to the DriverStation messages window. + * + * The error is also printed to the program console. + */ + static void ReportError(const wpi::Twine& error); + + /** + * Report a warning to the DriverStation messages window. + * + * The warning is also printed to the program console. + */ + static void ReportWarning(const wpi::Twine& error); + + /** + * Report an error to the DriverStation messages window. + * + * The error is also printed to the program console. + */ + static void ReportError(bool isError, int code, const wpi::Twine& error, + const wpi::Twine& location, const wpi::Twine& stack); + + /** + * Check if the FPGA outputs are enabled. + * + * The outputs may be disabled if the robot is disabled or e-stopped, the + * watchdog has expired, or if the roboRIO browns out. + * + * @return True if the FPGA outputs are enabled. + * @deprecated Use RobotController static class method + */ + WPI_DEPRECATED("Use RobotController static class method") + bool IsSysActive() const; + + /** + * Return the approximate match time. + * + * The FMS does not send an official match time to the robots, but does send + * an approximate match time. The value will count down the time remaining in + * the current period (auto or teleop). + * + * Warning: This is not an official time (so it cannot be used to dispute ref + * calls or guarantee that a function will trigger before the match ends). + * + * The Practice Match function of the DS approximates the behaviour seen on + * the field. + * + * @return Time remaining in current match period (auto or teleop) + */ + double GetMatchTime() const; + +}; + +} // namespace frc diff --git a/rev/_impl/wpilibc/frc/Error.cpp b/rev/_impl/wpilibc/frc/Error.cpp new file mode 100644 index 0000000..f758032 --- /dev/null +++ b/rev/_impl/wpilibc/frc/Error.cpp @@ -0,0 +1,98 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2008-2018 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. */ +/*----------------------------------------------------------------------------*/ + +#include "frc/Error.h" + +#include + +#include "frc/DriverStation.h" +#include "frc/Timer.h" +#include "frc/Utility.h" + +using namespace frc; + +Error::Error(Code code, const wpi::Twine& contextMessage, + wpi::StringRef filename, wpi::StringRef function, int lineNumber, + const ErrorBase* originatingObject) { + Set(code, contextMessage, filename, function, lineNumber, originatingObject); +} + +bool Error::operator<(const Error& rhs) const { + if (m_code < rhs.m_code) { + return true; + } else if (m_message < rhs.m_message) { + return true; + } else if (m_filename < rhs.m_filename) { + return true; + } else if (m_function < rhs.m_function) { + return true; + } else if (m_lineNumber < rhs.m_lineNumber) { + return true; + } else if (m_originatingObject < rhs.m_originatingObject) { + return true; + } else if (m_timestamp < rhs.m_timestamp) { + return true; + } else { + return false; + } +} + +Error::Code Error::GetCode() const { return m_code; } + +std::string Error::GetMessage() const { return m_message; } + +std::string Error::GetFilename() const { return m_filename; } + +std::string Error::GetFunction() const { return m_function; } + +int Error::GetLineNumber() const { return m_lineNumber; } + +const ErrorBase* Error::GetOriginatingObject() const { + return m_originatingObject; +} + +double Error::GetTimestamp() const { return m_timestamp; } + +void Error::Set(Code code, const wpi::Twine& contextMessage, + wpi::StringRef filename, wpi::StringRef function, + int lineNumber, const ErrorBase* originatingObject) { + bool report = true; + + if (code == m_code && GetTime() - m_timestamp < 1) { + report = false; + } + + m_code = code; + m_message = contextMessage.str(); + m_filename = filename; + m_function = function; + m_lineNumber = lineNumber; + m_originatingObject = originatingObject; + + if (report) { + m_timestamp = GetTime(); + Report(); + } +} + +void Error::Report() { + DriverStation::ReportError( + true, m_code, m_message, + m_function + wpi::Twine(" [") + wpi::sys::path::filename(m_filename) + + wpi::Twine(':') + wpi::Twine(m_lineNumber) + wpi::Twine(']'), + GetStackTrace(4)); +} + +void Error::Clear() { + m_code = 0; + m_message = ""; + m_filename = ""; + m_function = ""; + m_lineNumber = 0; + m_originatingObject = nullptr; + m_timestamp = 0.0; +} diff --git a/rev/_impl/wpilibc/frc/Error.h b/rev/_impl/wpilibc/frc/Error.h new file mode 100644 index 0000000..8eafd10 --- /dev/null +++ b/rev/_impl/wpilibc/frc/Error.h @@ -0,0 +1,70 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2008-2018 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. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include + +#include + +#include +#include + +#ifdef _WIN32 +#pragma push_macro("GetMessage") +#undef GetMessage +#endif + +#include "frc/Base.h" + +namespace frc { + +class ErrorBase; + +/** + * Error object represents a library error. + */ +class Error { + public: + using Code = int; + + Error() = default; + Error(Code code, const wpi::Twine& contextMessage, wpi::StringRef filename, + wpi::StringRef function, int lineNumber, + const ErrorBase* originatingObject); + + bool operator<(const Error& rhs) const; + + Code GetCode() const; + std::string GetMessage() const; + std::string GetFilename() const; + std::string GetFunction() const; + int GetLineNumber() const; + const ErrorBase* GetOriginatingObject() const; + double GetTimestamp() const; + void Clear(); + void Set(Code code, const wpi::Twine& contextMessage, wpi::StringRef filename, + wpi::StringRef function, int lineNumber, + const ErrorBase* originatingObject); + + private: + void Report(); + + Code m_code = 0; + std::string m_message; + std::string m_filename; + std::string m_function; + int m_lineNumber = 0; + const ErrorBase* m_originatingObject = nullptr; + double m_timestamp = 0.0; +}; + +} // namespace frc + +#ifdef _WIN32 +#pragma pop_macro("GetMessage") +#endif diff --git a/rev/_impl/wpilibc/frc/ErrorBase.cpp b/rev/_impl/wpilibc/frc/ErrorBase.cpp new file mode 100644 index 0000000..947e53b --- /dev/null +++ b/rev/_impl/wpilibc/frc/ErrorBase.cpp @@ -0,0 +1,152 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2008-2018 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. */ +/*----------------------------------------------------------------------------*/ + +#include "frc/ErrorBase.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define WPI_ERRORS_DEFINE_STRINGS +#include "frc/WPIErrors.h" + +using namespace frc; + +static wpi::mutex globalErrorsMutex; +static std::set globalErrors; + +ErrorBase::ErrorBase() { HAL_Initialize(500, 0); } + +Error& ErrorBase::GetError() { return m_error; } + +const Error& ErrorBase::GetError() const { return m_error; } + +void ErrorBase::ClearError() const { m_error.Clear(); } + +void ErrorBase::SetErrnoError(const wpi::Twine& contextMessage, + wpi::StringRef filename, wpi::StringRef function, + int lineNumber) const { + wpi::SmallString<128> buf; + wpi::raw_svector_ostream err(buf); + int errNo = errno; + if (errNo == 0) { + err << "OK: "; + } else { + err << std::strerror(errNo) << " (" << wpi::format_hex(errNo, 10, true) + << "): "; + } + + // Set the current error information for this object. + m_error.Set(-1, err.str() + 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); +} + +void ErrorBase::SetImaqError(int success, const wpi::Twine& contextMessage, + wpi::StringRef filename, wpi::StringRef function, + int lineNumber) const { + // If there was an error + if (success <= 0) { + // Set the current error information for this object. + m_error.Set(success, wpi::Twine(success) + ": " + 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); + } +} + +void ErrorBase::SetError(Error::Code code, const wpi::Twine& contextMessage, + wpi::StringRef filename, wpi::StringRef function, + int lineNumber) const { + // If there was an error + if (code != 0) { + // Set the current error information for this object. + 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); + } +} + +void ErrorBase::SetErrorRange(Error::Code code, int32_t minRange, + int32_t maxRange, int32_t requestedValue, + const wpi::Twine& contextMessage, + wpi::StringRef filename, wpi::StringRef function, + int lineNumber) const { + // If there was an error + if (code != 0) { + // Set the current error information for this object. + m_error.Set(code, + contextMessage + ", Minimum Value: " + wpi::Twine(minRange) + + ", MaximumValue: " + wpi::Twine(maxRange) + + ", Requested Value: " + wpi::Twine(requestedValue), + filename, function, lineNumber, this); + + // Update the global error if there is not one already set. + std::lock_guard mutex(globalErrorsMutex); + globalErrors.insert(m_error); + } +} + +void ErrorBase::SetWPIError(const wpi::Twine& errorMessage, Error::Code code, + const wpi::Twine& contextMessage, + wpi::StringRef filename, wpi::StringRef function, + int lineNumber) const { + // Set the current error information for this object. + m_error.Set(code, errorMessage + ": " + 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); +} + +void ErrorBase::CloneError(const ErrorBase& rhs) const { + m_error = rhs.GetError(); +} + +bool ErrorBase::StatusIsFatal() const { return m_error.GetCode() < 0; } + +void ErrorBase::SetGlobalError(Error::Code code, + const wpi::Twine& contextMessage, + wpi::StringRef filename, wpi::StringRef function, + 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); + } +} + +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); +} + +const Error& ErrorBase::GetGlobalError() { + std::lock_guard mutex(globalErrorsMutex); + return *globalErrors.begin(); +} diff --git a/rev/_impl/wpilibc/frc/ErrorBase.h b/rev/_impl/wpilibc/frc/ErrorBase.h new file mode 100644 index 0000000..3be9765 --- /dev/null +++ b/rev/_impl/wpilibc/frc/ErrorBase.h @@ -0,0 +1,202 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2008-2018 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. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include +#include +#include + +#include "frc/Base.h" +#include "frc/Error.h" + +#define wpi_setErrnoErrorWithContext(context) \ + this->SetErrnoError((context), __FILE__, __FUNCTION__, __LINE__) +#define wpi_setErrnoError() wpi_setErrnoErrorWithContext("") +#define wpi_setImaqErrorWithContext(code, context) \ + do { \ + if ((code) != 0) \ + this->SetImaqError((code), (context), __FILE__, __FUNCTION__, __LINE__); \ + } while (0) +#define wpi_setErrorWithContext(code, context) \ + do { \ + if ((code) != 0) \ + this->SetError((code), (context), __FILE__, __FUNCTION__, __LINE__); \ + } while (0) +#define wpi_setErrorWithContextRange(code, min, max, req, context) \ + do { \ + if ((code) != 0) \ + this->SetErrorRange((code), (min), (max), (req), (context), __FILE__, \ + __FUNCTION__, __LINE__); \ + } while (0) +#define wpi_setError(code) wpi_setErrorWithContext(code, "") +#define wpi_setStaticErrorWithContext(object, code, context) \ + do { \ + if ((code) != 0) \ + object->SetError((code), (context), __FILE__, __FUNCTION__, __LINE__); \ + } while (0) +#define wpi_setStaticError(object, code) \ + wpi_setStaticErrorWithContext(object, code, "") +#define wpi_setGlobalErrorWithContext(code, context) \ + do { \ + if ((code) != 0) \ + ::frc::ErrorBase::SetGlobalError((code), (context), __FILE__, \ + __FUNCTION__, __LINE__); \ + } while (0) +#define wpi_setGlobalError(code) wpi_setGlobalErrorWithContext(code, "") +#define wpi_setWPIErrorWithContext(error, context) \ + 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__, \ + __FUNCTION__, __LINE__) +#define wpi_setStaticWPIError(object, error) \ + wpi_setStaticWPIErrorWithContext(object, error, "") +#define wpi_setGlobalWPIErrorWithContext(error, context) \ + ::frc::ErrorBase::SetGlobalWPIError((wpi_error_s_##error), (context), \ + __FILE__, __FUNCTION__, __LINE__) +#define wpi_setGlobalWPIError(error) wpi_setGlobalWPIErrorWithContext(error, "") + +namespace frc { + +/** + * Base class for most objects. + * + * ErrorBase is the base class for most objects since it holds the generated + * error for that object. In addition, there is a single instance of a global + * error object. + */ +class ErrorBase { + // TODO: Consider initializing instance variables and cleanup in destructor + public: + ErrorBase(); + virtual ~ErrorBase() = default; + + ErrorBase(ErrorBase&&) = default; + ErrorBase& operator=(ErrorBase&&) = default; + + /** + * @brief Retrieve the current error. + * + * Get the current error information associated with this sensor. + */ + virtual Error& GetError(); + + /** + * @brief Retrieve the current error. + * + * Get the current error information associated with this sensor. + */ + virtual const Error& GetError() const; + + /** + * @brief Clear the current error information associated with this sensor. + */ + virtual void ClearError() const; + + /** + * @brief Set error information associated with a C library call that set an + * error to the "errno" global variable. + * + * @param contextMessage A custom message from the code that set the error. + * @param filename Filename of the error source + * @param function Function of the error source + * @param lineNumber Line number of the error source + */ + virtual void SetErrnoError(const wpi::Twine& contextMessage, + wpi::StringRef filename, wpi::StringRef function, + int lineNumber) const; + + /** + * @brief Set the current error information associated from the nivision Imaq + * API. + * + * @param success The return from the function + * @param contextMessage A custom message from the code that set the error. + * @param filename Filename of the error source + * @param function Function of the error source + * @param lineNumber Line number of the error source + */ + virtual void SetImaqError(int success, const wpi::Twine& contextMessage, + wpi::StringRef filename, wpi::StringRef function, + int lineNumber) const; + + /** + * @brief Set the current error information associated with this sensor. + * + * @param code The error code + * @param contextMessage A custom message from the code that set the error. + * @param filename Filename of the error source + * @param function Function of the error source + * @param lineNumber Line number of the error source + */ + virtual void SetError(Error::Code code, const wpi::Twine& contextMessage, + wpi::StringRef filename, wpi::StringRef function, + int lineNumber) const; + + /** + * @brief Set the current error information associated with this sensor. + * Range versions use for initialization code. + * + * @param code The error code + * @param minRange The minimum allowed allocation range + * @param maxRange The maximum allowed allocation range + * @param requestedValue The requested value to allocate + * @param contextMessage A custom message from the code that set the error. + * @param filename Filename of the error source + * @param function Function of the error source + * @param lineNumber Line number of the error source + */ + virtual void SetErrorRange(Error::Code code, int32_t minRange, + int32_t maxRange, int32_t requestedValue, + const wpi::Twine& contextMessage, + wpi::StringRef filename, wpi::StringRef function, + int lineNumber) const; + + /** + * @brief Set the current error information associated with this sensor. + * + * @param errorMessage The error message from WPIErrors.h + * @param contextMessage A custom message from the code that set the error. + * @param filename Filename of the error source + * @param function Function of the error source + * @param lineNumber Line number of the error source + */ + virtual void SetWPIError(const wpi::Twine& errorMessage, Error::Code code, + const wpi::Twine& contextMessage, + wpi::StringRef filename, wpi::StringRef function, + int lineNumber) const; + + virtual void CloneError(const ErrorBase& rhs) const; + + /** + * @brief Check if the current error code represents a fatal error. + * + * @return true if the current error is fatal. + */ + virtual bool StatusIsFatal() const; + + static void SetGlobalError(Error::Code code, const wpi::Twine& contextMessage, + wpi::StringRef filename, wpi::StringRef function, + int lineNumber); + + static void SetGlobalWPIError(const wpi::Twine& errorMessage, + const wpi::Twine& contextMessage, + wpi::StringRef filename, + wpi::StringRef function, int lineNumber); + + /** + * Retrieve the current global error. + */ + static const Error& GetGlobalError(); + + protected: + mutable Error m_error; +}; + +} // namespace frc diff --git a/rev/_impl/wpilibc/frc/PIDOutput.h b/rev/_impl/wpilibc/frc/PIDOutput.h new file mode 100644 index 0000000..37fb2a1 --- /dev/null +++ b/rev/_impl/wpilibc/frc/PIDOutput.h @@ -0,0 +1,25 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2008-2018 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. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include "frc/Base.h" + +namespace frc { + +/** + * PIDOutput interface is a generic output for the PID class. + * + * PWMs use this class. Users implement this interface to allow for a + * PIDController to read directly from the inputs. + */ +class PIDOutput { + public: + virtual void PIDWrite(double output) = 0; +}; + +} // namespace frc diff --git a/rev/_impl/wpilibc/frc/RobotController.cpp b/rev/_impl/wpilibc/frc/RobotController.cpp new file mode 100644 index 0000000..347440d --- /dev/null +++ b/rev/_impl/wpilibc/frc/RobotController.cpp @@ -0,0 +1,174 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2017-2018 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. */ +/*----------------------------------------------------------------------------*/ + +#include "frc/RobotController.h" + +#include + +#include "frc/ErrorBase.h" + +using namespace frc; + +int RobotController::GetFPGAVersion() { + int32_t status = 0; + int version = HAL_GetFPGAVersion(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return version; +} + +int64_t RobotController::GetFPGARevision() { + int32_t status = 0; + int64_t revision = HAL_GetFPGARevision(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return revision; +} + +uint64_t RobotController::GetFPGATime() { + int32_t status = 0; + uint64_t time = HAL_GetFPGATime(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return time; +} + +bool RobotController::GetUserButton() { + int32_t status = 0; + + bool value = HAL_GetFPGAButton(&status); + wpi_setGlobalError(status); + + return value; +} + +bool RobotController::IsSysActive() { + int32_t status = 0; + bool retVal = HAL_GetSystemActive(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return retVal; +} + +bool RobotController::IsBrownedOut() { + int32_t status = 0; + bool retVal = HAL_GetBrownedOut(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return retVal; +} + +double RobotController::GetInputVoltage() { + int32_t status = 0; + double retVal = HAL_GetVinVoltage(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return retVal; +} + +double RobotController::GetInputCurrent() { + int32_t status = 0; + double retVal = HAL_GetVinCurrent(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return retVal; +} + +double RobotController::GetVoltage3V3() { + int32_t status = 0; + double retVal = HAL_GetUserVoltage3V3(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return retVal; +} + +double RobotController::GetCurrent3V3() { + int32_t status = 0; + double retVal = HAL_GetUserCurrent3V3(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return retVal; +} + +bool RobotController::GetEnabled3V3() { + int32_t status = 0; + bool retVal = HAL_GetUserActive3V3(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return retVal; +} + +int RobotController::GetFaultCount3V3() { + int32_t status = 0; + int retVal = HAL_GetUserCurrentFaults3V3(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return retVal; +} + +double RobotController::GetVoltage5V() { + int32_t status = 0; + double retVal = HAL_GetUserVoltage5V(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return retVal; +} + +double RobotController::GetCurrent5V() { + int32_t status = 0; + double retVal = HAL_GetUserCurrent5V(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return retVal; +} + +bool RobotController::GetEnabled5V() { + int32_t status = 0; + bool retVal = HAL_GetUserActive5V(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return retVal; +} + +int RobotController::GetFaultCount5V() { + int32_t status = 0; + int retVal = HAL_GetUserCurrentFaults5V(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return retVal; +} + +double RobotController::GetVoltage6V() { + int32_t status = 0; + double retVal = HAL_GetUserVoltage6V(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return retVal; +} + +double RobotController::GetCurrent6V() { + int32_t status = 0; + double retVal = HAL_GetUserCurrent6V(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return retVal; +} + +bool RobotController::GetEnabled6V() { + int32_t status = 0; + bool retVal = HAL_GetUserActive6V(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return retVal; +} + +int RobotController::GetFaultCount6V() { + int32_t status = 0; + int retVal = HAL_GetUserCurrentFaults6V(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return retVal; +} + +CANStatus RobotController::GetCANStatus() { + int32_t status = 0; + float percentBusUtilization = 0; + uint32_t busOffCount = 0; + uint32_t txFullCount = 0; + uint32_t receiveErrorCount = 0; + uint32_t transmitErrorCount = 0; + HAL_CAN_GetCANStatus(&percentBusUtilization, &busOffCount, &txFullCount, + &receiveErrorCount, &transmitErrorCount, &status); + if (status != 0) { + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return {}; + } + return {percentBusUtilization, static_cast(busOffCount), + static_cast(txFullCount), static_cast(receiveErrorCount), + static_cast(transmitErrorCount)}; +} diff --git a/rev/_impl/wpilibc/frc/RobotController.h b/rev/_impl/wpilibc/frc/RobotController.h new file mode 100644 index 0000000..fae136b --- /dev/null +++ b/rev/_impl/wpilibc/frc/RobotController.h @@ -0,0 +1,188 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2017-2018 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. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include + +namespace frc { + +struct CANStatus { + float percentBusUtilization; + int busOffCount; + int txFullCount; + int receiveErrorCount; + int transmitErrorCount; +}; + +class RobotController { + public: + RobotController() = delete; + + /** + * Return the FPGA Version number. + * + * For now, expect this to be competition year. + * + * @return FPGA Version number. + */ + static int GetFPGAVersion(); + + /** + * Return the FPGA Revision number. + * + * The format of the revision is 3 numbers. The 12 most significant bits are + * the Major Revision. The next 8 bits are the Minor Revision. The 12 least + * significant bits are the Build Number. + * + * @return FPGA Revision number. + */ + static int64_t GetFPGARevision(); + + /** + * Read the microsecond-resolution timer on the FPGA. + * + * @return The current time in microseconds according to the FPGA (since FPGA + * reset). + */ + static uint64_t GetFPGATime(); + + /** + * Get the state of the "USER" button on the roboRIO. + * + * @return True if the button is currently pressed down + */ + static bool GetUserButton(); + + /** + * Check if the FPGA outputs are enabled. + * + * The outputs may be disabled if the robot is disabled or e-stopped, the + * watchdog has expired, or if the roboRIO browns out. + * + * @return True if the FPGA outputs are enabled. + */ + static bool IsSysActive(); + + /** + * Check if the system is browned out. + * + * @return True if the system is browned out + */ + static bool IsBrownedOut(); + + /** + * Get the input voltage to the robot controller. + * + * @return The controller input voltage value in Volts + */ + static double GetInputVoltage(); + + /** + * Get the input current to the robot controller. + * + * @return The controller input current value in Amps + */ + static double GetInputCurrent(); + + /** + * Get the voltage of the 3.3V rail. + * + * @return The controller 3.3V rail voltage value in Volts + */ + static double GetVoltage3V3(); + + /** + * Get the current output of the 3.3V rail. + * + * @return The controller 3.3V rail output current value in Amps + */ + static double GetCurrent3V3(); + + /** + * Get the enabled state of the 3.3V rail. The rail may be disabled due to a + * controller brownout, a short circuit on the rail, or controller + * over-voltage. + * + * @return The controller 3.3V rail enabled value. True for enabled. + */ + static bool GetEnabled3V3(); + + /** + * Get the count of the total current faults on the 3.3V rail since the + * controller has booted. + * + * @return The number of faults + */ + static int GetFaultCount3V3(); + + /** + * Get the voltage of the 5V rail. + * + * @return The controller 5V rail voltage value in Volts + */ + static double GetVoltage5V(); + + /** + * Get the current output of the 5V rail. + * + * @return The controller 5V rail output current value in Amps + */ + static double GetCurrent5V(); + + /** + * Get the enabled state of the 5V rail. The rail may be disabled due to a + * controller brownout, a short circuit on the rail, or controller + * over-voltage. + * + * @return The controller 5V rail enabled value. True for enabled. + */ + static bool GetEnabled5V(); + + /** + * Get the count of the total current faults on the 5V rail since the + * controller has booted. + * + * @return The number of faults + */ + static int GetFaultCount5V(); + + /** + * Get the voltage of the 6V rail. + * + * @return The controller 6V rail voltage value in Volts + */ + static double GetVoltage6V(); + + /** + * Get the current output of the 6V rail. + * + * @return The controller 6V rail output current value in Amps + */ + static double GetCurrent6V(); + + /** + * Get the enabled state of the 6V rail. The rail may be disabled due to a + * controller brownout, a short circuit on the rail, or controller + * over-voltage. + * + * @return The controller 6V rail enabled value. True for enabled. + */ + static bool GetEnabled6V(); + + /** + * Get the count of the total current faults on the 6V rail since the + * controller has booted. + * + * @return The number of faults. + */ + static int GetFaultCount6V(); + + static CANStatus GetCANStatus(); +}; + +} // namespace frc diff --git a/rev/_impl/wpilibc/frc/SpeedController.h b/rev/_impl/wpilibc/frc/SpeedController.h new file mode 100644 index 0000000..49a828b --- /dev/null +++ b/rev/_impl/wpilibc/frc/SpeedController.h @@ -0,0 +1,60 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2008-2018 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. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include "frc/PIDOutput.h" + +namespace frc { + +/** + * Interface for speed controlling devices. + */ +class SpeedController : public PIDOutput { + public: + virtual ~SpeedController() = default; + + /** + * Common interface for setting the speed of a speed controller. + * + * @param speed The speed to set. Value should be between -1.0 and 1.0. + */ + virtual void Set(double speed) = 0; + + /** + * Common interface for getting the current set speed of a speed controller. + * + * @return The current set speed. Value is between -1.0 and 1.0. + */ + virtual double Get() const = 0; + + /** + * Common interface for inverting direction of a speed controller. + * + * @param isInverted The state of inversion, true is inverted. + */ + virtual void SetInverted(bool isInverted) = 0; + + /** + * Common interface for returning the inversion state of a speed controller. + * + * @return isInverted The state of inversion, true is inverted. + */ + virtual bool GetInverted() const = 0; + + /** + * Common interface for disabling a motor. + */ + virtual void Disable() = 0; + + /** + * Common interface to stop the motor until Set is called again. + */ + virtual void StopMotor() = 0; +}; + +} // namespace frc diff --git a/rev/_impl/wpilibc/frc/Timer.cpp b/rev/_impl/wpilibc/frc/Timer.cpp new file mode 100644 index 0000000..ae4a66e --- /dev/null +++ b/rev/_impl/wpilibc/frc/Timer.cpp @@ -0,0 +1,107 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2008-2018 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. */ +/*----------------------------------------------------------------------------*/ + +#include "frc/Timer.h" + +#include +#include + +#include + +#include "frc/DriverStation.h" +#include "frc/RobotController.h" + +namespace frc { + +void Wait(double seconds) { + std::this_thread::sleep_for(std::chrono::duration(seconds)); +} + +double GetClock() { return Timer::GetFPGATimestamp(); } + +double GetTime() { + using std::chrono::duration; + using std::chrono::duration_cast; + using std::chrono::system_clock; + + return duration_cast>(system_clock::now().time_since_epoch()) + .count(); +} + +} // namespace frc + +using namespace frc; + +// for compatibility with msvc12--see C2864 +const double Timer::kRolloverTime = (1ll << 32) / 1e6; + +Timer::Timer() { Reset(); } + +double Timer::Get() const { + double result; + double currentTime = GetFPGATimestamp(); + + std::lock_guard lock(m_mutex); + if (m_running) { + // If the current time is before the start time, then the FPGA clock rolled + // over. Compensate by adding the ~71 minutes that it takes to roll over to + // the current time. + if (currentTime < m_startTime) { + currentTime += kRolloverTime; + } + + result = (currentTime - m_startTime) + m_accumulatedTime; + } else { + result = m_accumulatedTime; + } + + return result; +} + +void Timer::Reset() { + std::lock_guard lock(m_mutex); + m_accumulatedTime = 0; + m_startTime = GetFPGATimestamp(); +} + +void Timer::Start() { + std::lock_guard lock(m_mutex); + if (!m_running) { + m_startTime = GetFPGATimestamp(); + m_running = true; + } +} + +void Timer::Stop() { + double temp = Get(); + + std::lock_guard lock(m_mutex); + if (m_running) { + m_accumulatedTime = temp; + m_running = false; + } +} + +bool Timer::HasPeriodPassed(double period) { + if (Get() > period) { + std::lock_guard lock(m_mutex); + // Advance the start time by the period. + m_startTime += period; + // Don't set it to the current time... we want to avoid drift. + return true; + } + return false; +} + +double Timer::GetFPGATimestamp() { + // FPGA returns the timestamp in microseconds + return RobotController::GetFPGATime() * 1.0e-6; +} + +double Timer::GetMatchTime() { + return DriverStation::GetInstance().GetMatchTime(); +} diff --git a/rev/_impl/wpilibc/frc/Timer.h b/rev/_impl/wpilibc/frc/Timer.h new file mode 100644 index 0000000..f665c83 --- /dev/null +++ b/rev/_impl/wpilibc/frc/Timer.h @@ -0,0 +1,153 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2008-2018 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. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#include +#include + +#include "frc/Base.h" + +namespace frc { + +using TimerInterruptHandler = void (*)(void* param); + +/** + * Pause the task for a specified time. + * + * Pause the execution of the program for a specified period of time given in + * seconds. Motors will continue to run at their last assigned values, and + * sensors will continue to update. Only the task containing the wait will pause + * until the wait time is expired. + * + * @param seconds Length of time to pause, in seconds. + */ +void Wait(double seconds); + +/** + * Return the FPGA system clock time in seconds. + * + * This is deprecated and just forwards to Timer::GetFPGATimestamp(). + * + * @return Robot running time in seconds. + */ +WPI_DEPRECATED("Use Timer::GetFPGATimestamp() instead.") +double GetClock(); + +/** + * @brief Gives real-time clock system time with nanosecond resolution + * @return The time, just in case you want the robot to start autonomous at 8pm + * on Saturday. + */ +double GetTime(); + +/** + * Timer objects measure accumulated time in seconds. + * + * The timer object functions like a stopwatch. It can be started, stopped, and + * cleared. When the timer is running its value counts up in seconds. When + * stopped, the timer holds the current value. The implementation simply records + * the time when started and subtracts the current time whenever the value is + * requested. + */ +class Timer { + public: + /** + * Create a new timer object. + * + * Create a new timer object and reset the time to zero. The timer is + * initially not running and must be started. + */ + Timer(); + + virtual ~Timer() = default; + + Timer(Timer&&) = default; + Timer& operator=(Timer&&) = default; + + /** + * Get the current time from the timer. If the clock is running it is derived + * from the current system clock the start time stored in the timer class. If + * the clock is not running, then return the time when it was last stopped. + * + * @return Current time value for this timer in seconds + */ + double Get() const; + + /** + * Reset the timer by setting the time to 0. + * + * Make the timer startTime the current time so new requests will be relative + * to now. + */ + void Reset(); + + /** + * Start the timer running. + * + * Just set the running flag to true indicating that all time requests should + * be relative to the system clock. + */ + void Start(); + + /** + * Stop the timer. + * + * This computes the time as of now and clears the running flag, causing all + * subsequent time requests to be read from the accumulated time rather than + * looking at the system clock. + */ + void Stop(); + + /** + * Check if the period specified has passed and if it has, advance the start + * time by that period. This is useful to decide if it's time to do periodic + * work without drifting later by the time it took to get around to checking. + * + * @param period The period to check for (in seconds). + * @return True if the period has passed. + */ + bool HasPeriodPassed(double period); + + /** + * Return the FPGA system clock time in seconds. + * + * Return the time from the FPGA hardware clock in seconds since the FPGA + * started. Rolls over after 71 minutes. + * + * @returns Robot running time in seconds. + */ + static double GetFPGATimestamp(); + + /** + * Return the approximate match time. + * + * The FMS does not send an official match time to the robots, but does send + * an approximate match time. The value will count down the time remaining in + * the current period (auto or teleop). + * + * Warning: This is not an official time (so it cannot be used to dispute ref + * calls or guarantee that a function will trigger before the match ends). + * + * The Practice Match function of the DS approximates the behavior seen on the + * field. + * + * @return Time remaining in current match period (auto or teleop) + */ + static double GetMatchTime(); + + // The time, in seconds, at which the 32-bit FPGA timestamp rolls over to 0 + static const double kRolloverTime; + + private: + double m_startTime = 0.0; + double m_accumulatedTime = 0.0; + bool m_running = false; + mutable wpi::mutex m_mutex; +}; + +} // namespace frc diff --git a/rev/_impl/wpilibc/frc/Utility.cpp b/rev/_impl/wpilibc/frc/Utility.cpp new file mode 100644 index 0000000..503b6d0 --- /dev/null +++ b/rev/_impl/wpilibc/frc/Utility.cpp @@ -0,0 +1,203 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2008-2018 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. */ +/*----------------------------------------------------------------------------*/ + +#include "frc/Utility.h" + +#ifndef _WIN32 +#include +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "frc/ErrorBase.h" + +using namespace frc; + +bool wpi_assert_impl(bool conditionValue, const wpi::Twine& conditionText, + const wpi::Twine& message, wpi::StringRef fileName, + int lineNumber, wpi::StringRef funcName) { + if (!conditionValue) { + wpi::SmallString<128> locBuf; + wpi::raw_svector_ostream locStream(locBuf); + locStream << funcName << " [" << wpi::sys::path::filename(fileName) << ":" + << lineNumber << "]"; + + wpi::SmallString<128> errorBuf; + wpi::raw_svector_ostream errorStream(errorBuf); + + errorStream << "Assertion \"" << conditionText << "\" "; + + if (message.isTriviallyEmpty() || + (message.isSingleStringRef() && message.getSingleStringRef().empty())) { + errorStream << "failed.\n"; + } else { + errorStream << "failed: " << message << "\n"; + } + + std::string stack = GetStackTrace(2); + + // Print the error and send it to the DriverStation + HAL_SendError(1, 1, 0, errorBuf.c_str(), locBuf.c_str(), stack.c_str(), 1); + } + + return conditionValue; +} + +/** + * Common error routines for wpi_assertEqual_impl and wpi_assertNotEqual_impl. + * + * This should not be called directly; it should only be used by + * wpi_assertEqual_impl and wpi_assertNotEqual_impl. + */ +void wpi_assertEqual_common_impl(const wpi::Twine& valueA, + const wpi::Twine& valueB, + const wpi::Twine& equalityType, + const wpi::Twine& message, + wpi::StringRef fileName, int lineNumber, + wpi::StringRef funcName) { + wpi::SmallString<128> locBuf; + wpi::raw_svector_ostream locStream(locBuf); + locStream << funcName << " [" << wpi::sys::path::filename(fileName) << ":" + << lineNumber << "]"; + + wpi::SmallString<128> errorBuf; + wpi::raw_svector_ostream errorStream(errorBuf); + + errorStream << "Assertion \"" << valueA << " " << equalityType << " " + << valueB << "\" "; + + if (message.isTriviallyEmpty() || + (message.isSingleStringRef() && message.getSingleStringRef().empty())) { + errorStream << "failed.\n"; + } else { + errorStream << "failed: " << message << "\n"; + } + + std::string trace = GetStackTrace(3); + + // Print the error and send it to the DriverStation + HAL_SendError(1, 1, 0, errorBuf.c_str(), locBuf.c_str(), trace.c_str(), 1); +} + +bool wpi_assertEqual_impl(int valueA, int valueB, + const wpi::Twine& valueAString, + const wpi::Twine& valueBString, + const wpi::Twine& message, wpi::StringRef fileName, + int lineNumber, wpi::StringRef funcName) { + if (!(valueA == valueB)) { + wpi_assertEqual_common_impl(valueAString, valueBString, "==", message, + fileName, lineNumber, funcName); + } + return valueA == valueB; +} + +bool wpi_assertNotEqual_impl(int valueA, int valueB, + const wpi::Twine& valueAString, + const wpi::Twine& valueBString, + const wpi::Twine& message, wpi::StringRef fileName, + int lineNumber, wpi::StringRef funcName) { + if (!(valueA != valueB)) { + wpi_assertEqual_common_impl(valueAString, valueBString, "!=", message, + fileName, lineNumber, funcName); + } + return valueA != valueB; +} + +namespace frc { + +int GetFPGAVersion() { + int32_t status = 0; + int version = HAL_GetFPGAVersion(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return version; +} + +int64_t GetFPGARevision() { + int32_t status = 0; + int64_t revision = HAL_GetFPGARevision(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return revision; +} + +uint64_t GetFPGATime() { + int32_t status = 0; + uint64_t time = HAL_GetFPGATime(&status); + wpi_setGlobalErrorWithContext(status, HAL_GetErrorMessage(status)); + return time; +} + +bool GetUserButton() { + int32_t status = 0; + + bool value = HAL_GetFPGAButton(&status); + wpi_setGlobalError(status); + + return value; +} + +#ifndef _WIN32 + +/** + * Demangle a C++ symbol, used for printing stack traces. + */ +static std::string demangle(char const* mangledSymbol) { + char buffer[256]; + size_t length; + int32_t status; + + if (std::sscanf(mangledSymbol, "%*[^(]%*[(]%255[^)+]", buffer)) { + char* symbol = abi::__cxa_demangle(buffer, nullptr, &length, &status); + if (status == 0) { + return symbol; + } else { + // If the symbol couldn't be demangled, it's probably a C function, + // so just return it as-is. + return buffer; + } + } + + // If everything else failed, just return the mangled symbol + return mangledSymbol; +} + +std::string GetStackTrace(int offset) { + void* stackTrace[128]; + int stackSize = backtrace(stackTrace, 128); + char** mangledSymbols = backtrace_symbols(stackTrace, stackSize); + wpi::SmallString<1024> buf; + wpi::raw_svector_ostream trace(buf); + + for (int i = offset; i < stackSize; i++) { + // Only print recursive functions once in a row. + if (i == 0 || stackTrace[i] != stackTrace[i - 1]) { + trace << "\tat " << demangle(mangledSymbols[i]) << "\n"; + } + } + + std::free(mangledSymbols); + + return trace.str(); +} + +#else +static std::string demangle(char const* mangledSymbol) { + return "no demangling on windows"; +} + +std::string GetStackTrace(int offset) { return "no stack trace on windows"; } +#endif + +} // namespace frc diff --git a/rev/_impl/wpilibc/frc/Utility.h b/rev/_impl/wpilibc/frc/Utility.h new file mode 100644 index 0000000..9cb7abb --- /dev/null +++ b/rev/_impl/wpilibc/frc/Utility.h @@ -0,0 +1,127 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2008-2018 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. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +/** + * @file Contains global utility functions + */ + +#include + +#include + +#include +#include +#include + +#define wpi_assert(condition) \ + wpi_assert_impl(condition, #condition, "", __FILE__, __LINE__, __FUNCTION__) +#define wpi_assertWithMessage(condition, message) \ + wpi_assert_impl(condition, #condition, message, __FILE__, __LINE__, \ + __FUNCTION__) + +#define wpi_assertEqual(a, b) \ + wpi_assertEqual_impl(a, b, #a, #b, "", __FILE__, __LINE__, __FUNCTION__) +#define wpi_assertEqualWithMessage(a, b, message) \ + wpi_assertEqual_impl(a, b, #a, #b, message, __FILE__, __LINE__, __FUNCTION__) + +#define wpi_assertNotEqual(a, b) \ + wpi_assertNotEqual_impl(a, b, #a, #b, "", __FILE__, __LINE__, __FUNCTION__) +#define wpi_assertNotEqualWithMessage(a, b, message) \ + wpi_assertNotEqual_impl(a, b, #a, #b, message, __FILE__, __LINE__, \ + __FUNCTION__) + +/** + * Assert implementation. + * + * This allows breakpoints to be set on an assert. The users don't call this, + * but instead use the wpi_assert macros in Utility.h. + */ +bool wpi_assert_impl(bool conditionValue, const wpi::Twine& conditionText, + const wpi::Twine& message, wpi::StringRef fileName, + int lineNumber, wpi::StringRef funcName); + +/** + * Assert equal implementation. + * + * This determines whether the two given integers are equal. If not, the value + * of each is printed along with an optional message string. The users don't + * call this, but instead use the wpi_assertEqual macros in Utility.h. + */ +bool wpi_assertEqual_impl(int valueA, int valueB, + const wpi::Twine& valueAString, + const wpi::Twine& valueBString, + const wpi::Twine& message, wpi::StringRef fileName, + int lineNumber, wpi::StringRef funcName); + +/** + * Assert not equal implementation. + * + * This determines whether the two given integers are equal. If so, the value of + * each is printed along with an optional message string. The users don't call + * this, but instead use the wpi_assertNotEqual macros in Utility.h. + */ +bool wpi_assertNotEqual_impl(int valueA, int valueB, + const wpi::Twine& valueAString, + const wpi::Twine& valueBString, + const wpi::Twine& message, wpi::StringRef fileName, + int lineNumber, wpi::StringRef funcName); + +namespace frc { + +/** + * Return the FPGA Version number. + * + * For now, expect this to be competition year. + * + * @return FPGA Version number. + * @deprecated Use RobotController static class method + */ +WPI_DEPRECATED("Use RobotController static class method") +int GetFPGAVersion(); + +/** + * Return the FPGA Revision number. + * + * The format of the revision is 3 numbers. The 12 most significant bits are the + * Major Revision. The next 8 bits are the Minor Revision. The 12 least + * significant bits are the Build Number. + * + * @return FPGA Revision number. + * @deprecated Use RobotController static class method + */ +WPI_DEPRECATED("Use RobotController static class method") +int64_t GetFPGARevision(); + +/** + * Read the microsecond-resolution timer on the FPGA. + * + * @return The current time in microseconds according to the FPGA (since FPGA + * reset). + * @deprecated Use RobotController static class method + */ +WPI_DEPRECATED("Use RobotController static class method") +uint64_t GetFPGATime(); + +/** + * Get the state of the "USER" button on the roboRIO. + * + * @return True if the button is currently pressed down + * @deprecated Use RobotController static class method + */ +WPI_DEPRECATED("Use RobotController static class method") +bool GetUserButton(); + +/** + * Get a stack trace, ignoring the first "offset" symbols. + * + * @param offset The number of symbols at the top of the stack to ignore + */ +std::string GetStackTrace(int offset); + +} // namespace frc diff --git a/rev/_impl/wpilibc/frc/WPIErrors.h b/rev/_impl/wpilibc/frc/WPIErrors.h new file mode 100644 index 0000000..a7c4f16 --- /dev/null +++ b/rev/_impl/wpilibc/frc/WPIErrors.h @@ -0,0 +1,101 @@ +/*----------------------------------------------------------------------------*/ +/* Copyright (c) 2008-2018 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. */ +/*----------------------------------------------------------------------------*/ + +#pragma once + +#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 + +// 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"); +S(CompassTypeError, -8, + "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"); +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"); +S(CompressorNonMatching, -19, + "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"); +S(InconsistentArrayValueAdded, -22, + "When packing data into an array to the dashboard, not all values added were " + "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."); +S(DashboardDataOverflow, -24, + "When packing data to the dashboard, too much data was packed and the buffer " + "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."); +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"); +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"); + +// Warnings +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"); +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"); +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"); +S(IncompatibleState, 15, + "Incompatible State: The operation cannot be completed"); + +#undef S diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..e60dddd --- /dev/null +++ b/setup.py @@ -0,0 +1,395 @@ +# +# Much of this copied from https://github.com/pybind/python_example.git +# + +import os +from os.path import dirname, exists, join +from setuptools import find_packages, setup, Extension +from setuptools.command.build_ext import build_ext +from setuptools.command.sdist import sdist +import shutil +import subprocess +import sys +import setuptools + +rev_lib_version = "1.0.24" + +setup_dir = dirname(__file__) +git_dir = join(setup_dir, ".git") +base_package = "rev" +version_file = join(setup_dir, base_package, "version.py") + +# Automatically generate a version.py based on the git version +if exists(git_dir): + p = subprocess.Popen( + ["git", "describe", "--tags", "--long", "--dirty=-dirty"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + out, err = p.communicate() + # Make sure the git version has at least one tag + if err: + print("Error: You need to create a tag for this repo to use the builder") + sys.exit(1) + + # Convert git version to PEP440 compliant version + # - Older versions of pip choke on local identifiers, so we can't include the git commit + v, commits, local = out.decode("utf-8").rstrip().split("-", 2) + if commits != "0" or "-dirty" in local: + v = "%s.post0.dev%s" % (v, commits) + + # Create the version.py file + with open(version_file, "w") as fp: + fp.write("# Autogenerated by setup.py\n__version__ = '{0}'".format(v)) + fp.write("\n__rev_version__ = '%s'" % rev_lib_version) + +if exists(version_file): + with open(join(setup_dir, base_package, "version.py"), "r") as fp: + exec(fp.read(), globals()) +else: + __version__ = "master" + +with open(join(setup_dir, "README.rst"), "r") as readme_file: + long_description = readme_file.read() + + +# +# pybind-specific compilation stuff +# + + +class get_pybind_include(object): + """Helper class to determine the pybind11 include path + + The purpose of this class is to postpone importing pybind11 + until it is actually installed, so that the ``get_include()`` + method can be invoked. """ + + def __init__(self, user=False): + self.user = user + + def __str__(self): + import pybind11 + + return pybind11.get_include(self.user) + + +# As of Python 3.6, CCompiler has a `has_flag` method. +# cf http://bugs.python.org/issue26689 +def has_flag(compiler, flagname): + """Return a boolean indicating whether a flag name is supported on + the specified compiler. + """ + import tempfile + + with tempfile.NamedTemporaryFile("w", suffix=".cpp") as f: + f.write("int main (int argc, char **argv) { return 0; }") + try: + compiler.compile([f.name], extra_postargs=[flagname]) + except setuptools.distutils.errors.CompileError: + return False + return True + + +def cpp_flag(compiler): + """Return the -std=c++[11/14] compiler flag. + + The c++14 is prefered over c++11 (when it is available). + """ + if has_flag(compiler, "-std=c++14"): + return "-std=c++14" + elif has_flag(compiler, "-std=c++11"): + return "-std=c++11" + else: + raise RuntimeError( + "Unsupported compiler -- at least C++11 support " "is needed!" + ) + + +class BuildExt(build_ext): + """A custom build extension for adding compiler-specific options.""" + + c_opts = {"msvc": ["/EHsc"], "unix": []} + + if sys.platform == "darwin": + c_opts["unix"] += ["-stdlib=libc++", "-mmacosx-version-min=10.7"] + + def build_extensions(self): + ct = self.compiler.compiler_type + opts = self.c_opts.get(ct, []) + if ct == "unix": + opts.append('-DVERSION_INFO="%s"' % rev_lib_version) + opts.append("-s") # strip + opts.append("-g0") # remove debug symbols + opts.append(cpp_flag(self.compiler)) + if has_flag(self.compiler, "-fvisibility=hidden"): + opts.append("-fvisibility=hidden") + elif ct == "msvc": + opts.append('/DVERSION_INFO=\\"%s\\"' % rev_lib_version) + for ext in self.extensions: + ext.extra_compile_args = opts + build_ext.build_extensions(self) + + +install_requires = ["wpilib>=2019.0.0,<2020.0.0"] + + +class Downloader: + """ + Utility object to allow lazily retrieving needed artifacts on demand, + instead of distributing several extra MB with the pypi build. + """ + + def __init__(self): + self._hallib = None + self._halsrc = None + self._revsrc = None + self._wpiutillib = None + self._wpiutilsrc = None + + rev_devdir = os.environ.get("RPY_REV_DEVDIR") + if rev_devdir: + # development use only -- preextracted files so it doesn't have + # to download it over and over again + # -> if the directory doesn't exist, it will download the current + # files to that directory + + self._hallib = join(rev_devdir, "hallib") + self._halsrc = join(rev_devdir, "halsrc") + self._revsrc = join(rev_devdir, "rev") + self._wpiutillib = join(rev_devdir, "wpiutillib") + self._wpiutilsrc = join(rev_devdir, "wpiutilsrc") + + # copy/paste from hal_impl.distutils + def _download(self, url): + import atexit + import posixpath + from urllib.request import urlretrieve, urlcleanup + import sys + + print("Downloading", posixpath.basename(url)) + + def _reporthook(count, blocksize, totalsize): + percent = int(count * blocksize * 100 / totalsize) + sys.stdout.write("\r%02d%%" % percent) + sys.stdout.flush() + + filename, _ = urlretrieve(url, reporthook=_reporthook) + atexit.register(urlcleanup) + return filename + + def _download_and_extract_zip(self, url, to=None): + import atexit + import tempfile + + if to is None: + # generate temporary directory + tod = tempfile.TemporaryDirectory() + to = tod.name + atexit.register(tod.cleanup) + + zip_fname = self._download(url) + return self._extract_zip(zip_fname, to) + + def _extract_zip(self, zip_fname, to): + import shutil + import zipfile + + with zipfile.ZipFile(zip_fname) as z: + if isinstance(to, str): + z.extractall(to) + return to + else: + for src, dst in to.items(): + with z.open(src, "r") as zfp: + with open(dst, "wb") as fp: + shutil.copyfileobj(zfp, fp) + + @property + def hallib(self): + if not self._hallib or not exists(self._hallib): + import hal_impl.distutils + + self._hallib = hal_impl.distutils.extract_hal_libs(to=self._hallib) + return self._hallib + + @property + def halsrc(self): + if not self._halsrc or not exists(self._halsrc): + import hal_impl.distutils + + self._halsrc = hal_impl.distutils.extract_hal_headers(to=self._halsrc) + return self._halsrc + + @property + def wpiutillib(self): + if not self._wpiutillib or not exists(self._wpiutillib): + import hal_impl.distutils + + self._wpiutillib = hal_impl.distutils.extract_wpiutil_libs( + to=self._wpiutillib + ) + return self._wpiutillib + + @property + def wpiutilsrc(self): + if not self._wpiutilsrc or not exists(self._wpiutilsrc): + import hal_impl.distutils + + url = "%s/%s/%s" % ( + hal_impl.distutils.wpiutil_site, + hal_impl.distutils.wpiutil_version, + "wpiutil-cpp-%s-headers.zip" % hal_impl.distutils.wpiutil_version, + ) + + self._wpiutilsrc = self._download_and_extract_zip(url, to=self._wpiutilsrc) + return self._wpiutilsrc + + @property + def revsrc(self): + if not self._revsrc or not exists(self._revsrc): + # Download and extract + url = ( + "https://www.revrobotics.com/content/sw/max/sdk/SPARK-MAX-roboRIO-SDK-%s.zip" + % (rev_lib_version) + ) + self._revsrc = self._download_and_extract_zip(url, to=self._revsrc) + + # But there are embedded zips that need to be extracted + base = [ + "maven", + "com", + "revrobotics", + "frc", + "SparkMax-cpp", + rev_lib_version, + ] + for f in [ + "SparkMax-cpp-%(version)s-headers.zip", + "SparkMax-cpp-%(version)s-linuxathenastatic.zip", + ]: + zip_fname = join(self._revsrc, *base, f % dict(version=rev_lib_version)) + self._extract_zip(zip_fname, self._revsrc) + + return self._revsrc + + +get = Downloader() + +_travis_build = os.environ.get("TRAVIS_BUILD") + +# Detect roboRIO.. not foolproof, but good enough +if exists("/etc/natinst/share/scs_imagemetadata.ini") or _travis_build: + + # Don't try to link when testing on travis, as it will fail + # -> We can still catch compile errors, which is good enough I suspect + if _travis_build: + libraries = None + else: + libraries = ["wpiHal", "SparkMax"] + + wpilibc = join(setup_dir, base_package, "_impl", "wpilibc") + + ext_modules = [ + Extension( + "rev._impl.rev_roborio", + ["rev/_impl/rev_roborio.cpp"] + + [ + join(wpilibc, "frc", f) + for f in [ + "CAN.cpp", + "DriverStation.cpp", + "Error.cpp", + "ErrorBase.cpp", + "RobotController.cpp", + "Timer.cpp", + "Utility.cpp", + ] + ], + include_dirs=[ + # Path to pybind11 headers + get_pybind_include(), + get_pybind_include(user=True), + get.revsrc, + get.halsrc, + wpilibc, + join(get.halsrc), + join(get.wpiutilsrc), + ], + libraries=libraries, + library_dirs=[ + join(get.hallib, "linux", "athena", "shared"), + join(get.wpiutillib, "linux", "athena", "shared"), + join(get.revsrc, "linux", "athena", "static"), + ], + language="c++", + ) + ] + + # This doesn't actually work, as it needs to be installed before setup.py is ran + # ... but we specify it + # install_requires = ['pybind11>=1.7'] + install_requires.append("robotpy-hal-roborio>=2019.0.0,<2020.0.0") + cmdclass = {"build_ext": BuildExt} +else: + install_requires.append("robotpy-hal-sim>=2019.0.0,<2020.0.0") + ext_modules = None + cmdclass = {} + +# +# Autogenerating the required REV files is something that +# is done at sdist time. This means if you are testing builds, +# you have to run 'setup.py sdist build'. +# +# The advantage of doing it this way is that the autogen files +# are distributed with the pypi package, so simulation users +# don't have to install anything special to build this +# + + +class SDist(sdist): + def run(self): + # from header2whatever import batch_convert + # import CppHeaderParser + + # CppHeaderParser.ignoreSymbols.append("CCIEXPORT") + + # # Do this before deleting the autogen directory, as it may fail + # revsrc = get.revsrc + + # config_path = join(setup_dir, "gen", "gen.yml") + # outdir = join(setup_dir, "rev", "_impl", "autogen") + + # shutil.rmtree(outdir, ignore_errors=True) + + # batch_convert(config_path, outdir, revsrc) + + # with open(join(outdir, "__init__.py"), "w"): + # pass + + super().run() + + +cmdclass["sdist"] = SDist + +if os.environ.get("READTHEDOCS", None) == "True": + sys.argv.insert(1, "sdist") + +setup( + name="robotpy-rev", + version=__version__, + author="Dustin Spicuzza", + author_email="dustin@virtualroadside.com", + url="https://github.com/robotpy/robotpy-rev", + description="RobotPy bindings for REV third party libraries", + long_description=long_description, + packages=find_packages(), + ext_modules=ext_modules, + install_requires=install_requires, + cmdclass=cmdclass, + zip_safe=False, + entry_points={ + "robotpylib": ["info = rev._impl.info:Info"], + # "robotpysim": ["rev = rev._impl.sim_ui:RevUI"], + }, +)