From 44b9cc13980d11cfd46afec1e0a3c0f11302a02e Mon Sep 17 00:00:00 2001 From: PJ Reiniger Date: Fri, 24 Oct 2025 01:28:04 -0400 Subject: [PATCH] [robotpy] Mirror most other subprojects (#8208) GitOrigin-RevId: ac60fd3cf4a24023184376687da28373d14b781a This mirrors the robotpy files for the following projects: - apriltag - datalog - hal - ntcore - romiVendordep - wpilibc - wpimath - xrpVendordep This excludes cscore and the halsim wrappers for at this time. NOTE: This does not hook these projects up to the build system, just simply mirrors the files. The building will take place in a follow up PR to make it easier to review the changes necessary to build. --- .styleguide | 7 + apriltag/src/main/python/README.md | 4 + .../src/main/python/native-pyproject.toml | 41 + apriltag/src/main/python/pyproject.toml | 74 + .../main/python/robotpy_apriltag/__init__.py | 22 + .../src/main/python/robotpy_apriltag/py.typed | 0 .../main/python/robotpy_apriltag/src/main.cpp | 4 + .../src/main/python/semiwrap/AprilTag.yml | 16 + .../python/semiwrap/AprilTagDetection.yml | 43 + .../main/python/semiwrap/AprilTagDetector.yml | 99 + .../python/semiwrap/AprilTagFieldLayout.yml | 29 + .../main/python/semiwrap/AprilTagFields.yml | 2 + .../python/semiwrap/AprilTagPoseEstimate.yml | 9 + .../python/semiwrap/AprilTagPoseEstimator.yml | 36 + apriltag/src/test/python/tag1_640_480.jpg | Bin 0 -> 26688 bytes .../src/test/python/tag2_16h5_straight.png | Bin 0 -> 10027 bytes apriltag/src/test/python/tag2_45deg_X.png | Bin 0 -> 12625 bytes apriltag/src/test/python/tag2_45deg_y.png | Bin 0 -> 11275 bytes apriltag/src/test/python/test_detection.py | 119 ++ datalog/.styleguide | 5 + datalog/src/main/python/native-pyproject.toml | 40 + datalog/src/main/python/pyproject.toml | 63 + datalog/src/main/python/semiwrap/DataLog.yml | 431 +++++ .../semiwrap/DataLogBackgroundWriter.yml | 16 + .../main/python/semiwrap/DataLogReader.yml | 332 ++++ .../main/python/semiwrap/DataLogWriter.yml | 28 + datalog/src/main/python/src/main.cpp | 6 + datalog/src/main/python/wpilog/__init__.py | 52 + datalog/src/main/python/wpilog/py.typed | 0 datalog/src/test/python/test_wpilog.py | 5 + hal/.styleguide | 3 + hal/src/main/python/README.md | 7 + hal/src/main/python/hal/__init__.py | 12 + hal/src/main/python/hal/_initialize.py | 9 + hal/src/main/python/hal/exceptions.py | 2 + hal/src/main/python/hal/py.typed | 0 .../main/python/hal/simulation/__init__.py | 4 + hal/src/main/python/hal/simulation/main.cpp | 30 + .../python/hal/simulation/resethandles.cpp | 29 + hal/src/main/python/hal/simulation/sim_cb.h | 33 + .../main/python/hal/simulation/sim_value_cb.h | 37 + hal/src/main/python/hal/src/ds_types_fmt.h | 16 + hal/src/main/python/hal/src/hal.cpp | 110 ++ hal/src/main/python/native-pyproject.toml | 41 + hal/src/main/python/pyproject.toml | 169 ++ .../main/python/semiwrap/AddressableLED.yml | 9 + .../python/semiwrap/AddressableLEDTypes.yml | 14 + hal/src/main/python/semiwrap/AnalogInput.yml | 23 + hal/src/main/python/semiwrap/CAN.yml | 16 + hal/src/main/python/semiwrap/CANAPI.yml | 13 + hal/src/main/python/semiwrap/CANAPITypes.yml | 19 + hal/src/main/python/semiwrap/CTREPCM.yml | 26 + hal/src/main/python/semiwrap/Constants.yml | 5 + hal/src/main/python/semiwrap/Counter.yml | 12 + hal/src/main/python/semiwrap/DIO.yml | 22 + .../main/python/semiwrap/DriverStation.yml | 42 + .../python/semiwrap/DriverStationTypes.yml | 53 + hal/src/main/python/semiwrap/DutyCycle.yml | 11 + hal/src/main/python/semiwrap/Encoder.yml | 31 + hal/src/main/python/semiwrap/Extensions.yml | 16 + hal/src/main/python/semiwrap/HALBase.yml | 47 + .../main/python/semiwrap/HandlesInternal.yml | 15 + hal/src/main/python/semiwrap/I2C.yml | 16 + hal/src/main/python/semiwrap/I2CTypes.yml | 6 + hal/src/main/python/semiwrap/Main.yml | 9 + hal/src/main/python/semiwrap/Notifier.yml | 12 + hal/src/main/python/semiwrap/PWM.yml | 11 + hal/src/main/python/semiwrap/Ports.yml | 22 + hal/src/main/python/semiwrap/Power.yml | 14 + .../python/semiwrap/PowerDistribution.yml | 105 ++ hal/src/main/python/semiwrap/REVPH.yml | 82 + hal/src/main/python/semiwrap/SerialPort.yml | 30 + hal/src/main/python/semiwrap/SimDevice.yml | 376 ++++ hal/src/main/python/semiwrap/Threads.yml | 12 + .../main/python/semiwrap/UsageReporting.yml | 10 + .../simulation/AddressableLEDData.yml | 31 + .../semiwrap/simulation/AnalogInData.yml | 28 + .../semiwrap/simulation/CTREPCMData.yml | 47 + .../python/semiwrap/simulation/DIOData.yml | 33 + .../semiwrap/simulation/DigitalPWMData.yml | 23 + .../semiwrap/simulation/DriverStationData.yml | 113 ++ .../semiwrap/simulation/DutyCycleData.yml | 23 + .../semiwrap/simulation/EncoderData.yml | 60 + .../python/semiwrap/simulation/MockHooks.yml | 59 + .../semiwrap/simulation/NotifierData.yml | 14 + .../python/semiwrap/simulation/PWMData.yml | 25 + .../simulation/PowerDistributionData.yml | 29 + .../python/semiwrap/simulation/REVPHData.yml | 41 + .../main/python/semiwrap/simulation/Reset.yml | 5 + .../semiwrap/simulation/RoboRioData.yml | 97 + .../semiwrap/simulation/SimDeviceData.yml | 92 + hal/src/test/python/test_hal.py | 47 + hal/src/test/python/test_hal_simulation.py | 41 + ntcore/.styleguide | 3 + ntcore/src/main/python/README.md | 4 + ntcore/src/main/python/devtools/gen-pubsub.py | 64 + ntcore/src/main/python/native-pyproject.toml | 43 + ntcore/src/main/python/ntcore/__init__.py | 195 ++ ntcore/src/main/python/ntcore/_logutil.py | 123 ++ .../src/main/python/ntcore/meta/__init__.py | 28 + ntcore/src/main/python/ntcore/py.typed | 0 .../python/ntcore/src/NetworkTable.cpp.inl | 56 + .../ntcore/src/NetworkTableEntry.cpp.inl | 48 + .../main/python/ntcore/src/nt_instance.cpp | 56 + .../src/main/python/ntcore/src/nt_instance.h | 16 + .../main/python/ntcore/src/nt_type_caster.h | 39 + ntcore/src/main/python/ntcore/src/ntcore.cpp | 14 + .../src/main/python/ntcore/src/py2value.cpp | 180 ++ ntcore/src/main/python/ntcore/src/py2value.h | 25 + ntcore/src/main/python/ntcore/src/pyentry.cpp | 141 ++ ntcore/src/main/python/ntcore/src/pyentry.h | 21 + ntcore/src/main/python/ntcore/types.py | 13 + ntcore/src/main/python/ntcore/util.py | 224 +++ ntcore/src/main/python/pyproject.toml | 96 + .../python/semiwrap/BooleanArrayTopic.yml | 115 ++ .../src/main/python/semiwrap/BooleanTopic.yml | 107 ++ .../main/python/semiwrap/DoubleArrayTopic.yml | 115 ++ .../src/main/python/semiwrap/DoubleTopic.yml | 107 ++ .../main/python/semiwrap/FloatArrayTopic.yml | 115 ++ .../src/main/python/semiwrap/FloatTopic.yml | 107 ++ .../src/main/python/semiwrap/GenericEntry.yml | 86 + .../python/semiwrap/IntegerArrayTopic.yml | 115 ++ .../src/main/python/semiwrap/IntegerTopic.yml | 107 ++ .../main/python/semiwrap/MultiSubscriber.yml | 22 + .../src/main/python/semiwrap/NTSendable.yml | 20 + .../python/semiwrap/NTSendableBuilder.yml | 15 + .../src/main/python/semiwrap/NetworkTable.yml | 178 ++ .../python/semiwrap/NetworkTableEntry.yml | 97 + .../python/semiwrap/NetworkTableInstance.yml | 181 ++ .../python/semiwrap/NetworkTableListener.yml | 63 + .../main/python/semiwrap/NetworkTableType.yml | 2 + .../python/semiwrap/NetworkTableValue.yml | 184 ++ ntcore/src/main/python/semiwrap/RawTopic.yml | 114 ++ .../main/python/semiwrap/StringArrayTopic.yml | 107 ++ .../src/main/python/semiwrap/StringTopic.yml | 115 ++ .../main/python/semiwrap/StructArrayTopic.yml | 167 ++ .../src/main/python/semiwrap/StructTopic.yml | 149 ++ ntcore/src/main/python/semiwrap/Topic.yml | 89 + .../src/main/python/semiwrap/ntcore_cpp.yml | 317 ++++ .../main/python/semiwrap/ntcore_cpp_types.yml | 82 + ntcore/src/test/python/conftest.py | 165 ++ ntcore/src/test/python/test_api.py | 155 ++ ntcore/src/test/python/test_entry.py | 71 + ntcore/src/test/python/test_network_table.py | 176 ++ ntcore/src/test/python/test_struct_topic.py | 55 + ntcore/src/test/python/test_util.py | 153 ++ ntcore/src/test/python/test_value.py | 196 ++ romiVendordep/.styleguide | 5 + romiVendordep/src/main/python/README.md | 4 + .../src/main/python/native-pyproject.toml | 39 + romiVendordep/src/main/python/pyproject.toml | 55 + .../src/main/python/romi/__init__.py | 8 + romiVendordep/src/main/python/romi/py.typed | 0 .../src/main/python/romi/src/main.cpp | 6 + .../src/main/python/semiwrap/OnBoardIO.yml | 15 + .../src/main/python/semiwrap/RomiGyro.yml | 13 + .../src/main/python/semiwrap/RomiMotor.yml | 4 + romiVendordep/src/test/python/test_romi.py | 5 + wpilibc/src/main/python/LICENSE.txt | 56 + wpilibc/src/main/python/README.md | 33 + wpilibc/src/main/python/README.rst | 12 + wpilibc/src/main/python/native-pyproject.toml | 47 + wpilibc/src/main/python/pyproject.toml | 284 +++ .../src/main/python/semiwrap/ADXL345_I2C.yml | 30 + .../main/python/semiwrap/AddressableLED.yml | 44 + wpilibc/src/main/python/semiwrap/Alert.yml | 30 + .../python/semiwrap/AnalogAccelerometer.yml | 18 + .../main/python/semiwrap/AnalogEncoder.yml | 28 + .../src/main/python/semiwrap/AnalogGyro.yml | 29 + .../src/main/python/semiwrap/AnalogInput.yml | 30 + .../python/semiwrap/AnalogPotentiometer.yml | 16 + wpilibc/src/main/python/semiwrap/CAN.yml | 20 + wpilibc/src/main/python/semiwrap/Color.yml | 191 ++ .../src/main/python/semiwrap/Color8Bit.yml | 46 + .../src/main/python/semiwrap/Compressor.yml | 26 + .../python/semiwrap/CompressorConfigType.yml | 2 + .../src/main/python/semiwrap/CounterBase.yml | 15 + wpilibc/src/main/python/semiwrap/DMC60.yml | 7 + .../main/python/semiwrap/DSControlWord.yml | 24 + .../main/python/semiwrap/DataLogManager.yml | 14 + .../src/main/python/semiwrap/DigitalInput.yml | 13 + .../main/python/semiwrap/DigitalOutput.yml | 21 + .../main/python/semiwrap/DoubleSolenoid.yml | 22 + .../main/python/semiwrap/DriverStation.yml | 68 + .../semiwrap/DriverStationModeThread.yml | 9 + .../src/main/python/semiwrap/DutyCycle.yml | 26 + .../main/python/semiwrap/DutyCycleEncoder.yml | 38 + wpilibc/src/main/python/semiwrap/Encoder.yml | 43 + wpilibc/src/main/python/semiwrap/Errors.yml | 13 + wpilibc/src/main/python/semiwrap/Field2d.yml | 19 + .../main/python/semiwrap/FieldObject2d.yml | 25 + .../src/main/python/semiwrap/Filesystem.yml | 7 + .../src/main/python/semiwrap/GenericHID.yml | 54 + wpilibc/src/main/python/semiwrap/I2C.yml | 26 + .../python/semiwrap/IterativeRobotBase.yml | 66 + wpilibc/src/main/python/semiwrap/Jaguar.yml | 7 + wpilibc/src/main/python/semiwrap/Joystick.yml | 46 + wpilibc/src/main/python/semiwrap/Koors40.yml | 4 + .../src/main/python/semiwrap/LEDPattern.yml | 44 + .../src/main/python/semiwrap/Mechanism2d.yml | 13 + .../python/semiwrap/MechanismLigament2d.yml | 14 + .../python/semiwrap/MechanismObject2d.yml | 31 + .../main/python/semiwrap/MechanismRoot2d.yml | 30 + .../main/python/semiwrap/MotorController.yml | 13 + .../python/semiwrap/MotorControllerGroup.yml | 43 + .../src/main/python/semiwrap/MotorSafety.yml | 17 + wpilibc/src/main/python/semiwrap/Notifier.yml | 21 + .../src/main/python/semiwrap/OnboardIMU.yml | 20 + .../main/python/semiwrap/PS4Controller.yml | 100 + .../main/python/semiwrap/PS5Controller.yml | 99 + wpilibc/src/main/python/semiwrap/PWM.yml | 28 + .../python/semiwrap/PWMMotorController.yml | 33 + .../src/main/python/semiwrap/PWMSparkFlex.yml | 4 + .../src/main/python/semiwrap/PWMSparkMax.yml | 7 + .../src/main/python/semiwrap/PWMTalonFX.yml | 7 + .../src/main/python/semiwrap/PWMTalonSRX.yml | 7 + wpilibc/src/main/python/semiwrap/PWMVenom.yml | 7 + .../src/main/python/semiwrap/PWMVictorSPX.yml | 7 + .../src/main/python/semiwrap/PneumaticHub.yml | 91 + .../main/python/semiwrap/PneumaticsBase.yml | 37 + .../semiwrap/PneumaticsControlModule.yml | 47 + .../python/semiwrap/PneumaticsModuleType.yml | 2 + .../python/semiwrap/PowerDistribution.yml | 107 ++ .../src/main/python/semiwrap/Preferences.yml | 25 + .../src/main/python/semiwrap/RobotBase.yml | 62 + .../main/python/semiwrap/RobotController.yml | 36 + .../src/main/python/semiwrap/RobotState.yml | 10 + .../src/main/python/semiwrap/RuntimeType.yml | 2 + wpilibc/src/main/python/semiwrap/SD540.yml | 7 + .../python/semiwrap/SendableBuilderImpl.yml | 49 + .../main/python/semiwrap/SendableChooser.yml | 42 + .../python/semiwrap/SendableChooserBase.yml | 25 + .../src/main/python/semiwrap/SensorUtil.yml | 12 + .../src/main/python/semiwrap/SerialPort.yml | 50 + wpilibc/src/main/python/semiwrap/Servo.yml | 15 + wpilibc/src/main/python/semiwrap/SharpIR.yml | 16 + .../main/python/semiwrap/SmartDashboard.yml | 171 ++ wpilibc/src/main/python/semiwrap/Solenoid.yml | 20 + wpilibc/src/main/python/semiwrap/Spark.yml | 7 + .../src/main/python/semiwrap/SparkMini.yml | 4 + .../main/python/semiwrap/StadiaController.yml | 103 ++ .../main/python/semiwrap/SysIdRoutineLog.yml | 35 + .../src/main/python/semiwrap/SystemServer.yml | 4 + wpilibc/src/main/python/semiwrap/Talon.yml | 7 + wpilibc/src/main/python/semiwrap/Threads.yml | 7 + .../src/main/python/semiwrap/TimedRobot.yml | 17 + wpilibc/src/main/python/semiwrap/Timer.yml | 18 + .../main/python/semiwrap/TimesliceRobot.yml | 5 + wpilibc/src/main/python/semiwrap/Tracer.yml | 31 + wpilibc/src/main/python/semiwrap/Victor.yml | 7 + wpilibc/src/main/python/semiwrap/VictorSP.yml | 7 + wpilibc/src/main/python/semiwrap/Watchdog.yml | 22 + .../main/python/semiwrap/XboxController.yml | 92 + .../semiwrap/counter/EdgeConfiguration.yml | 2 + .../python/semiwrap/counter/Tachometer.yml | 19 + .../python/semiwrap/counter/UpDownCounter.yml | 13 + .../semiwrap/drive/DifferentialDrive.yml | 109 ++ .../python/semiwrap/drive/MecanumDrive.yml | 38 + .../python/semiwrap/drive/RobotDriveBase.yml | 21 + .../python/semiwrap/event/BooleanEvent.yml | 21 + .../main/python/semiwrap/event/EventLoop.yml | 13 + .../semiwrap/event/NetworkBooleanEvent.yml | 24 + .../python/semiwrap/simulation/ADXL345Sim.yml | 12 + .../semiwrap/simulation/AddressableLEDSim.yml | 26 + .../semiwrap/simulation/AnalogEncoderSim.yml | 11 + .../semiwrap/simulation/AnalogInputSim.yml | 25 + .../python/semiwrap/simulation/BatterySim.yml | 13 + .../python/semiwrap/simulation/CTREPCMSim.yml | 36 + .../semiwrap/simulation/CallbackStore.yml | 26 + .../python/semiwrap/simulation/DCMotorSim.yml | 35 + .../python/semiwrap/simulation/DIOSim.yml | 30 + .../simulation/DifferentialDrivetrainSim.yml | 96 + .../semiwrap/simulation/DigitalPWMSim.yml | 23 + .../semiwrap/simulation/DoubleSolenoidSim.yml | 13 + .../semiwrap/simulation/DriverStationSim.yml | 51 + .../simulation/DutyCycleEncoderSim.yml | 14 + .../semiwrap/simulation/DutyCycleSim.yml | 22 + .../semiwrap/simulation/ElevatorSim.yml | 42 + .../python/semiwrap/simulation/EncoderSim.yml | 45 + .../semiwrap/simulation/FlywheelSim.yml | 27 + .../semiwrap/simulation/GenericHIDSim.yml | 23 + .../semiwrap/simulation/JoystickSim.yml | 18 + .../semiwrap/simulation/LinearSystemSim.yml | 75 + .../semiwrap/simulation/PS4ControllerSim.yml | 33 + .../semiwrap/simulation/PS5ControllerSim.yml | 33 + .../simulation/PWMMotorControllerSim.yml | 8 + .../python/semiwrap/simulation/PWMSim.yml | 23 + .../semiwrap/simulation/PneumaticsBaseSim.yml | 32 + .../simulation/PowerDistributionSim.yml | 27 + .../python/semiwrap/simulation/REVPHSim.yml | 36 + .../python/semiwrap/simulation/RoboRioSim.yml | 34 + .../simulation/SendableChooserSim.yml | 8 + .../python/semiwrap/simulation/ServoSim.yml | 9 + .../python/semiwrap/simulation/SharpIRSim.yml | 8 + .../semiwrap/simulation/SimDeviceSim.yml | 85 + .../python/semiwrap/simulation/SimHooks.yml | 11 + .../simulation/SingleJointedArmSim.yml | 38 + .../semiwrap/simulation/SolenoidSim.yml | 16 + .../simulation/StadiaControllerSim.yml | 30 + .../semiwrap/simulation/XboxControllerSim.yml | 33 + wpilibc/src/main/python/wpilib/__init__.py | 210 +++ .../src/main/python/wpilib/_impl/__init__.py | 0 wpilibc/src/main/python/wpilib/_impl/main.py | 18 + .../main/python/wpilib/_impl/report_error.py | 91 + wpilibc/src/main/python/wpilib/_impl/start.py | 250 +++ wpilibc/src/main/python/wpilib/_impl/utils.py | 231 +++ .../src/main/python/wpilib/cameraserver.py | 94 + .../main/python/wpilib/counter/__init__.py | 6 + .../main/python/wpilib/counter/counter.cpp | 7 + wpilibc/src/main/python/wpilib/deployinfo.py | 33 + .../src/main/python/wpilib/drive/__init__.py | 8 + .../src/main/python/wpilib/drive/drive.cpp | 7 + .../src/main/python/wpilib/event/__init__.py | 6 + .../src/main/python/wpilib/event/event.cpp | 7 + .../main/python/wpilib/interfaces/__init__.py | 4 + wpilibc/src/main/python/wpilib/py.typed | 0 .../main/python/wpilib/simulation/__init__.py | 117 ++ .../python/wpilib/simulation/simulation.cpp | 36 + wpilibc/src/main/python/wpilib/src/main.cpp | 4 + .../python/wpilib/src/rpy/ControlWord.cpp | 18 + .../main/python/wpilib/src/rpy/ControlWord.h | 7 + .../main/python/wpilib/src/rpy/Filesystem.h | 33 + .../main/python/wpilib/src/rpy/Filesystem.inc | 54 + .../wpilib/src/rpy/MotorControllerGroup.cpp | 62 + .../wpilib/src/rpy/MotorControllerGroup.h | 46 + .../main/python/wpilib/src/rpy/Notifier.cpp | 189 ++ .../src/main/python/wpilib/src/rpy/Notifier.h | 146 ++ .../wpilib/src/rpy/SmartDashboardData.cpp | 43 + .../wpilib/src/rpy/SmartDashboardData.h | 17 + .../src/main/python/wpilib/sysid/__init__.py | 4 + wpilibc/src/test/python/conftest.py | 31 + wpilibc/src/test/python/test_alert.py | 223 +++ .../src/test/python/test_datalogmanager.py | 13 + wpilibc/src/test/python/test_joystick.py | 144 ++ wpilibc/src/test/python/test_mechanism2d.py | 11 + wpilibc/src/test/python/test_notifier.py | 62 + .../src/test/python/test_sendable_chooser.py | 55 + wpilibc/src/test/python/test_wpilib.py | 43 + wpilibc/src/test/python/test_wpilib_drive.py | 5 + .../src/test/python/test_wpilib_interfaces.py | 5 + .../src/test/python/test_wpilib_simulation.py | 5 + wpimath/.styleguide | 3 + wpimath/src/main/python/README.md | 7 + wpimath/src/main/python/native-pyproject.toml | 39 + wpimath/src/main/python/pyproject.toml | 1638 +++++++++++++++++ .../python/semiwrap/ComputerVisionUtil.yml | 2 + wpimath/src/main/python/semiwrap/MathUtil.yml | 47 + .../semiwrap/controls/ArmFeedforward.yml | 42 + .../semiwrap/controls/BangBangController.yml | 21 + .../CentripetalAccelerationConstraint.yml | 17 + ...ControlAffinePlantInversionFeedforward.yml | 46 + .../main/python/semiwrap/controls/DCMotor.yml | 46 + .../DifferentialDriveAccelerationLimiter.yml | 12 + .../controls/DifferentialDriveFeedforward.yml | 41 + .../DifferentialDriveKinematicsConstraint.yml | 19 + .../DifferentialDrivePoseEstimator.yml | 48 + .../DifferentialDrivePoseEstimator3d.yml | 15 + .../DifferentialDriveVoltageConstraint.yml | 11 + .../DifferentialDriveWheelVoltages.yml | 26 + .../semiwrap/controls/ElevatorFeedforward.yml | 39 + .../controls/EllipticalRegionConstraint.yml | 34 + .../semiwrap/controls/ExponentialProfile.yml | 63 + .../controls/ExtendedKalmanFilter.yml | 66 + .../controls/HolonomicDriveController.yml | 23 + .../controls/ImplicitModelFollower.yml | 48 + .../python/semiwrap/controls/KalmanFilter.yml | 62 + .../LTVDifferentialDriveController.yml | 14 + .../controls/LTVUnicycleController.yml | 20 + .../LinearPlantInversionFeedforward.yml | 52 + .../controls/LinearQuadraticRegulator.yml | 66 + .../python/semiwrap/controls/LinearSystem.yml | 106 ++ .../semiwrap/controls/LinearSystemId.yml | 62 + .../semiwrap/controls/LinearSystemLoop.yml | 79 + .../controls/MaxVelocityConstraint.yml | 18 + .../MecanumDriveKinematicsConstraint.yml | 19 + .../controls/MecanumDrivePoseEstimator.yml | 38 + .../controls/MecanumDrivePoseEstimator3d.yml | 12 + .../semiwrap/controls/PIDController.yml | 45 + .../semiwrap/controls/PoseEstimator.yml | 63 + .../semiwrap/controls/PoseEstimator3d.yml | 63 + .../controls/ProfiledPIDController.yml | 80 + .../controls/RectangularRegionConstraint.yml | 25 + .../controls/SimpleMotorFeedforward.yml | 57 + .../semiwrap/controls/SimulatedAnnealing.yml | 19 + .../SwerveDriveKinematicsConstraint.yml | 38 + .../controls/SwerveDrivePoseEstimator.yml | 53 + .../controls/SwerveDrivePoseEstimator3d.yml | 36 + .../python/semiwrap/controls/Trajectory.yml | 71 + .../semiwrap/controls/TrajectoryConfig.yml | 74 + .../controls/TrajectoryConstraint.yml | 38 + .../semiwrap/controls/TrajectoryGenerator.yml | 25 + .../controls/TrajectoryParameterizer.yml | 10 + .../semiwrap/controls/TrapezoidProfile.yml | 106 ++ .../semiwrap/controls/TravelingSalesman.yml | 15 + .../main/python/semiwrap/filter/Debouncer.yml | 14 + .../python/semiwrap/filter/LinearFilter.yml | 29 + .../python/semiwrap/filter/MedianFilter.yml | 15 + .../semiwrap/filter/SlewRateLimiter.yml | 21 + .../semiwrap/geometry/CoordinateAxis.yml | 10 + .../semiwrap/geometry/CoordinateSystem.yml | 13 + .../python/semiwrap/geometry/Ellipse2d.yml | 46 + .../main/python/semiwrap/geometry/Pose2d.yml | 68 + .../main/python/semiwrap/geometry/Pose3d.yml | 71 + .../python/semiwrap/geometry/Quaternion.yml | 50 + .../python/semiwrap/geometry/Rectangle2d.yml | 45 + .../python/semiwrap/geometry/Rotation2d.yml | 55 + .../python/semiwrap/geometry/Rotation3d.yml | 71 + .../python/semiwrap/geometry/Transform2d.yml | 53 + .../python/semiwrap/geometry/Transform3d.yml | 49 + .../semiwrap/geometry/Translation2d.yml | 78 + .../semiwrap/geometry/Translation3d.yml | 83 + .../main/python/semiwrap/geometry/Twist2d.yml | 55 + .../main/python/semiwrap/geometry/Twist3d.yml | 90 + .../TimeInterpolatableBuffer.yml | 50 + .../semiwrap/kinematics/ChassisSpeeds.yml | 84 + .../DifferentialDriveKinematics.yml | 19 + .../kinematics/DifferentialDriveOdometry.yml | 7 + .../DifferentialDriveOdometry3d.yml | 7 + .../DifferentialDriveWheelPositions.yml | 8 + .../DifferentialDriveWheelSpeeds.yml | 53 + .../python/semiwrap/kinematics/Kinematics.yml | 53 + .../kinematics/MecanumDriveKinematics.yml | 50 + .../kinematics/MecanumDriveOdometry.yml | 5 + .../kinematics/MecanumDriveOdometry3d.yml | 5 + .../kinematics/MecanumDriveWheelPositions.yml | 10 + .../kinematics/MecanumDriveWheelSpeeds.yml | 81 + .../python/semiwrap/kinematics/Odometry.yml | 52 + .../python/semiwrap/kinematics/Odometry3d.yml | 53 + .../kinematics/SwerveDriveKinematics.yml | 108 ++ .../kinematics/SwerveDriveOdometry.yml | 24 + .../kinematics/SwerveDriveOdometry3d.yml | 25 + .../kinematics/SwerveModulePosition.yml | 34 + .../semiwrap/kinematics/SwerveModuleState.yml | 41 + .../semiwrap/spline/CubicHermiteSpline.yml | 10 + .../semiwrap/spline/QuinticHermiteSpline.yml | 9 + .../main/python/semiwrap/spline/Spline.yml | 49 + .../python/semiwrap/spline/SplineHelper.yml | 8 + .../semiwrap/spline/SplineParameterizer.yml | 13 + wpimath/src/main/python/tools/create_units.py | 92 + wpimath/src/main/python/wpimath/__init__.py | 25 + .../main/python/wpimath/_controls/__init__.py | 1 + .../python/wpimath/_controls/controls.cpp | 7 + .../src/main/python/wpimath/_impl/__init__.py | 1 + .../_impl/src/PyTrajectoryConstraint.h | 54 + .../type_casters/_units_base_type_caster.h | 55 + .../_impl/src/type_casters/frc_eigen.h | 4 + .../units_acceleration_type_caster.h | 34 + .../type_casters/units_angle_type_caster.h | 98 + .../units_angular_acceleration_type_caster.h | 34 + .../units_angular_velocity_type_caster.h | 50 + .../src/type_casters/units_area_type_caster.h | 66 + .../units_capacitance_type_caster.h | 50 + .../type_casters/units_charge_type_caster.h | 90 + .../type_casters/units_compound_type_caster.h | 96 + .../units_concentration_type_caster.h | 42 + .../units_conductance_type_caster.h | 50 + .../type_casters/units_current_type_caster.h | 50 + .../units_data_transfer_rate_type_caster.h | 26 + .../src/type_casters/units_data_type_caster.h | 26 + .../type_casters/units_density_type_caster.h | 90 + .../type_casters/units_energy_type_caster.h | 146 ++ .../type_casters/units_force_type_caster.h | 82 + .../units_frequency_type_caster.h | 50 + .../units_illuminance_type_caster.h | 74 + .../units_impedance_type_caster.h | 50 + .../units_inductance_type_caster.h | 50 + .../type_casters/units_length_type_caster.h | 194 ++ .../units_luminous_flux_type_caster.h | 50 + .../units_luminous_intensity_type_caster.h | 50 + ...nits_magnetic_field_strength_type_caster.h | 58 + .../units_magnetic_flux_type_caster.h | 58 + .../src/type_casters/units_mass_type_caster.h | 114 ++ .../src/type_casters/units_misc_type_caster.h | 27 + .../units_moment_of_inertia_type_caster.h | 18 + .../type_casters/units_power_type_caster.h | 58 + .../type_casters/units_pressure_type_caster.h | 90 + .../units_radiation_type_caster.h | 154 ++ .../units_solid_angle_type_caster.h | 66 + .../units_substance_type_caster.h | 18 + .../units_temperature_type_caster.h | 50 + .../src/type_casters/units_time_type_caster.h | 106 ++ .../type_casters/units_torque_type_caster.h | 50 + .../type_casters/units_velocity_type_caster.h | 50 + .../type_casters/units_voltage_type_caster.h | 66 + .../type_casters/units_volume_type_caster.h | 274 +++ .../python/wpimath/controller/__init__.py | 60 + .../main/python/wpimath/estimator/__init__.py | 72 + .../main/python/wpimath/filter/__init__.py | 6 + .../src/main/python/wpimath/filter/filter.cpp | 7 + .../main/python/wpimath/geometry/__init__.py | 40 + .../main/python/wpimath/geometry/geometry.cpp | 7 + .../geometry/include/rpy/geometryToString.h | 99 + .../python/wpimath/interpolation/__init__.py | 22 + .../wpimath/interpolation/interpolation.cpp | 7 + .../python/wpimath/kinematics/__init__.py | 70 + .../python/wpimath/kinematics/kinematics.cpp | 7 + .../python/wpimath/optimization/__init__.py | 4 + .../src/main/python/wpimath/path/__init__.py | 4 + wpimath/src/main/python/wpimath/py.typed | 0 .../main/python/wpimath/spline/__init__.py | 20 + .../src/main/python/wpimath/spline/spline.cpp | 7 + .../src/main/python/wpimath/src/wpimath.cpp | 4 + .../main/python/wpimath/system/__init__.py | 40 + .../python/wpimath/system/plant/__init__.py | 3 + .../python/wpimath/trajectory/__init__.py | 20 + .../wpimath/trajectory/constraint/__init__.py | 30 + wpimath/src/main/python/wpimath/units.py | 549 ++++++ wpimath/src/test/python/conftest.py | 1 + wpimath/src/test/python/cpp/pyproject.toml | 27 + .../src/test/python/cpp/semiwrap/module.yml | 19 + .../test/python/cpp/wpimath_test/__init__.py | 8 + .../python/cpp/wpimath_test/include/module.h | 24 + .../python/cpp/wpimath_test/src/module.cpp | 53 + .../test/python/geometry/test_rotation2d.py | 82 + .../python/kinematics/test_chassis_speeds.py | 72 + .../src/test/python/kinematics/test_swerve.py | 75 + wpimath/src/test/python/test_controller.py | 5 + wpimath/src/test/python/test_estimator.py | 5 + wpimath/src/test/python/test_interpolation.py | 50 + .../test_optimization_simulated_annealing.py | 42 + wpimath/src/test/python/test_spline.py | 5 + wpimath/src/test/python/test_system.py | 6 + wpimath/src/test/python/test_trajectory.py | 145 ++ .../test_trajectory_exponential_profile.py | 319 ++++ .../src/test/python/test_trapezoid_profile.py | 24 + wpimath/src/test/python/test_units.py | 71 + .../trajectory/test_trapezoidal_profile.py | 176 ++ wpinet/src/test/python/run_tests.py | 12 - wpiutil/src/test/python/run_tests.py | 26 - xrpVendordep/src/main/python/README.md | 4 + .../src/main/python/native-pyproject.toml | 39 + xrpVendordep/src/main/python/pyproject.toml | 66 + .../src/main/python/semiwrap/XRPGyro.yml | 14 + .../src/main/python/semiwrap/XRPMotor.yml | 11 + .../src/main/python/semiwrap/XRPOnBoardIO.yml | 10 + .../main/python/semiwrap/XRPRangefinder.yml | 4 + .../python/semiwrap/XRPReflectanceSensor.yml | 5 + .../src/main/python/semiwrap/XRPServo.yml | 8 + xrpVendordep/src/main/python/xrp/__init__.py | 22 + .../src/main/python/xrp/extension/__init__.py | 3 + .../src/main/python/xrp/extension/main.py | 23 + xrpVendordep/src/main/python/xrp/py.typed | 0 xrpVendordep/src/main/python/xrp/src/main.cpp | 6 + xrpVendordep/src/main/python/xrp/xrp.pc | 9 + xrpVendordep/src/test/python/test_xrp.py | 5 + 545 files changed, 27293 insertions(+), 38 deletions(-) create mode 100644 apriltag/src/main/python/README.md create mode 100644 apriltag/src/main/python/native-pyproject.toml create mode 100644 apriltag/src/main/python/pyproject.toml create mode 100644 apriltag/src/main/python/robotpy_apriltag/__init__.py create mode 100644 apriltag/src/main/python/robotpy_apriltag/py.typed create mode 100644 apriltag/src/main/python/robotpy_apriltag/src/main.cpp create mode 100644 apriltag/src/main/python/semiwrap/AprilTag.yml create mode 100644 apriltag/src/main/python/semiwrap/AprilTagDetection.yml create mode 100644 apriltag/src/main/python/semiwrap/AprilTagDetector.yml create mode 100644 apriltag/src/main/python/semiwrap/AprilTagFieldLayout.yml create mode 100644 apriltag/src/main/python/semiwrap/AprilTagFields.yml create mode 100644 apriltag/src/main/python/semiwrap/AprilTagPoseEstimate.yml create mode 100644 apriltag/src/main/python/semiwrap/AprilTagPoseEstimator.yml create mode 100644 apriltag/src/test/python/tag1_640_480.jpg create mode 100644 apriltag/src/test/python/tag2_16h5_straight.png create mode 100644 apriltag/src/test/python/tag2_45deg_X.png create mode 100644 apriltag/src/test/python/tag2_45deg_y.png create mode 100644 apriltag/src/test/python/test_detection.py create mode 100644 datalog/src/main/python/native-pyproject.toml create mode 100644 datalog/src/main/python/pyproject.toml create mode 100644 datalog/src/main/python/semiwrap/DataLog.yml create mode 100644 datalog/src/main/python/semiwrap/DataLogBackgroundWriter.yml create mode 100644 datalog/src/main/python/semiwrap/DataLogReader.yml create mode 100644 datalog/src/main/python/semiwrap/DataLogWriter.yml create mode 100644 datalog/src/main/python/src/main.cpp create mode 100644 datalog/src/main/python/wpilog/__init__.py create mode 100644 datalog/src/main/python/wpilog/py.typed create mode 100644 datalog/src/test/python/test_wpilog.py create mode 100644 hal/src/main/python/README.md create mode 100644 hal/src/main/python/hal/__init__.py create mode 100644 hal/src/main/python/hal/_initialize.py create mode 100644 hal/src/main/python/hal/exceptions.py create mode 100644 hal/src/main/python/hal/py.typed create mode 100644 hal/src/main/python/hal/simulation/__init__.py create mode 100644 hal/src/main/python/hal/simulation/main.cpp create mode 100644 hal/src/main/python/hal/simulation/resethandles.cpp create mode 100644 hal/src/main/python/hal/simulation/sim_cb.h create mode 100644 hal/src/main/python/hal/simulation/sim_value_cb.h create mode 100644 hal/src/main/python/hal/src/ds_types_fmt.h create mode 100644 hal/src/main/python/hal/src/hal.cpp create mode 100644 hal/src/main/python/native-pyproject.toml create mode 100644 hal/src/main/python/pyproject.toml create mode 100644 hal/src/main/python/semiwrap/AddressableLED.yml create mode 100644 hal/src/main/python/semiwrap/AddressableLEDTypes.yml create mode 100644 hal/src/main/python/semiwrap/AnalogInput.yml create mode 100644 hal/src/main/python/semiwrap/CAN.yml create mode 100644 hal/src/main/python/semiwrap/CANAPI.yml create mode 100644 hal/src/main/python/semiwrap/CANAPITypes.yml create mode 100644 hal/src/main/python/semiwrap/CTREPCM.yml create mode 100644 hal/src/main/python/semiwrap/Constants.yml create mode 100644 hal/src/main/python/semiwrap/Counter.yml create mode 100644 hal/src/main/python/semiwrap/DIO.yml create mode 100644 hal/src/main/python/semiwrap/DriverStation.yml create mode 100644 hal/src/main/python/semiwrap/DriverStationTypes.yml create mode 100644 hal/src/main/python/semiwrap/DutyCycle.yml create mode 100644 hal/src/main/python/semiwrap/Encoder.yml create mode 100644 hal/src/main/python/semiwrap/Extensions.yml create mode 100644 hal/src/main/python/semiwrap/HALBase.yml create mode 100644 hal/src/main/python/semiwrap/HandlesInternal.yml create mode 100644 hal/src/main/python/semiwrap/I2C.yml create mode 100644 hal/src/main/python/semiwrap/I2CTypes.yml create mode 100644 hal/src/main/python/semiwrap/Main.yml create mode 100644 hal/src/main/python/semiwrap/Notifier.yml create mode 100644 hal/src/main/python/semiwrap/PWM.yml create mode 100644 hal/src/main/python/semiwrap/Ports.yml create mode 100644 hal/src/main/python/semiwrap/Power.yml create mode 100644 hal/src/main/python/semiwrap/PowerDistribution.yml create mode 100644 hal/src/main/python/semiwrap/REVPH.yml create mode 100644 hal/src/main/python/semiwrap/SerialPort.yml create mode 100644 hal/src/main/python/semiwrap/SimDevice.yml create mode 100644 hal/src/main/python/semiwrap/Threads.yml create mode 100644 hal/src/main/python/semiwrap/UsageReporting.yml create mode 100644 hal/src/main/python/semiwrap/simulation/AddressableLEDData.yml create mode 100644 hal/src/main/python/semiwrap/simulation/AnalogInData.yml create mode 100644 hal/src/main/python/semiwrap/simulation/CTREPCMData.yml create mode 100644 hal/src/main/python/semiwrap/simulation/DIOData.yml create mode 100644 hal/src/main/python/semiwrap/simulation/DigitalPWMData.yml create mode 100644 hal/src/main/python/semiwrap/simulation/DriverStationData.yml create mode 100644 hal/src/main/python/semiwrap/simulation/DutyCycleData.yml create mode 100644 hal/src/main/python/semiwrap/simulation/EncoderData.yml create mode 100644 hal/src/main/python/semiwrap/simulation/MockHooks.yml create mode 100644 hal/src/main/python/semiwrap/simulation/NotifierData.yml create mode 100644 hal/src/main/python/semiwrap/simulation/PWMData.yml create mode 100644 hal/src/main/python/semiwrap/simulation/PowerDistributionData.yml create mode 100644 hal/src/main/python/semiwrap/simulation/REVPHData.yml create mode 100644 hal/src/main/python/semiwrap/simulation/Reset.yml create mode 100644 hal/src/main/python/semiwrap/simulation/RoboRioData.yml create mode 100644 hal/src/main/python/semiwrap/simulation/SimDeviceData.yml create mode 100644 hal/src/test/python/test_hal.py create mode 100644 hal/src/test/python/test_hal_simulation.py create mode 100644 ntcore/src/main/python/README.md create mode 100755 ntcore/src/main/python/devtools/gen-pubsub.py create mode 100644 ntcore/src/main/python/native-pyproject.toml create mode 100644 ntcore/src/main/python/ntcore/__init__.py create mode 100644 ntcore/src/main/python/ntcore/_logutil.py create mode 100644 ntcore/src/main/python/ntcore/meta/__init__.py create mode 100644 ntcore/src/main/python/ntcore/py.typed create mode 100644 ntcore/src/main/python/ntcore/src/NetworkTable.cpp.inl create mode 100644 ntcore/src/main/python/ntcore/src/NetworkTableEntry.cpp.inl create mode 100644 ntcore/src/main/python/ntcore/src/nt_instance.cpp create mode 100644 ntcore/src/main/python/ntcore/src/nt_instance.h create mode 100644 ntcore/src/main/python/ntcore/src/nt_type_caster.h create mode 100644 ntcore/src/main/python/ntcore/src/ntcore.cpp create mode 100644 ntcore/src/main/python/ntcore/src/py2value.cpp create mode 100644 ntcore/src/main/python/ntcore/src/py2value.h create mode 100644 ntcore/src/main/python/ntcore/src/pyentry.cpp create mode 100644 ntcore/src/main/python/ntcore/src/pyentry.h create mode 100644 ntcore/src/main/python/ntcore/types.py create mode 100644 ntcore/src/main/python/ntcore/util.py create mode 100644 ntcore/src/main/python/pyproject.toml create mode 100644 ntcore/src/main/python/semiwrap/BooleanArrayTopic.yml create mode 100644 ntcore/src/main/python/semiwrap/BooleanTopic.yml create mode 100644 ntcore/src/main/python/semiwrap/DoubleArrayTopic.yml create mode 100644 ntcore/src/main/python/semiwrap/DoubleTopic.yml create mode 100644 ntcore/src/main/python/semiwrap/FloatArrayTopic.yml create mode 100644 ntcore/src/main/python/semiwrap/FloatTopic.yml create mode 100644 ntcore/src/main/python/semiwrap/GenericEntry.yml create mode 100644 ntcore/src/main/python/semiwrap/IntegerArrayTopic.yml create mode 100644 ntcore/src/main/python/semiwrap/IntegerTopic.yml create mode 100644 ntcore/src/main/python/semiwrap/MultiSubscriber.yml create mode 100644 ntcore/src/main/python/semiwrap/NTSendable.yml create mode 100644 ntcore/src/main/python/semiwrap/NTSendableBuilder.yml create mode 100644 ntcore/src/main/python/semiwrap/NetworkTable.yml create mode 100644 ntcore/src/main/python/semiwrap/NetworkTableEntry.yml create mode 100644 ntcore/src/main/python/semiwrap/NetworkTableInstance.yml create mode 100644 ntcore/src/main/python/semiwrap/NetworkTableListener.yml create mode 100644 ntcore/src/main/python/semiwrap/NetworkTableType.yml create mode 100644 ntcore/src/main/python/semiwrap/NetworkTableValue.yml create mode 100644 ntcore/src/main/python/semiwrap/RawTopic.yml create mode 100644 ntcore/src/main/python/semiwrap/StringArrayTopic.yml create mode 100644 ntcore/src/main/python/semiwrap/StringTopic.yml create mode 100644 ntcore/src/main/python/semiwrap/StructArrayTopic.yml create mode 100644 ntcore/src/main/python/semiwrap/StructTopic.yml create mode 100644 ntcore/src/main/python/semiwrap/Topic.yml create mode 100644 ntcore/src/main/python/semiwrap/ntcore_cpp.yml create mode 100644 ntcore/src/main/python/semiwrap/ntcore_cpp_types.yml create mode 100644 ntcore/src/test/python/conftest.py create mode 100644 ntcore/src/test/python/test_api.py create mode 100644 ntcore/src/test/python/test_entry.py create mode 100644 ntcore/src/test/python/test_network_table.py create mode 100644 ntcore/src/test/python/test_struct_topic.py create mode 100644 ntcore/src/test/python/test_util.py create mode 100644 ntcore/src/test/python/test_value.py create mode 100644 romiVendordep/src/main/python/README.md create mode 100644 romiVendordep/src/main/python/native-pyproject.toml create mode 100644 romiVendordep/src/main/python/pyproject.toml create mode 100644 romiVendordep/src/main/python/romi/__init__.py create mode 100644 romiVendordep/src/main/python/romi/py.typed create mode 100644 romiVendordep/src/main/python/romi/src/main.cpp create mode 100644 romiVendordep/src/main/python/semiwrap/OnBoardIO.yml create mode 100644 romiVendordep/src/main/python/semiwrap/RomiGyro.yml create mode 100644 romiVendordep/src/main/python/semiwrap/RomiMotor.yml create mode 100644 romiVendordep/src/test/python/test_romi.py create mode 100644 wpilibc/src/main/python/LICENSE.txt create mode 100644 wpilibc/src/main/python/README.md create mode 100644 wpilibc/src/main/python/README.rst create mode 100644 wpilibc/src/main/python/native-pyproject.toml create mode 100644 wpilibc/src/main/python/pyproject.toml create mode 100644 wpilibc/src/main/python/semiwrap/ADXL345_I2C.yml create mode 100644 wpilibc/src/main/python/semiwrap/AddressableLED.yml create mode 100644 wpilibc/src/main/python/semiwrap/Alert.yml create mode 100644 wpilibc/src/main/python/semiwrap/AnalogAccelerometer.yml create mode 100644 wpilibc/src/main/python/semiwrap/AnalogEncoder.yml create mode 100644 wpilibc/src/main/python/semiwrap/AnalogGyro.yml create mode 100644 wpilibc/src/main/python/semiwrap/AnalogInput.yml create mode 100644 wpilibc/src/main/python/semiwrap/AnalogPotentiometer.yml create mode 100644 wpilibc/src/main/python/semiwrap/CAN.yml create mode 100644 wpilibc/src/main/python/semiwrap/Color.yml create mode 100644 wpilibc/src/main/python/semiwrap/Color8Bit.yml create mode 100644 wpilibc/src/main/python/semiwrap/Compressor.yml create mode 100644 wpilibc/src/main/python/semiwrap/CompressorConfigType.yml create mode 100644 wpilibc/src/main/python/semiwrap/CounterBase.yml create mode 100644 wpilibc/src/main/python/semiwrap/DMC60.yml create mode 100644 wpilibc/src/main/python/semiwrap/DSControlWord.yml create mode 100644 wpilibc/src/main/python/semiwrap/DataLogManager.yml create mode 100644 wpilibc/src/main/python/semiwrap/DigitalInput.yml create mode 100644 wpilibc/src/main/python/semiwrap/DigitalOutput.yml create mode 100644 wpilibc/src/main/python/semiwrap/DoubleSolenoid.yml create mode 100644 wpilibc/src/main/python/semiwrap/DriverStation.yml create mode 100644 wpilibc/src/main/python/semiwrap/DriverStationModeThread.yml create mode 100644 wpilibc/src/main/python/semiwrap/DutyCycle.yml create mode 100644 wpilibc/src/main/python/semiwrap/DutyCycleEncoder.yml create mode 100644 wpilibc/src/main/python/semiwrap/Encoder.yml create mode 100644 wpilibc/src/main/python/semiwrap/Errors.yml create mode 100644 wpilibc/src/main/python/semiwrap/Field2d.yml create mode 100644 wpilibc/src/main/python/semiwrap/FieldObject2d.yml create mode 100644 wpilibc/src/main/python/semiwrap/Filesystem.yml create mode 100644 wpilibc/src/main/python/semiwrap/GenericHID.yml create mode 100644 wpilibc/src/main/python/semiwrap/I2C.yml create mode 100644 wpilibc/src/main/python/semiwrap/IterativeRobotBase.yml create mode 100644 wpilibc/src/main/python/semiwrap/Jaguar.yml create mode 100644 wpilibc/src/main/python/semiwrap/Joystick.yml create mode 100644 wpilibc/src/main/python/semiwrap/Koors40.yml create mode 100644 wpilibc/src/main/python/semiwrap/LEDPattern.yml create mode 100644 wpilibc/src/main/python/semiwrap/Mechanism2d.yml create mode 100644 wpilibc/src/main/python/semiwrap/MechanismLigament2d.yml create mode 100644 wpilibc/src/main/python/semiwrap/MechanismObject2d.yml create mode 100644 wpilibc/src/main/python/semiwrap/MechanismRoot2d.yml create mode 100644 wpilibc/src/main/python/semiwrap/MotorController.yml create mode 100644 wpilibc/src/main/python/semiwrap/MotorControllerGroup.yml create mode 100644 wpilibc/src/main/python/semiwrap/MotorSafety.yml create mode 100644 wpilibc/src/main/python/semiwrap/Notifier.yml create mode 100644 wpilibc/src/main/python/semiwrap/OnboardIMU.yml create mode 100644 wpilibc/src/main/python/semiwrap/PS4Controller.yml create mode 100644 wpilibc/src/main/python/semiwrap/PS5Controller.yml create mode 100644 wpilibc/src/main/python/semiwrap/PWM.yml create mode 100644 wpilibc/src/main/python/semiwrap/PWMMotorController.yml create mode 100644 wpilibc/src/main/python/semiwrap/PWMSparkFlex.yml create mode 100644 wpilibc/src/main/python/semiwrap/PWMSparkMax.yml create mode 100644 wpilibc/src/main/python/semiwrap/PWMTalonFX.yml create mode 100644 wpilibc/src/main/python/semiwrap/PWMTalonSRX.yml create mode 100644 wpilibc/src/main/python/semiwrap/PWMVenom.yml create mode 100644 wpilibc/src/main/python/semiwrap/PWMVictorSPX.yml create mode 100644 wpilibc/src/main/python/semiwrap/PneumaticHub.yml create mode 100644 wpilibc/src/main/python/semiwrap/PneumaticsBase.yml create mode 100644 wpilibc/src/main/python/semiwrap/PneumaticsControlModule.yml create mode 100644 wpilibc/src/main/python/semiwrap/PneumaticsModuleType.yml create mode 100644 wpilibc/src/main/python/semiwrap/PowerDistribution.yml create mode 100644 wpilibc/src/main/python/semiwrap/Preferences.yml create mode 100644 wpilibc/src/main/python/semiwrap/RobotBase.yml create mode 100644 wpilibc/src/main/python/semiwrap/RobotController.yml create mode 100644 wpilibc/src/main/python/semiwrap/RobotState.yml create mode 100644 wpilibc/src/main/python/semiwrap/RuntimeType.yml create mode 100644 wpilibc/src/main/python/semiwrap/SD540.yml create mode 100644 wpilibc/src/main/python/semiwrap/SendableBuilderImpl.yml create mode 100644 wpilibc/src/main/python/semiwrap/SendableChooser.yml create mode 100644 wpilibc/src/main/python/semiwrap/SendableChooserBase.yml create mode 100644 wpilibc/src/main/python/semiwrap/SensorUtil.yml create mode 100644 wpilibc/src/main/python/semiwrap/SerialPort.yml create mode 100644 wpilibc/src/main/python/semiwrap/Servo.yml create mode 100644 wpilibc/src/main/python/semiwrap/SharpIR.yml create mode 100644 wpilibc/src/main/python/semiwrap/SmartDashboard.yml create mode 100644 wpilibc/src/main/python/semiwrap/Solenoid.yml create mode 100644 wpilibc/src/main/python/semiwrap/Spark.yml create mode 100644 wpilibc/src/main/python/semiwrap/SparkMini.yml create mode 100644 wpilibc/src/main/python/semiwrap/StadiaController.yml create mode 100644 wpilibc/src/main/python/semiwrap/SysIdRoutineLog.yml create mode 100644 wpilibc/src/main/python/semiwrap/SystemServer.yml create mode 100644 wpilibc/src/main/python/semiwrap/Talon.yml create mode 100644 wpilibc/src/main/python/semiwrap/Threads.yml create mode 100644 wpilibc/src/main/python/semiwrap/TimedRobot.yml create mode 100644 wpilibc/src/main/python/semiwrap/Timer.yml create mode 100644 wpilibc/src/main/python/semiwrap/TimesliceRobot.yml create mode 100644 wpilibc/src/main/python/semiwrap/Tracer.yml create mode 100644 wpilibc/src/main/python/semiwrap/Victor.yml create mode 100644 wpilibc/src/main/python/semiwrap/VictorSP.yml create mode 100644 wpilibc/src/main/python/semiwrap/Watchdog.yml create mode 100644 wpilibc/src/main/python/semiwrap/XboxController.yml create mode 100644 wpilibc/src/main/python/semiwrap/counter/EdgeConfiguration.yml create mode 100644 wpilibc/src/main/python/semiwrap/counter/Tachometer.yml create mode 100644 wpilibc/src/main/python/semiwrap/counter/UpDownCounter.yml create mode 100644 wpilibc/src/main/python/semiwrap/drive/DifferentialDrive.yml create mode 100644 wpilibc/src/main/python/semiwrap/drive/MecanumDrive.yml create mode 100644 wpilibc/src/main/python/semiwrap/drive/RobotDriveBase.yml create mode 100644 wpilibc/src/main/python/semiwrap/event/BooleanEvent.yml create mode 100644 wpilibc/src/main/python/semiwrap/event/EventLoop.yml create mode 100644 wpilibc/src/main/python/semiwrap/event/NetworkBooleanEvent.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/ADXL345Sim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/AddressableLEDSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/AnalogEncoderSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/AnalogInputSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/BatterySim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/CTREPCMSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/CallbackStore.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/DCMotorSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/DIOSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/DifferentialDrivetrainSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/DigitalPWMSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/DoubleSolenoidSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/DriverStationSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/DutyCycleEncoderSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/DutyCycleSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/ElevatorSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/EncoderSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/FlywheelSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/GenericHIDSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/JoystickSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/LinearSystemSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/PS4ControllerSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/PS5ControllerSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/PWMMotorControllerSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/PWMSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/PneumaticsBaseSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/PowerDistributionSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/REVPHSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/RoboRioSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/SendableChooserSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/ServoSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/SharpIRSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/SimDeviceSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/SimHooks.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/SingleJointedArmSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/SolenoidSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/StadiaControllerSim.yml create mode 100644 wpilibc/src/main/python/semiwrap/simulation/XboxControllerSim.yml create mode 100644 wpilibc/src/main/python/wpilib/__init__.py create mode 100644 wpilibc/src/main/python/wpilib/_impl/__init__.py create mode 100644 wpilibc/src/main/python/wpilib/_impl/main.py create mode 100644 wpilibc/src/main/python/wpilib/_impl/report_error.py create mode 100644 wpilibc/src/main/python/wpilib/_impl/start.py create mode 100644 wpilibc/src/main/python/wpilib/_impl/utils.py create mode 100644 wpilibc/src/main/python/wpilib/cameraserver.py create mode 100644 wpilibc/src/main/python/wpilib/counter/__init__.py create mode 100644 wpilibc/src/main/python/wpilib/counter/counter.cpp create mode 100644 wpilibc/src/main/python/wpilib/deployinfo.py create mode 100644 wpilibc/src/main/python/wpilib/drive/__init__.py create mode 100644 wpilibc/src/main/python/wpilib/drive/drive.cpp create mode 100644 wpilibc/src/main/python/wpilib/event/__init__.py create mode 100644 wpilibc/src/main/python/wpilib/event/event.cpp create mode 100644 wpilibc/src/main/python/wpilib/interfaces/__init__.py create mode 100644 wpilibc/src/main/python/wpilib/py.typed create mode 100644 wpilibc/src/main/python/wpilib/simulation/__init__.py create mode 100644 wpilibc/src/main/python/wpilib/simulation/simulation.cpp create mode 100644 wpilibc/src/main/python/wpilib/src/main.cpp create mode 100644 wpilibc/src/main/python/wpilib/src/rpy/ControlWord.cpp create mode 100644 wpilibc/src/main/python/wpilib/src/rpy/ControlWord.h create mode 100644 wpilibc/src/main/python/wpilib/src/rpy/Filesystem.h create mode 100644 wpilibc/src/main/python/wpilib/src/rpy/Filesystem.inc create mode 100644 wpilibc/src/main/python/wpilib/src/rpy/MotorControllerGroup.cpp create mode 100644 wpilibc/src/main/python/wpilib/src/rpy/MotorControllerGroup.h create mode 100644 wpilibc/src/main/python/wpilib/src/rpy/Notifier.cpp create mode 100644 wpilibc/src/main/python/wpilib/src/rpy/Notifier.h create mode 100644 wpilibc/src/main/python/wpilib/src/rpy/SmartDashboardData.cpp create mode 100644 wpilibc/src/main/python/wpilib/src/rpy/SmartDashboardData.h create mode 100644 wpilibc/src/main/python/wpilib/sysid/__init__.py create mode 100644 wpilibc/src/test/python/conftest.py create mode 100644 wpilibc/src/test/python/test_alert.py create mode 100644 wpilibc/src/test/python/test_datalogmanager.py create mode 100644 wpilibc/src/test/python/test_joystick.py create mode 100644 wpilibc/src/test/python/test_mechanism2d.py create mode 100644 wpilibc/src/test/python/test_notifier.py create mode 100644 wpilibc/src/test/python/test_sendable_chooser.py create mode 100644 wpilibc/src/test/python/test_wpilib.py create mode 100644 wpilibc/src/test/python/test_wpilib_drive.py create mode 100644 wpilibc/src/test/python/test_wpilib_interfaces.py create mode 100644 wpilibc/src/test/python/test_wpilib_simulation.py create mode 100644 wpimath/src/main/python/README.md create mode 100644 wpimath/src/main/python/native-pyproject.toml create mode 100644 wpimath/src/main/python/pyproject.toml create mode 100644 wpimath/src/main/python/semiwrap/ComputerVisionUtil.yml create mode 100644 wpimath/src/main/python/semiwrap/MathUtil.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/ArmFeedforward.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/BangBangController.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/CentripetalAccelerationConstraint.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/ControlAffinePlantInversionFeedforward.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/DCMotor.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/DifferentialDriveAccelerationLimiter.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/DifferentialDriveFeedforward.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/DifferentialDriveKinematicsConstraint.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/DifferentialDrivePoseEstimator.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/DifferentialDrivePoseEstimator3d.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/DifferentialDriveVoltageConstraint.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/DifferentialDriveWheelVoltages.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/ElevatorFeedforward.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/EllipticalRegionConstraint.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/ExponentialProfile.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/ExtendedKalmanFilter.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/HolonomicDriveController.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/ImplicitModelFollower.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/KalmanFilter.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/LTVDifferentialDriveController.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/LTVUnicycleController.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/LinearPlantInversionFeedforward.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/LinearQuadraticRegulator.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/LinearSystem.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/LinearSystemId.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/LinearSystemLoop.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/MaxVelocityConstraint.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/MecanumDriveKinematicsConstraint.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/MecanumDrivePoseEstimator.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/MecanumDrivePoseEstimator3d.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/PIDController.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/PoseEstimator.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/PoseEstimator3d.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/ProfiledPIDController.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/RectangularRegionConstraint.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/SimpleMotorFeedforward.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/SimulatedAnnealing.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/SwerveDriveKinematicsConstraint.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/SwerveDrivePoseEstimator.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/SwerveDrivePoseEstimator3d.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/Trajectory.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/TrajectoryConfig.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/TrajectoryConstraint.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/TrajectoryGenerator.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/TrajectoryParameterizer.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/TrapezoidProfile.yml create mode 100644 wpimath/src/main/python/semiwrap/controls/TravelingSalesman.yml create mode 100644 wpimath/src/main/python/semiwrap/filter/Debouncer.yml create mode 100644 wpimath/src/main/python/semiwrap/filter/LinearFilter.yml create mode 100644 wpimath/src/main/python/semiwrap/filter/MedianFilter.yml create mode 100644 wpimath/src/main/python/semiwrap/filter/SlewRateLimiter.yml create mode 100644 wpimath/src/main/python/semiwrap/geometry/CoordinateAxis.yml create mode 100644 wpimath/src/main/python/semiwrap/geometry/CoordinateSystem.yml create mode 100644 wpimath/src/main/python/semiwrap/geometry/Ellipse2d.yml create mode 100644 wpimath/src/main/python/semiwrap/geometry/Pose2d.yml create mode 100644 wpimath/src/main/python/semiwrap/geometry/Pose3d.yml create mode 100644 wpimath/src/main/python/semiwrap/geometry/Quaternion.yml create mode 100644 wpimath/src/main/python/semiwrap/geometry/Rectangle2d.yml create mode 100644 wpimath/src/main/python/semiwrap/geometry/Rotation2d.yml create mode 100644 wpimath/src/main/python/semiwrap/geometry/Rotation3d.yml create mode 100644 wpimath/src/main/python/semiwrap/geometry/Transform2d.yml create mode 100644 wpimath/src/main/python/semiwrap/geometry/Transform3d.yml create mode 100644 wpimath/src/main/python/semiwrap/geometry/Translation2d.yml create mode 100644 wpimath/src/main/python/semiwrap/geometry/Translation3d.yml create mode 100644 wpimath/src/main/python/semiwrap/geometry/Twist2d.yml create mode 100644 wpimath/src/main/python/semiwrap/geometry/Twist3d.yml create mode 100644 wpimath/src/main/python/semiwrap/interpolation/TimeInterpolatableBuffer.yml create mode 100644 wpimath/src/main/python/semiwrap/kinematics/ChassisSpeeds.yml create mode 100644 wpimath/src/main/python/semiwrap/kinematics/DifferentialDriveKinematics.yml create mode 100644 wpimath/src/main/python/semiwrap/kinematics/DifferentialDriveOdometry.yml create mode 100644 wpimath/src/main/python/semiwrap/kinematics/DifferentialDriveOdometry3d.yml create mode 100644 wpimath/src/main/python/semiwrap/kinematics/DifferentialDriveWheelPositions.yml create mode 100644 wpimath/src/main/python/semiwrap/kinematics/DifferentialDriveWheelSpeeds.yml create mode 100644 wpimath/src/main/python/semiwrap/kinematics/Kinematics.yml create mode 100644 wpimath/src/main/python/semiwrap/kinematics/MecanumDriveKinematics.yml create mode 100644 wpimath/src/main/python/semiwrap/kinematics/MecanumDriveOdometry.yml create mode 100644 wpimath/src/main/python/semiwrap/kinematics/MecanumDriveOdometry3d.yml create mode 100644 wpimath/src/main/python/semiwrap/kinematics/MecanumDriveWheelPositions.yml create mode 100644 wpimath/src/main/python/semiwrap/kinematics/MecanumDriveWheelSpeeds.yml create mode 100644 wpimath/src/main/python/semiwrap/kinematics/Odometry.yml create mode 100644 wpimath/src/main/python/semiwrap/kinematics/Odometry3d.yml create mode 100644 wpimath/src/main/python/semiwrap/kinematics/SwerveDriveKinematics.yml create mode 100644 wpimath/src/main/python/semiwrap/kinematics/SwerveDriveOdometry.yml create mode 100644 wpimath/src/main/python/semiwrap/kinematics/SwerveDriveOdometry3d.yml create mode 100644 wpimath/src/main/python/semiwrap/kinematics/SwerveModulePosition.yml create mode 100644 wpimath/src/main/python/semiwrap/kinematics/SwerveModuleState.yml create mode 100644 wpimath/src/main/python/semiwrap/spline/CubicHermiteSpline.yml create mode 100644 wpimath/src/main/python/semiwrap/spline/QuinticHermiteSpline.yml create mode 100644 wpimath/src/main/python/semiwrap/spline/Spline.yml create mode 100644 wpimath/src/main/python/semiwrap/spline/SplineHelper.yml create mode 100644 wpimath/src/main/python/semiwrap/spline/SplineParameterizer.yml create mode 100755 wpimath/src/main/python/tools/create_units.py create mode 100644 wpimath/src/main/python/wpimath/__init__.py create mode 100644 wpimath/src/main/python/wpimath/_controls/__init__.py create mode 100644 wpimath/src/main/python/wpimath/_controls/controls.cpp create mode 100644 wpimath/src/main/python/wpimath/_impl/__init__.py create mode 100644 wpimath/src/main/python/wpimath/_impl/src/PyTrajectoryConstraint.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/_units_base_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/frc_eigen.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_acceleration_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_angle_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_angular_acceleration_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_angular_velocity_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_area_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_capacitance_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_charge_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_compound_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_concentration_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_conductance_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_current_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_data_transfer_rate_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_data_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_density_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_energy_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_force_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_frequency_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_illuminance_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_impedance_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_inductance_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_length_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_luminous_flux_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_luminous_intensity_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_magnetic_field_strength_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_magnetic_flux_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_mass_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_misc_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_moment_of_inertia_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_power_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_pressure_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_radiation_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_solid_angle_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_substance_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_temperature_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_time_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_torque_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_velocity_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_voltage_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/_impl/src/type_casters/units_volume_type_caster.h create mode 100644 wpimath/src/main/python/wpimath/controller/__init__.py create mode 100644 wpimath/src/main/python/wpimath/estimator/__init__.py create mode 100644 wpimath/src/main/python/wpimath/filter/__init__.py create mode 100644 wpimath/src/main/python/wpimath/filter/filter.cpp create mode 100644 wpimath/src/main/python/wpimath/geometry/__init__.py create mode 100644 wpimath/src/main/python/wpimath/geometry/geometry.cpp create mode 100644 wpimath/src/main/python/wpimath/geometry/include/rpy/geometryToString.h create mode 100644 wpimath/src/main/python/wpimath/interpolation/__init__.py create mode 100644 wpimath/src/main/python/wpimath/interpolation/interpolation.cpp create mode 100644 wpimath/src/main/python/wpimath/kinematics/__init__.py create mode 100644 wpimath/src/main/python/wpimath/kinematics/kinematics.cpp create mode 100644 wpimath/src/main/python/wpimath/optimization/__init__.py create mode 100644 wpimath/src/main/python/wpimath/path/__init__.py create mode 100644 wpimath/src/main/python/wpimath/py.typed create mode 100644 wpimath/src/main/python/wpimath/spline/__init__.py create mode 100644 wpimath/src/main/python/wpimath/spline/spline.cpp create mode 100644 wpimath/src/main/python/wpimath/src/wpimath.cpp create mode 100644 wpimath/src/main/python/wpimath/system/__init__.py create mode 100644 wpimath/src/main/python/wpimath/system/plant/__init__.py create mode 100644 wpimath/src/main/python/wpimath/trajectory/__init__.py create mode 100644 wpimath/src/main/python/wpimath/trajectory/constraint/__init__.py create mode 100644 wpimath/src/main/python/wpimath/units.py create mode 100644 wpimath/src/test/python/conftest.py create mode 100644 wpimath/src/test/python/cpp/pyproject.toml create mode 100644 wpimath/src/test/python/cpp/semiwrap/module.yml create mode 100644 wpimath/src/test/python/cpp/wpimath_test/__init__.py create mode 100644 wpimath/src/test/python/cpp/wpimath_test/include/module.h create mode 100644 wpimath/src/test/python/cpp/wpimath_test/src/module.cpp create mode 100644 wpimath/src/test/python/geometry/test_rotation2d.py create mode 100644 wpimath/src/test/python/kinematics/test_chassis_speeds.py create mode 100644 wpimath/src/test/python/kinematics/test_swerve.py create mode 100644 wpimath/src/test/python/test_controller.py create mode 100644 wpimath/src/test/python/test_estimator.py create mode 100644 wpimath/src/test/python/test_interpolation.py create mode 100644 wpimath/src/test/python/test_optimization_simulated_annealing.py create mode 100644 wpimath/src/test/python/test_spline.py create mode 100644 wpimath/src/test/python/test_system.py create mode 100644 wpimath/src/test/python/test_trajectory.py create mode 100644 wpimath/src/test/python/test_trajectory_exponential_profile.py create mode 100644 wpimath/src/test/python/test_trapezoid_profile.py create mode 100644 wpimath/src/test/python/test_units.py create mode 100644 wpimath/src/test/python/trajectory/test_trapezoidal_profile.py delete mode 100755 wpinet/src/test/python/run_tests.py delete mode 100755 wpiutil/src/test/python/run_tests.py create mode 100644 xrpVendordep/src/main/python/README.md create mode 100644 xrpVendordep/src/main/python/native-pyproject.toml create mode 100644 xrpVendordep/src/main/python/pyproject.toml create mode 100644 xrpVendordep/src/main/python/semiwrap/XRPGyro.yml create mode 100644 xrpVendordep/src/main/python/semiwrap/XRPMotor.yml create mode 100644 xrpVendordep/src/main/python/semiwrap/XRPOnBoardIO.yml create mode 100644 xrpVendordep/src/main/python/semiwrap/XRPRangefinder.yml create mode 100644 xrpVendordep/src/main/python/semiwrap/XRPReflectanceSensor.yml create mode 100644 xrpVendordep/src/main/python/semiwrap/XRPServo.yml create mode 100644 xrpVendordep/src/main/python/xrp/__init__.py create mode 100644 xrpVendordep/src/main/python/xrp/extension/__init__.py create mode 100644 xrpVendordep/src/main/python/xrp/extension/main.py create mode 100644 xrpVendordep/src/main/python/xrp/py.typed create mode 100644 xrpVendordep/src/main/python/xrp/src/main.cpp create mode 100644 xrpVendordep/src/main/python/xrp/xrp.pc create mode 100644 xrpVendordep/src/test/python/test_xrp.py diff --git a/.styleguide b/.styleguide index b4cebdb3d0..5349f31ae7 100644 --- a/.styleguide +++ b/.styleguide @@ -23,6 +23,13 @@ generatedFileExclude { fieldImages/src/main/native/resources/ apriltag/src/test/resources/ wpilibc/src/generated/ + + apriltag/src/main/python/ + apriltag/src/test/python/ + wpilibc/src/main/python/ + wpilibc/src/test/python/ + xrpVendordep/src/main/python/ + xrpVendordep/src/test/python/ } repoRootNameOverride { diff --git a/apriltag/src/main/python/README.md b/apriltag/src/main/python/README.md new file mode 100644 index 0000000000..08fcd0aa58 --- /dev/null +++ b/apriltag/src/main/python/README.md @@ -0,0 +1,4 @@ +robotpy-apriltag +================ + +RobotPy wrappers around WPILib's version of the apriltag library. diff --git a/apriltag/src/main/python/native-pyproject.toml b/apriltag/src/main/python/native-pyproject.toml new file mode 100644 index 0000000000..4d6a434ea0 --- /dev/null +++ b/apriltag/src/main/python/native-pyproject.toml @@ -0,0 +1,41 @@ +[build-system] +build-backend = "hatchling.build" +requires = [ + "hatchling", + "hatch-nativelib~=0.2.0", + "hatch-robotpy~=0.2.1", + "robotpy-native-wpiutil==2027.0.0a2", + "robotpy-native-wpimath==2027.0.0a2", +] + +[project] +name = "robotpy-native-apriltag" +version = "2027.0.0a2" +description = "WPILib AprilTag Library" +license = "BSD-3-Clause" + +dependencies = [ + "robotpy-native-wpiutil==2027.0.0a2", + "robotpy-native-wpimath==2027.0.0a2", +] + +[tool.hatch.build.targets.wheel] +packages = ["src/native"] + +[[tool.hatch.build.hooks.robotpy.maven_lib_download]] +artifact_id = "apriltag-cpp" +group_id = "edu.wpi.first.apriltag" +repo_url = "https://frcmaven.wpi.edu/artifactory/release-2027" +version = "2027.0.0-alpha-2" + +extract_to = "src/native/apriltag" +libs = ["apriltag"] + +[[tool.hatch.build.hooks.nativelib.pcfile]] +pcfile = "src/native/apriltag/robotpy-native-apriltag.pc" +name = "apriltag" + +includedir = "src/native/apriltag/include" +libdir = "src/native/apriltag/lib" +shared_libraries = ["apriltag"] +requires = ["robotpy-native-wpiutil", "robotpy-native-wpimath"] diff --git a/apriltag/src/main/python/pyproject.toml b/apriltag/src/main/python/pyproject.toml new file mode 100644 index 0000000000..03697cc231 --- /dev/null +++ b/apriltag/src/main/python/pyproject.toml @@ -0,0 +1,74 @@ +[build-system] +build-backend = "hatchling.build" +requires = [ + "semiwrap~=0.1.7", + "hatch-meson~=0.1.0b2", + "hatch-robotpy~=0.2.1", + "hatchling", + "robotpy-native-apriltag==2027.0.0a2", + "robotpy-wpiutil==2027.0.0a2", + "robotpy-wpimath==2027.0.0a2", +] + +[project] +name = "robotpy-apriltag" +version = "2027.0.0a2" +description = "RobotPy bindings for WPILib's AprilTag library" +authors = [ + {name = "RobotPy Development Team", email = "robotpy@googlegroups.com"}, +] +license = "BSD-3-Clause" +dependencies = [ + "robotpy-native-apriltag==2027.0.0a2", + "robotpy-wpiutil==2027.0.0a2", + "robotpy-wpimath==2027.0.0a2", +] + +[project.urls] +"Source code" = "https://github.com/robotpy/mostrobotpy" + + +[tool.hatch.build.hooks.robotpy] +version_file = "robotpy_apriltag/version.py" + +[tool.hatch.build.hooks.semiwrap] + +[tool.hatch.build.hooks.meson] + +[tool.hatch.build.targets.wheel] +packages = ["robotpy_apriltag"] + + +[tool.semiwrap] +update_init = [ + "robotpy_apriltag robotpy_apriltag._apriltag" +] +scan_headers_ignore = [ + "common/*", + "test/*", + + "apriltag.h", + "apriltag_math.h", + "apriltag_pose.h", + + "frc/apriltag/AprilTagDetector_cv.h", + + "tag16h5.h", + "tag36h11.h", +] + +[tool.semiwrap.extension_modules."robotpy_apriltag._apriltag"] +name = "apriltag" +wraps = ["robotpy-native-apriltag"] +depends = ["wpiutil", "wpimath"] + +[tool.semiwrap.extension_modules."robotpy_apriltag._apriltag".headers] +# frc/apriltag +AprilTag = "frc/apriltag/AprilTag.h" +AprilTagDetection = "frc/apriltag/AprilTagDetection.h" +AprilTagDetector = "frc/apriltag/AprilTagDetector.h" +# AprilTagDetector_cv = "frc/apriltag/AprilTagDetector_cv.h" +AprilTagFieldLayout = "frc/apriltag/AprilTagFieldLayout.h" +AprilTagFields = "frc/apriltag/AprilTagFields.h" +AprilTagPoseEstimate = "frc/apriltag/AprilTagPoseEstimate.h" +AprilTagPoseEstimator = "frc/apriltag/AprilTagPoseEstimator.h" diff --git a/apriltag/src/main/python/robotpy_apriltag/__init__.py b/apriltag/src/main/python/robotpy_apriltag/__init__.py new file mode 100644 index 0000000000..233e3e6d95 --- /dev/null +++ b/apriltag/src/main/python/robotpy_apriltag/__init__.py @@ -0,0 +1,22 @@ +from . import _init__apriltag + +# autogenerated by 'semiwrap create-imports robotpy_apriltag robotpy_apriltag._apriltag' +from ._apriltag import ( + AprilTag, + AprilTagDetection, + AprilTagDetector, + AprilTagField, + AprilTagFieldLayout, + AprilTagPoseEstimate, + AprilTagPoseEstimator, +) + +__all__ = [ + "AprilTag", + "AprilTagDetection", + "AprilTagDetector", + "AprilTagField", + "AprilTagFieldLayout", + "AprilTagPoseEstimate", + "AprilTagPoseEstimator", +] diff --git a/apriltag/src/main/python/robotpy_apriltag/py.typed b/apriltag/src/main/python/robotpy_apriltag/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apriltag/src/main/python/robotpy_apriltag/src/main.cpp b/apriltag/src/main/python/robotpy_apriltag/src/main.cpp new file mode 100644 index 0000000000..caba6f678a --- /dev/null +++ b/apriltag/src/main/python/robotpy_apriltag/src/main.cpp @@ -0,0 +1,4 @@ + +#include "semiwrap_init.robotpy_apriltag._apriltag.hpp" + +SEMIWRAP_PYBIND11_MODULE(m) { initWrapper(m); } diff --git a/apriltag/src/main/python/semiwrap/AprilTag.yml b/apriltag/src/main/python/semiwrap/AprilTag.yml new file mode 100644 index 0000000000..42ae4168c8 --- /dev/null +++ b/apriltag/src/main/python/semiwrap/AprilTag.yml @@ -0,0 +1,16 @@ +functions: + to_json: + ignore: true + from_json: + ignore: true +classes: + frc::AprilTag: + attributes: + ID: + pose: + methods: + Generate36h11AprilTagImage: + ignore: true + Generate16h5AprilTagImage: + ignore: true + operator==: diff --git a/apriltag/src/main/python/semiwrap/AprilTagDetection.yml b/apriltag/src/main/python/semiwrap/AprilTagDetection.yml new file mode 100644 index 0000000000..23a35df5d2 --- /dev/null +++ b/apriltag/src/main/python/semiwrap/AprilTagDetection.yml @@ -0,0 +1,43 @@ +extra_includes: +- pybind11/eigen.h + +classes: + frc::AprilTagDetection: + methods: + GetFamily: + GetId: + GetHamming: + GetDecisionMargin: + GetHomography: + GetHomographyMatrix: + GetCenter: + GetCorner: + GetCorners: + inline_code: | + .def("__repr__", [](const AprilTagDetection &self) { + return py::str("") + .format(self.GetFamily(), self.GetId(), self.GetHamming(), self.GetDecisionMargin(), self.GetCenter()); + }) + frc::AprilTagDetection::Point: + attributes: + x: + y: + inline_code: | + .def(py::init([](double x, double y) { + AprilTagDetection::Point pt{x, y}; + return std::make_unique(std::move(pt)); + }), py::arg("x"), py::arg("y")) + .def("__len__", [](const AprilTagDetection::Point &self) { return 2; }) + .def("__getitem__", [](const AprilTagDetection::Point &self, int index) { + switch (index) { + case 0: + return self.x; + case 1: + return self.y; + default: + throw std::out_of_range("AprilTagDetection.Point index out of range"); + } + }) + .def("__repr__", [](const AprilTagDetection::Point &self) { + return py::str("AprilTagDetection.Point(x={}, y={})").format(self.x, self.y); + }) diff --git a/apriltag/src/main/python/semiwrap/AprilTagDetector.yml b/apriltag/src/main/python/semiwrap/AprilTagDetector.yml new file mode 100644 index 0000000000..3385e5172e --- /dev/null +++ b/apriltag/src/main/python/semiwrap/AprilTagDetector.yml @@ -0,0 +1,99 @@ +extra_includes: +- pybind11_typing.h + +classes: + frc::AprilTagDetector: + methods: + AprilTagDetector: + SetConfig: + GetConfig: + SetQuadThresholdParameters: + GetQuadThresholdParameters: + AddFamily: + RemoveFamily: + ClearFamilies: + Detect: + overloads: + int, int, int, uint8_t*: + ignore: true + int, int, uint8_t*: + ignore: true + inline_code: | + .def("detect", [](AprilTagDetector *self, py::buffer img) { + + // validate the input image buffer + auto buf = img.request(); + if (buf.ndim != 2) { + throw py::value_error("buffer must only have two dimensions"); + } else if (buf.itemsize != 1) { + throw py::value_error("buffer elements must be bytes"); + } + + // We are going to move the detection result into this shared_ptr + // so that python can keep it alive. We don't expose the result directly + // to the user because we'd have to pretend it's a list, and that would + // be annoying. + std::shared_ptr c_result; + { + py::gil_scoped_release unlock; + c_result = std::make_shared(std::move(self->Detect(buf.shape[1], buf.shape[0], (uint8_t*)buf.ptr))); + } + + // This tells python about the shared_ptr, and it'll keep it alive as + // long as the python reference is alive. When we call get(), we marked + // the return value as reference_internal so python will keep the python + // reference for the results object alive for as long as all of its + // results that we put into the list are alive + py::object py_result = py::cast(c_result); + auto len = c_result->size(); + auto get = py_result.attr("get"); + py::typing::List l(len); + for (size_t i = 0; i < len; i++) { + l[i] = get(i); + } + return l; + }, py::arg("image"), + R"doc( + Detect tags from an 8-bit grayscale image with shape (height, width) + + :return: list of results + )doc" + ) + frc::AprilTagDetector::Config: + attributes: + numThreads: + quadDecimate: + quadSigma: + refineEdges: + decodeSharpening: + debug: + methods: + operator==: + frc::AprilTagDetector::QuadThresholdParameters: + attributes: + minClusterPixels: + maxNumMaxima: + criticalAngle: + maxLineFitMSE: + minWhiteBlackDiff: + deglitch: + methods: + operator==: + frc::AprilTagDetector::Results: + rename: _Results + ignored_bases: + - std::span + force_no_trampoline: true + methods: + Results: + overloads: + '': + ignore: true + void*, const private_init&: + ignore: true + inline_code: | + // use the keepalive to keep the array of results around until + // the user deletes them + .def("get", [](const AprilTagDetector::Results &self, int i) { + return self[i]; + }, py::return_value_policy::reference_internal) diff --git a/apriltag/src/main/python/semiwrap/AprilTagFieldLayout.yml b/apriltag/src/main/python/semiwrap/AprilTagFieldLayout.yml new file mode 100644 index 0000000000..eba162f44c --- /dev/null +++ b/apriltag/src/main/python/semiwrap/AprilTagFieldLayout.yml @@ -0,0 +1,29 @@ +functions: + to_json: + ignore: true + from_json: + ignore: true + LoadAprilTagLayoutField: + ignore: true +classes: + frc::AprilTagFieldLayout: + enums: + OriginPosition: + methods: + AprilTagFieldLayout: + overloads: + '': + std::string_view: + std::vector, units::meter_t, units::meter_t: + LoadField: + GetFieldLength: + GetFieldWidth: + GetTags: + SetOrigin: + overloads: + OriginPosition: + const Pose3d&: + GetOrigin: + GetTagPose: + Serialize: + operator==: diff --git a/apriltag/src/main/python/semiwrap/AprilTagFields.yml b/apriltag/src/main/python/semiwrap/AprilTagFields.yml new file mode 100644 index 0000000000..4a411d3f27 --- /dev/null +++ b/apriltag/src/main/python/semiwrap/AprilTagFields.yml @@ -0,0 +1,2 @@ +enums: + AprilTagField: diff --git a/apriltag/src/main/python/semiwrap/AprilTagPoseEstimate.yml b/apriltag/src/main/python/semiwrap/AprilTagPoseEstimate.yml new file mode 100644 index 0000000000..37faf79df4 --- /dev/null +++ b/apriltag/src/main/python/semiwrap/AprilTagPoseEstimate.yml @@ -0,0 +1,9 @@ +classes: + frc::AprilTagPoseEstimate: + attributes: + pose1: + pose2: + error1: + error2: + methods: + GetAmbiguity: diff --git a/apriltag/src/main/python/semiwrap/AprilTagPoseEstimator.yml b/apriltag/src/main/python/semiwrap/AprilTagPoseEstimator.yml new file mode 100644 index 0000000000..d5f39edf3b --- /dev/null +++ b/apriltag/src/main/python/semiwrap/AprilTagPoseEstimator.yml @@ -0,0 +1,36 @@ +extra_includes: +- frc/apriltag/AprilTagDetection.h + +classes: + frc::AprilTagPoseEstimator: + methods: + AprilTagPoseEstimator: + SetConfig: + GetConfig: + EstimateHomography: + overloads: + const AprilTagDetection& [const]: + std::span [const]: + EstimateOrthogonalIteration: + overloads: + const AprilTagDetection&, int [const]: + std::span, std::span, int [const]: + Estimate: + overloads: + const AprilTagDetection& [const]: + std::span, std::span [const]: + frc::AprilTagPoseEstimator::Config: + force_no_default_constructor: true + attributes: + tagSize: + fx: + fy: + cx: + cy: + methods: + operator==: + inline_code: | + .def(py::init([](units::meter_t tagSize, double fx, double fy, double cx, double cy) { + AprilTagPoseEstimator::Config cfg{tagSize, fx, fy, cx, cy}; + return std::make_unique(std::move(cfg)); + }), py::arg("tagSize"), py::arg("fx"), py::arg("fy"), py::arg("cx"), py::arg("cy")) diff --git a/apriltag/src/test/python/tag1_640_480.jpg b/apriltag/src/test/python/tag1_640_480.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ad5cdce4c4705c7e1f56335db8241215abb14aca GIT binary patch literal 26688 zcmeI3dpwi>|NpN!=aAHts8kA5Dr}C8sfaObLz+rb*n}k1#He&wgv_Bi4LcZCDT_!t zjZVTgs~n4>@s@>WD3qp>=>FcuW;MXA2f!-4OV1#h=>JwnSiwz!HHa0!svz2rLm;BCteYiNF$p zB?3zXmI(Y$AaH;fc04>La-)d}Xsv9T?BB8pPypDNd^7!TbvZM$UBJI*5LP7op|X@g z_(Nf7TOzPTV2QvIfh7V<1eORa5m+LyL|}=)5`iTG|8oeK?lOg2@3OQuGudcrVr^n! zZTcsb06_jdXRxvA&w-_!!5<1s+Y*5#0!svz2rLm;BCteYiNF$pB?3zXmIy2n_@6{z zu@l$}$jZq4d4Zc8_>xzWmzR^1S5{P1P*PP^RaH?|QBhmIa)sJ*jpZsTD^{=2SOwM8 z)KpbpqqQ2UwGyfc{c{tDEchNd`DOC*%b;p1YS91kw)h0lP=b7tNsxtX24plKvKo-Z z7l1C9!H@@kdGY7R7k}R%GT?VqP*hr`tO8!ZUjfKKWMyUKWdD3?@aj15JRqkbzjBj_ zy}~N2zvAWysA*z)k&@n?%BPyRwjW!}j-9@+OnLPht+m=)^$iSRMsS3=g{77C-hB>^ z`<A3Hwx^?&&~F!)V6H1>0RV)ED2^zWHJ zc0mBy|83U4S@wU}r2*O{BPS;-r})P%h)fJvWHsdEH<>7`w8tv?N37axny3WblU`K$ zbeWzR?uX{F({0MDx4=Jd9s6V2-z@vr47>2(vh3ds`**uK099EC*m$xUfE_TcDqMi$ zJ}G}nXJFsgYr*S@s#t+lq}17M;DM8?4Q`$YbK)LNB#JNgT%q7 zPgW{nud)@3rHRs^SgY+YWb8SXe2R!-v1N%E>zl<-^bJScI_c)^s-XsG3OrPcDQh@T znCR^GOv^!wgY8L7o+!?Xr{&=xmvGu~Iz*s{ST$qPlcT!eS;(@1ddMBeX}Ifkq)jmP zYV}jYAH${@Jj`XsZsd?{z5@nB3c0Icj9!HQXTg;Tk}pZdTP?j$7C9JQ+>=a=auPs?NE4i6h4z4Ml2iF8rgPMN!?T-^ z`Fn0KaSr@zd5ua(9?VMWR{RP2^(k|L#fiA??9#l$@FajsL>rd5^{)vvWGJ6v919TA zu#QGyke=*6t>Se^jwN-W!V+>ypnqY7tc0B1*yK1^v~Z;O(=Of)SZ8qYOhR)NzS6OS zq4OX^q41rd05H?dw9wo%;`|^%%RwJWoXD9ogwO(ng{dT04_R0p$u%&y8fhqF3RBz7 z)9(QMn=Qi0@rK(v67-n(3yvfCn(nf^Tm-59v|u8#AtivTJ@ zZ8hi@?0_|-5Fk1%GYONeK`q{5A*9Fu0&m5schTN4i)|tyVU9>6xt-AO#5I!qWG~>m zyeg^e>rn@G}Xw2=%a%z*yU!&#WN}I-HOvO5q6+WD3iF<&wXR<4+>`H&@(F`A zBw5jKCft2Xo?xfD1vmw(sE%Sd)$76I8}Evo&a&(z8L4QQXE@y0OZXLzrc+weASRq; z^NncKs!9sLe~5y1dA!gXbiKS_*`fkkY;0j;V$AE+!I2y zbp&Q0Uzm1fC(jb6e0t1Dp2LGH$M=|d%4X%yNde?IK%Of31!U_iNO#%L|J6lrHD3bFfj&uvUS|3W;CJqPMQMnl7c}*h&8HYjW2xkj<(Zp6%jJ%1U^X+^ z=~DEk{SrEHmdM0I@`p_8XT=}byZSO@Z27u8C-RKwU|gk>f<_CxBN}GK2!Mh85-J*x z3tsWudD!hFJ_4l{)>G9HF@w_pza6`PU5K%r0A9Pz;p@3pTu2SGTaxeeM#x>M_A|LJ z*F|`fr$huIF-EXKDV;d%DW5E+8kyj>2CY;K>PU2Uk=$}R!r$C?i+XxiT)7-tLU-US zAS_l4ou&qJ)X7I}hTV@;70@Q-B2damjHL(C%*FLsGQJ*WTUFIqz2-rLn=A)@A>WBp zS~V2R@$e|l?@1?iN1#;P2C1$%9yn5nC}v|v8~`nMss6!g;sc<9pjeZ|392~Zd~g#S zpgq2jl^qILgv!TpXXC6&*0su_n4RvOE+~x=#&VF7JYWI6TsX;utEF^eq@(Vgj)oe# zcc1|s`42Ke0ex7_h#SegRgQ8Q1{+)_^n&P;we#&YH|`^)N2_eC5T8mSw)CUa*n^6a{N_yr;KaIlSR7HCwep1(bHrs@NKp&k zWaf;PH*ginM7)R2O|W@(Qa8!I>k|xe0gd*Tm}V6R6F+s=9i-k`+4M=2MSs|r*N}-? zz9AnrJx9Fm#@^IFuDN->Bej8?9gZ%d))PN4{Uo#~Mo5JCE0aY~9K+mwG_IpxU zmdXwj9A)6Nu+3$XAuljiM#6$2(S3O)C{|yBCh-Xl(uKdm0=60n`wQfa0eu^xs8X2y zyHy6_XvhieOIU%WM)%~kq0|};hnuZ0{suKYnjYQrs12o2J?LDG(8C=|cMAGOIn7acDXK?XVKy#IL)KyO=cH6|(Op>O@+pq22%!~DFT|M) zyT4pNWK9-VszWD#l{Q-TQ{(LQ1eSQ{Aat?}(#KkNgRd;UKII_0p&kkA7v|L0aCYKd zV@(}c^RSD2+l%(k;eX+YLxvFV0&5Yf(~ST`!%(0u$q0q9_{UYi=P9C?_PI%zusS=` z;IkVPIF=o{bG`HwahEFS8i>I_;}DG48IPqOwW#0G>;5h~xY;IrJrQ_qKGtj(s&noM zLutBXJ?Stl$0!1&L9yOtBFyuq-7s9c!BAimx>a_q(|&}$BFh5$YHc#^Sl)AFY3B_G zz%0~Yobzc0dmj$bfL{47W>J)rr=Y4Q;G&o8YLddPc~}Zg912Pmp%A0T2lh(R%UqYo z5ifbjYUhyfu5gIX`4Vc<<4hZM(zQ}ZjsU?G_bku=A?(Up4n2Hr-v!5Cr5#LzQuZ!T zm(EHuLcDQFnEEmVAPEGMtovK>F5iTjB1eP^4<+ zJ`PLH3F2E@jCg$i#VHS}moZS}Bjd_|=exyMyGwT#?cyl(3rp1K(UJE1uK&irzli>biiWMaRmdY_dne)V_6lK7RMOdAWQbhYT-pn&`PKe^j z5AT@~UEz?@)EF@WLqGEA=#sxUiinENoO6~K&818wpi zu6}0_rDkMX723qN`7w||W$caPCdJio&BvV|T(~;hR+V?Gpf^#lshwn0p06lpYI{n0mr^1C-?&H>JA;>_z z`rU_gS=%6Se(=xwM%3n3?U)Y5`oJfT?|Ie@BlXpA+aFK(+?;s{dt<=Eu8I|3w}30v z-WejH`X$_Rx!F9GT`dGvSXVk zwcR01W{jF2C~w~k9K^fh3rQ7Qq`2)DORX3x@J|?2#aXUJ02E?6PwXr5=7cep32cV3 z+whrJc&JwnRwQK4#QX&P|i-3{aL|Jz!B*L-7Op=IFyjv2^LLP_^pzls{YF30cZ}1~ke>G%Umfh9d zcRae}4fl?@++A(| z*ZuUf88dTzq+T_&|vd|>yun^{2ppOe0n7Oy#JZ|CX~t@OCx zOqa?s{oQ!U-iZpzNO@QRY2nPIWTZOMXyMF7?%is6u%n&XkM~&wI=wl}^53F{2Rs|s zhWyorowH7MgtWuz_&o2;!>bF&KiXMm%o0A_nQ5QjsCKAGStvH{0{Uh|%CM z?skzv*Y7fSNt#aIw^L>~UTZ&nP_nQ=cxMpDn>ZBzBWT#M#9Fgk{K1U;cRJh5<3nh8< zC)ii>M4#msW9oJ8IUL(@x8hM=Ji?ST)(@|E^1<6mU>8x5*vg>tP@ZB}C zF|2N{N3Y4%yp7w#pbtx{ao-umq0=*>i&LW>VH2Fd#8qKZceT9ssFQJV(x+E{L6z#{ z-Rpjqwb{LKA2;fgd;3KB_RkaCiZ2CK%?tcu)b2db&~aSl@SZzX!(Jh~bW#Zpt`B0# zp2;xYl#}AxqVj|Z|5k196NiqEdr6ERU5KwL)~LJb%>62}r?Wmu|F0Oq4nT^TDDD7m z^zN_`>$oGf#nW=;h)g+O2mLP3WMYc9tPV9bL*Ud{9LP|xAOytI21`HIe}klm#i8`r zSkH5CKvGI)hNDw*GjPA;^a7|U+(CLkjOMj{7}5G6a}M7E4m@{-|6+sAE7yH@44W?3 zh@%)0LOd%~ZJX%!E?uJ7G07#F1zLwUlc%vgL_3Vw#4^oYq3sV{1b7A%-p1XHRKwdB zWT=LWKPBK>F;!XFusGr0${tuSL#{ZB3YfXnH$0+PqYWX?_OD;xVCFWvuY`Hq?vrCl zal+PF&))G4_t|f4zaB0EgH?r7ZKb7*Wsg5bECR?ci@@V%-IFzoK*3K+Z(vbz!gjSi z-_F!zFU%s|Py69+4!eh2P2GC{LA*F%|M}ru`N7K+D%)kxjl10&5+prIQ3Kml-)sKL zo4S4Rd5mm?=JmDj8)M=%t?4DInI{g-NKn^xxF1U|hB6THE5A>1!sbZEUBd5B$c~}t zp-UD#O9XJ_TB1~7TfV1#8^Uwn^BCI;i5IB>CQs*sb&XHvT&pL36gW1pc0>q#mNz5n zlc2Y{M^MW~UG~=l{>a0cseW(5@M0DwhS*8%F`fpvfa z|5@?Ak+I2v<{d-gADunCKr>B=$}ZryPKSN@%?imlxcoc6{&-va- zcQLNbo#SPDjyC^ps~anQw=H|jB4rh-)@OIdw$SdpPu`{bkLZ4#DDvgrfm5z4PKN?B z4T}JwDEvBp#3po=?O!1%B*rgBa-lQiWp7{i)Q^s5cGkBS0nJJ8cOMvjPZog`8Dt7` zowiq+$?y>a1A!W`7H(uWT(}4vVPy1~*DeA-f0j?!yQ)X*ygZ!lYN|RZIO{S!eu(lr z-hTqSpeZ}ua4_R`iN&V~hYN%C@av+Do}s4deVU)#`;2k!^^{YTk)l#v&-#qf!|!?z zs!z8aeK^_@rM(Df>89-K$gG{0rpH%odqNlroE5%|uOOMenB4N`8hnVib6%HV5s0I5 z46eb&v+wUxdY>NIicMWG* zrI>I5$viQ|?PMQ47;-r`l@J*{3MM8tYR40T$Z-hn#Fn6bS3C5%;bugw7|5UJ!mPEC zv)<`jFFPvk#%qb{VLW=J+7y$6S0|+H7XJT*;-5w1pX)cfAJp$hUr|H7X=`Zqr9*SW zZax%;g#_#mtv0m(76LnY@LTj1V@cJqid=6gRg_Y$u!W$&Y|$F_;C}JE#l-3l9Tq+Hd2>QU^lXlg{}GbbqVt zT?7s-0$g67^K--|R>1gy!=L?R?p&St^)RGxi$Vu*NK$M$vrUx!S#|v=a}MhH=0|+X z^c{V>1V+Z|Z+CqBE@_h|Yiq(^P?ApB?r81%v2gcSTHS8{y)xbB*b2tb1F@fU6{R!3 z^0!X~3oSE(AyudEr#ZwdwSdgVOX41iuz_@T!f1nE*H?#yV_DvelhzTLd22<(uZ~&b*kR0#hpZ zg%n*Ovw3ew;eElvH}1ot8+tzD*vOh0&*tBtPkV`~=n-t*n~pUn;YjA+&Q{#ohv~@? z|AIC$Wwq%aISBIvy^|tBo*V4ZIBMB2CTyarBRI?CzSQX)%Z?C2mK-(ccb+J*8lXzH zRDuU0-4@PB%2zZurZ-q)oo75D1GN8^tYD=MP$*{Yw-8^Kldg}ZDuRdI-NSo!B1{b@ zLd8@BLi{cEzOZ3mbFaYMZ`L8U4b*I#=iTVmKJviqUG2xvbYOHG;>v2eH< z5*x9u%u#FAFskBQx3%5mwMl5lne$E!SX62l-)|!L8rVH5>C~15WZxoi# z>59frUqn`~I0%a|hQf@{!ky$TFi9l@r`MD55pS<`NcVoXcVxW04Lqmm0+E<| zA8mvxST!Dr|2|MI5PKy<2>Va@&m5r7;dG^0mq0p;fb~S)``kG%OZwaX`|4kPn@+3S zrE_T<-Wl7qKTh^+sm}5l^zqUOe``h;yAFm(7FHeqh68&OC9PuXw99GA*BQInPoUe0 z*`WTT?Z>-<*0qlY0=N69dc3%iFyH&^QAl6fB0x=duh=%%91EMRxxG_RsNQl7Ay>QR zi2cXvurA@=-O=^<&pWNXT3hD$3m3TR7e{)ri@;0DJbC!M!RHe{1bOM_8jp?CRBEyH zBK5S`R)XE1)Cv8MLi?9JvOqkF*mLg|#2`XXi&0X(-N-!r^`nQ6VSd#vKKuKKSNblO zECRbX>K0D7>WO{74*Nn#IL(I+X zPSVKqENp11A~mG}edu$9q(uL2z+PfQisdAGup_OJ?mv*%m?H)nZKv7vNPMtn;{aVe zlu{2XQk!Day_pDLQ`01~8Jb`2bMrY1PLE*Ii_~U>>*C?ylep7{a0jUOmw)t~rZ!Mj z!0_fzi1VKnS>eJU8w9`~XZ!5OBRd8<3vEJ^p``pE_D-^RC<#A=ukN0cMAynI5$)eX+%P^O> z7a82!isl_TV{7cI{`++9!i=VP+V92KS$g}x4fE>(p+>!EExlNH5Q7cl1l}}s_d|!A zVQt@*HFhAS_(%H-kNUc|LYZEetW3`OZ5l`X#{w2?|8k!-o;Uh9&DUs|Q7xSMaQlFu zz;J^;eT|RmHZ48*dv3w9e?t7UVyfW^Fc^f4kk9G*U)1f^42r4u6rB@+z4(;Z&E6e` z>+Exd_nuQ;JVOgR55_k19a{v@uEpQavb7J12oFCxp#uW5;X)$N5kAgAn4|!R6f-zG zt<_25j|sm6ts_kx+edtj;>2gTak|6MHpAsYX$JzNv6d)5=N#bBT)bJe|8ohhH&1>Lm=wlXa0qs;#@ zd3rl=Jw^+3weRuC?ZLScj-tYS!%PAVVHOP|niONipRuxTUBu1TS5&_>db1(ZZT0Ua z(*4@1t2q?w@m^}z{FM#7`E9L{M<`?X?+4?2XS~PEf(oz>CxdOZYR8mlomIhlPi$WC zluYVF#I`G|=Ul>?C>ArygP?Qw8M?CI>$?y%x6R#Iyyo+3B>(5FFL|RI+)>ssj2+~_ zp~D|-)B=V}qAQciW!}H|C~(!7;6x$VCMQ9HI<9Kfc%SeAy!Kpm3h_Qu4^=93y8zOx^vq zKO~4{8Wz~*a8NWMopdP*53Bp4XK4UU;5nSJ`;i!;)sj{?^~1|$RC$OHTGtGR1i1NJ zR=A~)fnuG4?h`SKz{*a_l)P^)&DCl1J#P}|0IG*J1!HfL**a) zA3kyafKJh?@#P^E{FBEXeyxc@l!V=q;>*39S~RWsfsL+Olj}s!;}P0}H^|C|KL}j3 zwy9MAd46~MPt&dk%e-FPh>oQl~62kgHp>Fyy+9B6gcWsyB+i0F^%MtH8rqi57U{eSl zj2=?Qd70CNoTGtjo95&wDW^A>j>&D<`1rDqMw=d{!R|;^T1DifRCihD{OW1S1G~&~ z_2iw;GG=qzddXlC?u1^O0#R$+xsLiua^5+hC`Zu6Hl5$a1W(;eaSTI-yjw6j{lLz9 zArpKWSCMtJ`}vbHdU+90{&GIHPsNxR>cG5=xs>(k27iy0veZ@=bT%&vCz~5skx^~a za*Ke#L3cIV5+tCK$pN8cszFTI~fO;#8?Q-_p-0}y5f!EC)SXn)V$ zSlT6abv*4Mn~zd6#sr|-*MbvzVNg+1y__)c!alaiB*)|z+hi1_7Jy+Hz*Ai1=7?3J zBv;9$yKKrDc)u%2ZqppsYN8}&H_n1XK%Ei``vrJRY_-S+?Z1dt(XeC>Q4B!Tba^VbY)&r zq1^G~%&X-MVeg(EkUO3i&B%BU`Awb=(G#5iX3+pPedZzGZ@!5}lwPmGzL2ZA`yT1M zpZ8tCYtYGsof#*OsW9FOlK~7?&1CNCCq>^{@Aw85THp6F-i)O0lhSB>zqOxnK*I zww%NP;gYl;hI5weO1P4d`R+n=Bp!BmMJ1HBp^CO?Spl*%z}X?#WhLD=VuEEr^`B&* z);uQatczkP@(?$IWm~|kl{f+PYXDfZW(2g45VwnCvqDdw6()UVK= z+}k_dEOQR{fiBN*;BLC3*jS`_!k2|hs`bhwB^am@?T4}D(JVYQ7os1Ful9`PJ{faj z`dmt;*7AO~EBSmce=>%VU4syuclDdl!7ubq+Dn|Fr{1@O# zjm3POXC3#VDzkscl!U7fg-D+DFHFOqv=s9mWSlP~zXQ{4CtIdHF#)IRL%-^J!Xau~ zktI<>$#$}sITO@(DDF5m9hJn|{vyCYmk{P)yzx((4|(AE3(B#N~ zauB;RJf47aowr!S^u6QCs7*+i*4U^67$R&Uh5sz7{2=nB%dMAI9dFBRg*R z$t>GfX*odm%|JIxUG*e67wL{86Ra;WY!5|6FydqWw<11!FpR*G-Y!fW@a0s$<~MaL z0u_Xzzx=-7eO{JCE4dm=`Rp4B7)z}@xm@0oW z6=G{PuKZ}30I*4I%pP4%##_y_(c4z;9K(q;xx)mdvsiG^R;Uf99?*j3#&P3dX08hN zc9>Azjoo$ZnZy;xR|7Yy%7&g_mW##fKeugJ73E-;w0~<#U69G9MIb@kSr@|$JIgr4 zG7xU>9QEB7Uusl$mg1H+9;7}$tH~DAl;1XGJgdbAnMbHI1P3SzL{rL}d#OcwGm`Nb zwpqZi>wfbEMPX;^*6eQsMHCrg_ang!@(i=T-lQz7>6ni1@%z;`w3=RC4CZ7M&=BU= zv#Qq6pTDl!{cT>+uK1IZ21RpCE2=bi1Q?z-h}0CT@AG=-`du0F1$p ziVw0Y&ao$@Zb%p{VGl7W$x@^<&Tw@c#*hw)tVPF`6V;3j;Hqv!9y-2CGSgnHm3z-x zx`jTLfYSB^?yAj2}-jn=w_6+B!T|iv7on>@U>+2`S9T# z^xis6+G~rEPSzpEirMH-USdSrs*7%T>>$h1LyjR5)2~D!eLPIxhF&l`pjlHfL8|3^ z)kO!prfK7wz!1zLsEBuMUDYH)%Zdw)Vatw7-R6iF@(dwFHQf~bA%+$5G_IPEV72Xm z$xM6sp6yXiM-wvErGL$~l3hQH8^l!Cj>}yuG`l=!SScK-n#;*p{yfV-ztA3v2sd57 z=bc056QVh-Me}|O>p%_Pg?+8C(70Ni=SBqbM81H5 z@P{{YOiEvIfzf6-9mYe+2ZVg=Udv0CI6i9nqCl5fZ`BY(K=)*3aH_Ecbgyxc@$0=( ztCA>_)cWHx1+a&c#kWu@^nggT8}U;6CRu!>!=}L-G$lOdm?yR%4yH~)D_qX%UoT~T znqZxrlDhU)T&ZDBu-h=BsH6aAx{7jGoT2R1V(K>BKQ!|{&ISB)TJJyBF9?qC+lVQ% zmC{2$B?DPt6XpHVw$=$Of8CxN)_zXynCa_Z0|n}LG?l{Iw*pRTNH=8JM)4#GxMlR^ zWb~|e{OGfEP8Q^RHW2b@2_agdRK>q=)O?-Su|B9lU2~9&-W@{ zDR_MT4DV{ENmkJ#l_Y83-0H;f(i7sC_43?iHub!CDa-TEhU{cXuN}Ht>u@fp39k$!0FDqX9kKw zci4;QG5!HXhxdHEvhsF(RRi%_&B~pFho1Yq

S@5MJlgZv?Kk{+g*%B0Jr#c5J8h z)~DY>l)Lp0LgZ^DCE@9gZtg7G2p>*(IKNgxi!0l5NICOtQC)HjVecdZw_%$(;0x^< ztz*hK8Uv0{>!BQEn5qybZCwL*z{5aI>E8 z-z$5jysi4p!PbJzXuX%WPhY=|gOzHQp<-J%pWw>xVKecba<54arS@FSQW-3c?1`Ge zmE$!GZ6CE^zYn|6?IV5{->T#5ByGC!F18KRtp8cW+qL>;Zrx9C@^G*H<_u&o#5yqT zh80Uc$G0bK85LapN(`%d5-!Yn4ReNGp&w5^YAGRyT zO@Nt!Vn!@sf%BVYWU?@>hp3{?vq(kDi=#)6R7uXKqC-~oTyEmY)+AGu)ye1(55RdK zcLpc)*x28Ib|=A%(pn!{mX5oH(Vn|z>2Q~QX>*Dz-O~RJ95ZkKj!eN890v@L^=qXEe@tjb+qS>OM%(6TXT}*l3hH6)6($&Xb0MYP z%tQTfrCY?I9{x)4h0^BeG%t(L9ghOF$zTAw6n4qn4 zEUiHN8l@W9lbk4k7sPRy{Tw(@Z^#T}e}4zAVr+u9-Rz--jX2Zg{l(8>TM6`YQj|(< z?tV#HH7Wf%X4s|fBBnVwfe;Gb@3fj+B0IzWBpzd8mI3+%{Kmf{`Y#37GE{j>M~wU? zKJw0tfa!ZW)+;wQu3}kVhG9DxPk*3u9lZOcheqwQ+AA;OH}z+D(Z=va?_dyr0`o|$ zcrDjcghJOFIUtlsXGx5$EohIf8S&ADGb7@Jn>T1r#C6<+g^9vaV0ugj+R~$t3s#L(Nxaf?a%gI(<(MP4&<~a$iOav`Ywy0hb~?fLY*zSilZq z%KMIszws8}vbC^xFiREtU(jDph9Xf)&sn5^_hDw2tfhCuf)iWIv3+lrXtG+`WB2(iHmlNaD!3vf-bH|W^4 zL=@#>EgFqgPh=;qZx03#z5^rk5u)0y-jiI@v-6%nEMwCt|B$(O#r&GWzu(b)R zsTgq>L;n9b;gw?htl#U=WuZ{NpwGJ_{SGczMj3X{HSFbcRgRnoln$U_ooYz`DTach z1@fQ+yGe^cH)&S5e$4C0Wp)Y)F@j^ctUYGLU+rzhm%@0WMPNy<YINU;NBOlZ<6^?=U)L>SlND)Ncs1jzFynD~TcB zX($NpZXy7MQ96}gQ?bJDjAfS++8XG09;blov)S%1T3f0$mk(J4CKls<4_dOn&kZ5F zxpiZbge^gHQVY)$Tni7C(}#N|y4%;>a-324xMK*@zF`p{TSTC)Szzgbf5j2&N@ZNf zoiJum`lIwj^=^zloj_HL0J$4G2Ct~&THs^OFq;7w{1&{sn`nko77NcZyPbUD2~qIs zLy;Kc526^4C-L859O$iq>{CodtAEb-|Klm}Kkjj@IEb6puv67%Ng~yfbL=J<#XA@> zIf?dr65X**^<@r6s#H&pQE#Tl;nZxa0|6sR=9fD>jmxc3BXlH zk{x9~aHixfV_9`7x^`a~ ZDSgBy@(9h5EP_XD4zmCq?Zs7$e^R}jS3c26asU{ zf>Hv5^cEFGVE_>!5PBDq5FwHXA@Hpoy?^g>@8A1y{v_v|UDjUfUGIALPU38iS}v7a zD~AwTdhmexafGC42rW>OT?kKPD&D<<52+xtgLblTU6ef+3-?OFzd8on`gsJC+ydQ^ zr>~!n`;MTqf$r|ULFfH~xeF?dq0tW9Xn&x)Tdt>A!)2;!UbR}Bl$4Q* zSYZ5fhlJUIfexgquu*kmk6e+&!0lIYJqwRqF{emLY}{w|_hz*@w`-iq&=l0d9y`(y z-0-P=GNNnx;-jTs>O+mVP1m(&OA0(543mAqN`&Ok74pSj&=;-owfGxFw{MU5NkA1* z{Mi@F5Pw!F&?NAe*iFt0@Ta_G8TJw)+z4BWKTVbA2blN4JPzh_VV)1>IdQ%&U>=yq z!T;ogoh>Q|^=zs9_C|x*J{2j)xFL26+=gc%R;%t$#9V~t+bsSql%CYZ~Z1DZ`Bji{}1oBfDkfxV)|PpAHY^*;Cgeiawt3zrOWeuN@&%8$+d$sR}r# zhpKK_R+AT>HqpNyp)?(MMA1D}A}k3!oo#4<&=mBdNZZ{AOZ4QrzeowYD2vd_I1qK_ z%;P^zev=2g55eS)2L^Q=$$V!+;{j+Xg9!J)NYl*xzdkb=E)S}&B4iT>Pjb>~Bs4sDCZ7wPeF_4wryh!TPY<6^}P_dK801`0q-uEY<@OfQysSC!JF zFCg?0&%QpERC_Aosbpw95qz^0t4;@52s)jK?L8{hGniFwOL7@5+lx@YB52Pdd+)H* z5j!OehVv!RnulP^hnxN~v(eC{6VIT0XYG0hwYC$!en`b{ohG+AM$S4*v6C4Kk?Rrc zQYBeZZC;bH6c}+F_vujIqn~ZWh$XoQ_P$brXfDSNJz{Qf*G4oB`0(G3z4_Opg?0JE zCxz4H*TQ!qG%63))E|&%pXHR_nEsZKFOWm@0=y>W9eN&H8d_er)B}L{cmhv*BmFH6 zCJj7q-e&Hgv4zpW)mn+8pAtGZoLlLP=Y<5>JAMR$KRwkst2@wF(}q}A^S z)Tb?$;CXB^!AW`-FZ1$>wfAn8Ro`3?I?*M8()Zye5ARTtv@GRtY~*mbTsKu!D%5{y z`YcouT?F{!4N=ab_X;5c)mM{+Cn1^f$g(%n)Ag;^tX0P{^_)r$-Z=6j~6lW z@Lz|4V|DmOKDOr+Cd%>4;#LPdM_xuD)T)jfG-s_p&!~vnXdr=HeINohF$tlGDce~E zk=ez5ILBT806(NHQRO^u4Ao+i@2a_i2ln7t%~*z~uY}08u7H8I{c2uXbM&rzDkBfD zP;Ujxr`%J!${c7z$n_u|_(m8hF(sM76!I|-6<`uvb2+Cd(^J^N75G$ky9kBEvD0)& z-(cMQ7Uiwpqv(2^z82W{CFXYQYG0Rg8z2h!-aXEfk#~=GGNSv`#o$EB|@* z8~({e#q z1n1=?862R;zyx#~^E_5ttyYDI<5%bx$vTyl-`_V`Oq!hQ;Ue_7gv=Fw{%I4Z#wNB6bmvFL*dLr2cLWtnV~nHa6FQaW8x7P zboSZTD1LItgC}pCGd>=Lt{5WpeDy1}hju$8<<}$w4d4@!y;k>2o!*FGw3^HlL$Ijr zu<9K0D`n#IiKz^1T0hR-qGNB)JNXQO|B}EnyRet7?vz?V$K+zk2x-)F8Z*zF{GhLW zfJvgq-d}#uiR<}9GuDbcDyuA!FNaL6U~qbnRPn<$>*&PL(_Z#at+4gZ8{c(i_C-Cf zJMGnVC{GgE{DH0AV)sWTxKJAg6BliUe7~=Kh)MHMwzl76ryum!bF}J^^U!VyM8Abi zSa7+h{Y1yH{G>W4arU@W>B#rJwc|t@d31SuN+L=-w!PzU^ZAoLV}~RR+?OHbjXB*{ zwW>3F^v3hLGv7@}I0)X+jgcoA80g0zipa)Rkj0V^Q3cz>jy459?XIkyq(*0nqM|tr zPN<$BvdoYdQkmLU0JilcBy;H!=+$QFmv$#eb^&AU9!K?UKW<0pQ!n6_qFD6D*HBVt zVJ?d3AfOY^#QQ?i=JN4qezmTm`1$U^sR> z`!n#t6MjSVY@TRl*d#vOu$nj2!kjCcv={a+k8V{liBeYl7cN&882rfxD6F&8Vg7ja z2Aa{^>$A|Mu(q}~zgpMnqi4$|bI=v`Lq0nY>Xj;LZ)=>gYY)jM!!%bR2&OulENaNq z!_tc{ubu!l&{xr7Ky{DGk!zB#05^y!c2}n1qqW8Dj zF5^lg^d1LTI_|gX+^5Q2i$Nmc@MfB6*Y%czju#6NP1P@9iKG)`KI>X2sP|DES$%oQ zLbn$J@(Ugip;(v2$%iG6Mx2b8B~w?YxN+b)eK*X7G8B*Gg_%Ho!n_=;CaFT3$X0KN zdKU}8Nm~VrP1~*TWBmOVhKfI6nUk>REcatWMbvBvlpd@ULB~<5Gb%Dhc`lw+0L=tz zj3q^hFJaB3{I@f}zg`bY%A+O)Qiu|c7qxo@ig$=~XdbSt$8-JAwxhW(re_#R<3|uW zhl@h}kr@N4$;cC_bNZX8ix9;SPbF7X=`%7FC5UR7R5gD6qtM7(Z>t-Klt5`)!D!db zue!aSb2FxAu8Q?x3oaj-2fqCnD*;P3dI>foaZ&WsXBD(j-UhAFVuTzqXPD|W@AsOV zTCOyC8k{Xw4uol+hU45R!-iw2sgK}PI2~6`S=H$jkIRwQ{jabdNrwPZvBfd6QBRdr zZ`r5LZP}E%6j5gJESfRhBhR^+(=$3sQvlJg7@|^ZdvEjw88GUpXFNw(oq~M6FcJjf zVynFEsqP4knc*3>Os8Z|)I&fX-fUQiOgG?=y?&;Ah^sI3EhLtn;TH-LyH`Uc*ASMT zf|K#erTT+LH2zfjUJZm;m}lg^EMYfIG8x)d8Z)Er16Vy)vqWR0N}^w-sFL$~xrr{rku;G{ zi_8+SJxz#yW#blw#M6Z|&xmkiNf&|V6EAOCn{BD?c+@YKBV^9L5*my+_{sfQ#n<~|J?Xj3o{_1q;@AaoHW54cf?AC zI59W24bfcKM%R?COKF{dAlWj!8L1%B7G40hp5|%o&5NgOhA&MDfO>zT||q3-4N| zms26TZQk=|trjA}r;ly{tUm);!%)CSaVtU$>u)ws9i(8I9JrkD&#i!C=`Sv^T7jR$ z8@o%2>V>(NfK#=tETcuMLSgrcHKQrPEr1*vo|uAA!VwIv&D#U7*>a1bk{V#2^$ZW~ z`l=6x4(CIUjdohNZgyz=9ny@z}gO6Vn$GZE=5wHd^|5Dm{J_TBhxV!PD04!{*%YL?{Onjq6|L z?^m}~yM;o~*J5L04ChLg3&lg};w^OAwq3dL&m%ZfY}POU7!6}d{nM5$d^g(FHh}f_ zds6rPZ5GZnrZWJkNjqim+TNxBTO@H8Z(IgCMeRo8;~xAV18CS|jBWUhJR9Qj<3Tol z@cSbp7U>zeKJv*6qb?7Q(=Y5_B4KN!g^9o8(8aPOQAf7fIXDcc74I+Y1OxAH^J&o{ zB4^jK>90-Jr%rud#`b!!>i*|1tmujNTRHvlCS#%3?GAG$c?~9h)~Uv42J*udK>g|7 zh4}beL)Q2)al%4z{45f5`)!E&*1M6`yxTSUYojISsfox-`0-xFr@_fWSE#6r%GrEY z^f|{EU=sQ+Y;nZ>C(p`-W2Kxp& zr(RkQ)hD`m^8!S9VK4b_H6wn%!&?V`&mVW#p#rrR5N9aIDPT0cd{Gt* z@eyW6t5jVz8bIx};$O=sZ({L_aT{Vah5=hzG;`^-yJH-vxLPPX#`TyP$QFdN# zF=nqslq`(r&lcx7uc4r~O5)+Nf4pkM_6KkMZo)O3JU!cgyb_-cBwzo)yjl*s3Y;~R zycH*f_lMhjL4y7pTiX_wDQew;&|hLD^ZPqyd?z#&PtY9u{Z+HLt{*zbsfJ$Zcmmgu zjYN~s#mMvoUcQ2POVkj#zy+ZijMG@v9KL%ckP}V;`CnZ*wNcOwR}8`pI`f_gGSg%W9em@^H?a~TMMcMLJEl_%b=^Lt?# zuaa+VJoPAYs@BDK_3G6=g}m-Ay$f)jV%f8*p-%u6!GQ{F8r;O*yAlyvX;G6I1;E6w zuP$e(NIQNYRq#^sEkP+I!-Q~|Do1lcQqKRTI`0Ea0{?p)^prpPiqOI^{9l0i-`T-& z^ZzyYzxiM$+8}g0Mx5rm?(nq+a&T+{=vz+~(-Qp#&?#K5mcW*9U zzXK+QD|7(38ZLv0@8F&9j^+0e4t2 Ag8%>k literal 0 HcmV?d00001 diff --git a/apriltag/src/test/python/tag2_45deg_X.png b/apriltag/src/test/python/tag2_45deg_X.png new file mode 100644 index 0000000000000000000000000000000000000000..8d3521d08bcf5b7ed9b462a486a2111a6909f54b GIT binary patch literal 12625 zcmeHNi93}0+kdpLBnnY!IY;Fb5?LBlT7`;17>XpxZY*OxrT&3u=8{oJ4Xeo#;A9b3I(!wL+;R-ZVo zeHOzOr(@VciDgURlcfdkir~j0w?iikm%-z^>|!wdU&8%}iMxT*C3nvYt~QvhqmzTp zZZ~UJ8yiQr%TDgRg-i`tv>Pot>}qqt-OkBT+R)Cy1~a(eF1>f3w6=}4^j`VBd!^-- z)b`1%DalKp)|EbOU^xBgeiDXBV<)r^8hXX|wVOqddi^>F=S=N+s*W|k(;s?C393F8 z4DbJVG*0}t#Yet}t!cHp-8S+fU3$!KVgA>ksTH@B--VqKtq6IIJg1*40Qjw;HMpa#>3CN_=yh)2Y%w@&wBB* zEdKwa2Wp5gb@NhRcibwE*c?q0*siFqF8Tib`(#@TyM5PdH74~WJz$1AxOuiav1D#` zmV1Wf|8?bf@`aM~wu-autb+B-F(0NeWi^H^r?RlZJCi;8nG!;|D+kkOtC@0yAS*`Q zLX}|a*}bc$X2vI(GpxbhfZ1NQ5{BijO&18<_A)DLy{7MQYWC3mTm|*lrzwI*ma#bN zCfBZBG802^)3{N5;XY8f$Gx_muzGm3)u~)4VEm|gw#Q4pXgFzI5!p3}sZAsvp|`FB zEElh+u*W{VoBp&^Gx()gdMUduxz0ev=n?sfUBLljgRabyYX>=7Fl_sm&q|o{#e%VX zUMc%aoI<-gk+Pz^$d1cj>*r0Qg0d=X+rjohwR$f`DYo+GN4XKG&a!9Nj7$*NGvL%&e89=D9 zJVz}ZuDwSqx^vv(h+Szky*?q>pULC@W0S-Cr35vzd6VC&W8&}cCfq#C`ePU6&&;o5 z>tbt`%A}V9RC#Q(Scu&mm>KJgV9wUe-3no~dlD>64UgzQTJP_z*6#SsDTIY#eR?39 zV7tEmP^8yze_~l(<2|s(zTHnB$(&f3G-8l2pxxKkmp!%t3nY%E(2bcZ7 zTGCSL$wP479dpSAm=t4bj91?2I~I_`%V{io92%aq-ctBQT#Z~enb|2vSd0Z;2O|FI zbpAw7++e-utVRv#+#W)Z{?)3=uh|mCa=P`LV7M~$^T#!?%5QF}Gh(1bJ8$sy9AVa} zH(jUp_fg5>%QFp~%$e?H-=Xau9v&z41u*F?K=V}$J11&9+0~n;Nz*g6vzDBlTCVEq ztu8Z_$zR=l%@$yV<=!V3fhP9p`RhB!%l5O&HD_I5)k=3YR^D@Au*sWYbPOxqGUBGt z<|vxLD{Xo`HD}aeIfiZS-6;&;)e^?c+p=Af_^TfL5}Sg$jploFZIHFPy1JA%UBkNz zyRIX%g|hJtWKwy5>ua4_%=8eL@w})$6)SvbMk0lM`SPVsF>w7F6vU+lTQTRaQPI)X z*4F6g#jz<=2IFKc5LR3>cO4xa7j9E3+VIsQR}X^Wxh#HCTtY&ay5|78weX&-aXh-i z7_!ZIyJKg;M%V_4T|v$zfqeo=c*2^%-Hxx#C`)d~2k1ed-ogX*sd0J~4SN-cOTHD( zKgRBBc{y0LQVx#ZzF$G%3}pd^oxo3siH{G>_2hwlwzKx>a=3z~xJNBPk0bHV*(r9hUSIARgGtu9p>R8c!svK=4q3J)Uey4ULD*He18M%)$jInX z8o?FIb#qt-hmY&6#Z=Bl?;+iqn3w?B9{(%bSvNPg!H@Ro_du0>Qi2J{dfSoEXNg2e zSxrlH@V_P*Xi7E9$j&x_PjCOPPbWvX3dlf9ZdW<0xZ}xyqY!$1zL9{hZVlObm6%xqp7L)i5Lub(C<9I){i=(K4(0LdbOr(SqiKgO zh=_=|OpW%(#Keq$=aZuhnv1)>7YC3mEhBjTW4VJ~)tOt49Yj8pdlQj^Vb;m@oD$je zyt9-g%MVcaI}kY3HXD;z3aUyZN?G%_5eka7Z77H{a{Q(|;t4)k{8cMjTUw~8sX}|Y z|6=T_e!>m5Y_H#^>uyeVyEvcW^TIWL`*vm0CtQ_+gz!o@M`Bs*@WP!q%2q#yeKG-e zZ_!IoBT|+GDd~#D#l2?7hc<_xD!amQ63A9XMY z5|l{%Kz4X{#|1@DK1zuU23O6(Z=?~HJxk*nm2iWDsZ{D$JvBc+(byE1ur;8J_t`~Q zc>7AZyft+F#65&-e{9n9Z)127qy%+p#dX>pjq#XvN)&)qLqji~E(jhO8L1N)H6#+X zeb<;A&eK?n!-<&wEx1rUU^Xw3^*Dm1PtI+RiH+Yuu>S2I$q(#YfR#I5y7X(o(7p7# zw1XGsz6;H*nek`xL+>ycli!2F?iEtD$EHktzpjp92i_Z~V9tHta>Ao6C{K`;KYl#9 zF+K5{yPAUds*@laWTY#eEa>1P&(hM$!2KKvQU2EtNzE-SI|g7w3*49>JXYl;rDCyNlkL(U8Z(fWdBj0PqQtZ|_6BB3hyXCAXFM^WlIZD}&!~<{> zRU{BLrWZ_JL5dd?Y`XSV8n_Z?=e9=j#z=$uWZTeR@M9oww9dSC>fA&4{f~k6T(N6! zAMis7uxR6*k-s7EAo{UBly0|7EiA%UitPbcZh>gcc=^(RQbzlhv;JH}aP7MJ5Y#%; zTbmfcu`}KqZy2$OaOFtefVPJ>fh=~z>E)<85+XC|9L3U$fql#P4DbVn5j=QOF zdTzVpxB4!JFKX*CY!p?cQc{llPJ}qu7Gz~*S^4?p!$DNmv0`9>KUVDCy}Ojnwt_QG zv&i$#lpCD zqUwZfK>y$Vmp-*5YKX6UuQ_V4Vs1QZE}IRyZKMhmZ~pnAFU+Aph}&|9fFb!YW9<)+Q$&oy`K?PdKN zL81^rRxU0I9X`B#plz4*X$PoX&M|7|JE6=$BpvV`dd$B|AW-%RJ2iT1mPY`f;khfL zAA)JE5R`?Dsq~bDg@nI=DNiZp%!hxZutC+rW;; zyBeMt48E!LO9Qui@xPwA>I8^ANF@^QZCxMWek@9NDWU|p=Z`~KV&niC<09wY9d;yGZ>k&t=`4mGi~lmqxOB(Brd`QteX zGJgm%>9CfTmhmLb$)q|(l}lt&20qPmr;>^dleN$Jb4|vky!t@=9Pt65&1e&ZWbwbkHKIA9Z9xzuG&PPh?<$1A@5}^ z>PxYEl;)=n9QZPAy+ELQ8Yy=@WVRd})xaBrgse|sF&{D*rXttX6T%VX^*!0BpM@1t zT?g5vF^Tv}Lsn)a>*j;q^t5dn!7PX6{TOz)3qt>RFYu2+Gv)|f<{XM3)X_1D_Th@Y zWm9vMbRV7~-DsWdl`FfzM?lxgFWcMe+OC7gP?PVJ`{ zZ~>PKih%SbT!_i0tBbJ*tB{>IA2aRe`BwQw2Z!nk?%}^vB3bQ@EF1$qql-9idTGOT zWg?e=PAhdOz0C5x0bC-rWd)`sf#R)>&3*)xK^|^UK!x&he%7Kc$Jec~si4vph=85C zr%si;mt&fPs9!tseLnEUU{xj<$Zg$C+&uJo^SpZTVl9wJQ2bQZ>(`}Rk;kq5K%_A( zS9DJk#B~yjdk;M7J^uXyo+KYA4yx6CLC?U%=p{wnP+eJMawo4G$_hs!)rd$uRb3TD zbCE_=1&{n!J-xl%iir~di&BvcM)*Dpp_|2rC${Bg#pnH-AFsgfc|(=5NRt1!fZ{-9 z;W?YA$APBW=Z*giC0>oZ3u42Nvf^0ZV0Zaozf*uZ^oxnUkLEgF(jOixZ7o(&BB-iL zB+l~bAbfI2iN5{~ef^$@qI?TH8b?uTKOyB)lt402`8-gV4zKPo__zP0zQhw2w-m^| z?|@sPEWrj?O>)RL#PG4c^2{xm))i<_;Lj6yG%%1LV-434+c^oE0UbJ$ zC%RFi{r3E6O4)JbhHlil)p18CgG&RxFHN@9($+@F2@=(yDsA<|>gt-BqxAZngoRk(XH)>! zA&{%DhhnaEz<*i=C$QpARu8HnrLjJJ5W}>q?Ty=yz6^+zx#Wfa|kUsq*9J*ln28{Lczh41L^pRE0K9d!UuX@{=6NwV=!l0uOBs70vH zy=UU=V#{!hLGhpJHr%6YeDibzv86j(mE1O?=goWZPFp_@E&b9O*M0C5k;DqomJdS%mEG@JfQBuUkvI zRZ~*(a4x5oHa7FP_v+2!3kUqaTYQWx)~~MFtv$0`uXkXS*&|;gtvM4z+jP0ZYiPV! zO!-p7jYt-XfO#JQ=h6eF9YxEa4}8Ap+ofX}w(|Xc>+!1P?fRkwp^U>u%;{3vGeW4- zOs^*O=2(D>7jt||Tuf|uz^vS?O}y!WsXMjlr+)KSwwU{#+ZjIPG?NIi{DJe?Cq9j_ z%fTd|5?M%aYiGSORww|Il5I$lx7U^?)B70A{Eg{zjh1CXb3JiqcIAZEb+%B$Wb7e0 zo-p&*1x#7a8tl`RdEw^|tt5~Of93wnEe^p&y7jNDWH8JI&mjam&??uJaZw$oC(g;Z zaL~+8j-71CbRED z=ITL;mDQtl>H^pf9h8=KFtI3@{vub-O=nubIS_ShPgbV=6IvK58FkGXKSDf(}V9Q;Ga*SQPr1K;e&U%i@W_AUI z;rW87v>9Zfh*3i)30yL=w%=*G;;3;&2Fmr|e`!9Z|(CVATLg|rUv=99~vs(@f zPO=P$#Ea?k>5d~-BX@)yVmS?beOqdJdik}bLeL5~LVw4fx>lz+`YI#i&g?)>C``lV zBt>wo@ohvR5gBT%!-wh0R=oz}2bgszC@C6HDrsPW=%JwK7G$S($JhKt7-ovNrm2IC z%`Y^c0T@snrSv9vTG`st`JoZ_VVw2GfNtfg=2Y0x)^=*5#U^@U(|E9uzm?VAK|d~r zea5f!_)>YVfv&>qWG40u^e`A2(@79xMQu;M%7ho&+uP%Kca(cEf+dVqgL3NSS2jfEkVx4EOa zIr#1Q+p~+W-!~-Qo8@OBhJ5hzjNW4GlSnpb{V}?b;61ecU+eNkI?87nW~RoHI(gBw z54&25N2;nq(YD_|PYVjcwj6iP*{bU5do+c*DIPO%i0_=v(wTYj9$JmNa=BH%O;y-? zBEO#oG3D3mC2DM(FNWLlrv`F&ZiB)5Rc-d)Z?dwYhTPu&<~HBCsHgz>5MK#{4h#PY z7ziJw@bbsb7Wg_t5X3%arQ%EjCI-6*NyK>)^Fs+!7~__ff_**U?_sPR98ysvL#LU; zxl2^zP_+wwY?$4huR@IzBYK@M1 zKu4IYaUx!qNEBYqGY&wb|4$DNC@sLOyV<27z}wa^$4lh^Sz=`LX(iC8ilG6}U6kmT zV}H$|9=0N}20vFs8YY@~0QP)&e=u>Lo)A|nY67qGT>vpXJ03BoQZSkcbJrJ7_2?ps zii+UAs>w;px9F{Uy1J!McYzB7y*X=fX1D>md0(zs=PfZy-weJehW+{8rwYUpfow1` zHij{movp3Rm&PqEsK+WR0|qAbCxe0BM-={^4HJEFS;TvAHV`NbiL{Suxnnt|wF&vj z2o2p`U0?85oxh%9hb;U_3jq22Gjo{cId^csKFq_PINN*0ZbTbVPh$+pX{f=lPuBAjY{4Nbhj0eqMu zSZS~glaGQ@%dSaK2W4x)zi9FTTot{v2+SbEwe#(E;jsqtZWQ{cX_3!@F-G1i;Xm!5 zPe#y-$a7f}yqtJc53woWdwnWR08`na9}d=0gGoc)_o9_>{$>QS&ZlWoaI_M%A9$y` zaD3Uk?enF~+|cGdBc`+6JtLjxf2mkUBb18=!$IH>i$j=@=GwIh#-xVTET* z&CEhJ&wM?WY+FcWaYwAkWN_jSec;`>^hPl)cqIbw48tL%;L%b;_yiCB3JZQdMzaKX zelGYwVu$HDk3JLRk@L5iBl|o=kHW8g?|^>6O(APj!O#ZRb2aPYHC{GTb2i=K)o6kj4lTOhxj NIHISWcIf;c{{!Upqs{;T literal 0 HcmV?d00001 diff --git a/apriltag/src/test/python/tag2_45deg_y.png b/apriltag/src/test/python/tag2_45deg_y.png new file mode 100644 index 0000000000000000000000000000000000000000..f024f5cdb11e728b64ba00614fe25dfacb6e964e GIT binary patch literal 11275 zcmeHNcT`j9wm*)ej15FGQlyC@SV2SqX^s~}6k!l7AOg}1>gI@XWhRI@vdOw&4Bo1|+Bs%opY3266 zrM9?dBy|4z*!|?)sMRZerRI2ST6z7wvwXd?%1gQU$NeiEZkyQ3w!0=?40TmWKC@ZG zs477$uPtv#@up>dtV4!UNsRASi^`(JJr1!6&L?8^o(*O^VhJfe-IxB7XHIOT=meOU z#yX0F)fjg3xZ|63_`gDxHqlq0e;qd{LEgY*rMMF4Gt{TT1$|;~gug!U)em3s@HH>K z@&oX|SDyS@4*nC%LHePARd8+_8;jV+VLy8cMK>=mFSTV*pJ7c)P$O0yc&#N;S6646 z5pTKzvz>`(fiC0sE2@#k=_19&#o5`}!a_1SH(u+^?$P0oHwBEnQe%H$1bGGohzSd4 zzP5@QYL{J#mF@&(8L65 z>s!S3-V=r1W$Y1)W02V#IXpZZF#d;W2a`gP%%F@Vuqud@+4hI#yBY^4?1F-3BY1SH zkVEr)%FOHRf)a_Wq^v9M^y>L{B+2|ddw=h3f|LF|ebg=7IoL3hGVaw;8lvgWDwSeQ zH~9BV_ovWub92vMyhuHkw*T)r0$8Zh`m==%P)Y zppZqXUr}y!W>Lk)1=+}!p6C@Qxnb7)uw(GI81|Wp-0ShfFXfjcB_%6H zMn*);-V#fvor1?34fab=d#0w;{rvor0~Rb|npww838r)ZR&`6H`3qKrb*L@qRg=t2$bnKig>gzxF6n9=Zl#rd9`@N8~R=A1Iv_naA zH6w_Jz{Ow4|RxyK5Dg2?! z+y+gKUU6r%bFKu#S_u%kgW-T-@mqQhV%PzqvRFlT_a)VYSFJ1-&44CdSso_DPNV5W zYWj~<%yO7A!+x4z&=7}XLMrUH{{$O}BQupnj2}E~ANCs>9hcFn z*jf1>BF|<{+!i%TBw4D$E_%>M+dfJ(GUaeM)$p{dn_K0~3<+SVOP(J~sqQTHPiuD8 zr?*5oX2vR##OWL$vaVar7ZCAOo|` zy1c1_^i7sS&O13VMVP2LB%>f|6wIco^(6U!h=7L#1Oy$psxo^kL!P?ib2!QKCG;~dkD#Sj(7@+JB26ha zU}2tL;N5RrGCNWXz)odxs<~WGi2Es0jn@I&jc3!T@g;?!#&d?ZGItAA-kT zO?@sGVnZ}YjN~Z!QOy_NkS0S||G1N?gg%()lq25W-p+Q@8DTMHm{XNXJq+H5D8FJU zI2qbyXvm0E0~{-AjEuS&v-1;RpIfmCECXRDwfxZ30G8 zKYTb6mcMY}Li&S;oQ^^f*oZrjo^tH|=?H7ODEt*;tpZV6i;Q0-zJ4|L-31~72bD_o zyf;5Vr%1_!jGIC#{=u8ge)@Fd;NW14^}YkI-=agD0(%N%Whir=b6ozc1De*S@C}tX8JiH$4x;6EW<7aQbwyPoYyiA|j&4n@ ztgH+eDeSj%bd-;ci?a_5)QF9ZO>MkVae>JAzPW#1ai%jZfGsmmev=M+Cte48%FO&# z_Fts8*ei?aWu1-6JV!s4)+iP>5Tnj`{+tjM6_s63AVxj5?b8IweAjKoNI5yVV`-*( zdh5f=VQ15e=OdlnO3AMdqSW1Um(KbG#)VsEZKjuup3qvD8_R}OcJJN|ALI8g&3HOa z2j10=XolhujDl0(lTTztGv^1Hz{)dJ*6gb^->U>D;Z8?-g=8ST`^_@Ulj9$Pucmg* z3*chJqU*taJZ$iZha5g}|CZg~UFY@YWS0=vZTK?^OQSeeg`#MN7$P)K&I*7dzZ5v}+7F^$_jvpHo+`0@qjyQHsFS9xm$7Lm2K3@L?+7(fNZC-dD>sKK`EYu3!acbze z+)A8VQO{RcjU73wJIsOEn5Dyzd82i9=4wDf8P3pcVs@ zLdDJW2$lClxjqBG!&kD%%D(6}C`4qmwpo^9Bw%6J?OIMLnOtm?nVFegQX;u--8xf( z{+=wW!nY~@**}3@5B|1@>8R)VxzcIBbqPu57Zo9ffbQ?vo-}}_hx(21@Dukp0Z2?U zn^Udn)f0m)=*Ei|M^z=L2xOVfJ)b`-lcxt4wk-5bP(&97nf$mvctvp`B*;rObfO2A z++u!KRBCvf4OwHpn#I3p9x3<4s&ErnW}0<24VKMyX{qX-{x}(M!yVS7x4?G>fOtrq zX6J*>xorWDH04tF=%!dn3x;Jw;aT8}%yQqP@S`4{b!kj#P54pDdLn7BL_6NIezgFrW?c|VoM@w08`hzhK zwaJpgz2-)$Cgl<G4Xr=oJ4ivG5Yimu^Q6ov3$W&%9CoLMoa ze8`bya$sK~dP4@ZBNa?>28my z3U*AtG-den!!70A%C(J(tIL0w%j8gWtwSc73)i>ed&;96i*xt(K0uyh%b+d;`sT=< zy8Ky4Izd_ycXraM!sGXaHr>I(1T}W^IUB~yUXxXLu|f6EV*oh%xn)-RXwIjJ8MA2( z;M?9uMkMfi*>pTAX`4|z;9AC=@b_|Ztxrq^Xh9wCN)%)K4OdiPfPw0$AAxJJ zu=XV(Y;MA_nT)WBGdJy)tJ1)q|Kql5K^-Wm`-bxVgRw5bCu{Fzb zOcalp9Ew=|N)&;eW;m**oYBNf(7)B&Kaq{tvga*--gcvYFqxt%5pAQZ(Z{6=3wN@~ zgcMzIVPU>CZy*u!yQ~Fe`}RC2n{>vcWk-?k?%HQJ8m|xlEfCZj$I`go3O3Aug~u(% zA9SI^A1>xpc~eGTvt#lpQnT$_;{2pfU`k*3lH{fg5+7~p`|g$^BYX4mcW78VhMEp%Fn=AE+nJbVv37rHoQQfa|= zh^SNaHh|v~rBpRYXG%L-=m@tk_dHLTm>QHEV{k$^ZyM}(AvYWC1RvSfCi*{`+HRfl z(?>6#Y2O^+VCoh%CvUkTtpynZ@S{SZ?Kbe*u2WLrHN3A2B=o2J!X&&vB?eWwFM+ge zxV=B}>E$t3?&~#J*?K$}u_zez-z^Kq_joY2;=%X^1w$OA!WWR{71aq)G%a1v%p0;67TXx z6T>vn%F6kOm`0cWh7wwc6Hf>JBt8U8FSVh}3J04Hem-{`Y(h)VSI=%(wNMN0MFULe z$#du|%Kd1U2ef6jsp9m9reVw}7OyMFiUJ?UovA>0tQ+Xz12r;$w|IAS&Re_cMz8I- z8>JYzZtBo>yr1Da<1LWQ6Bu(~cHFafuRc^u5<@!Ia<6l)B{YMeo}kkDV~V&4YDqW1 zLPzXC3E;-0%x&Mid9xi%HZWrJ_Vzx6+ISN+nQm@-;RbZ1iABwW6HwdJU-;)*QlSQ? zH7eGs$knR28iok|T;mG?SdZ58;*)h}`14bq=}ptM>~rVNQA8iqakI|O&b5>-HVy1> zMvH4@-w1`qpc=%H1hw|guU+xy$lz3O;aS`=Naf_W51Y|*CwcoqPQik0!;|WFQ(Oa2 zXCy9c+9SeJ>3uONtxI1IehiITCUnrmwK zIeA(caCk!H$8tcb$5j;0eVx$K_!aXj-tq)wf;~QQYinyW_L?-bjeOkhV8bNyFNASo zruc4J`yG?yQWFpf<1@=Wn1A8vb8h~l`ZnK}#>K@|3(s<+tdf2IUZ?ayT8IVRiQFdF zGY~g#RQqjrBq{{cpFBBc5o;sPG)YDNgM2jrsSSp?uwkc?X_JKmtNH9G-JK9#d+;LW z%}KESI0}+lx-@G#jLA&=89XGZxzO8*SK_W~OF{ToBK*&405|^ca`FH8z($bsV0jzV zCzK^3yuCz&KU{wjeLN!iXE@OtuYu%SoD(HcOaF2ylXP8!$;D7c&51XAK&^s90)nicFJb!o!70sM~Dn?F4F{h%Y8fj!gr@PwWB;!AoqGEr} zF&LH4W_}MkABc(2z@kuqfOLv90OsvPt`&8FiEO@)IN$fy8+d$%PnMLD(g!Ju&*!(c zw?`pKq^%8!l;|U$Mb-q-=6U;YYiFmFzz9rJXyVq`>J*I*`5*$a*CXOlvoqLx-pcBR z3#g+fl^Ej{`yq))3~Nbg>1c&Ke}8}b%a;}M^7BnrjevO|U%|iaJw5kBTT*S-krFcb z{@aF|SMmM|O}(LOGR=3PaqZ;Eli7KBetr9C0`THN!45cC`Zqj~t4bE;XTMQP$BAhh zc+Pfha8lXDSVu`2ndR?nMtE9U+Lc_(Y$lVk%U!or2ww>aP#`9%N+jLw z+FXzxhI;}Hc&L^TBiOj=!Y$ApqgV2Pz>rb-iW*v4H@ZE< zV7Er+Tr_Ne?2|lthl>wZO=$M(S_9?t&*|CyLrEaGfoS%|IoFOzR07@94yj#gYV5`| z1GeD^O=m{2ZP;)9>gy;Fpp#9Iv>`CTqcsX9ypKR983?eSvw}Sneuc?ox}N>tu?I;; zQ&W?jWeCevi*zcOb>L%>guuwLG+~*8309z-tviqYF92nZYo=2?fJ<*z(HQEHVLVw= zN}Bi-=WeDVDQMzm=H+#%CdTcA@i)9;PN`<;?+zBR>66{_<4q1ChIRVDD{(=$&<=5pE~^{QawgJX5|MS;TW+0Da{C z{rgCjh4X=*$g}M%9li*3m&R!5i5J&5)k|wcH(zUw8G*n)ThCrpfdBA0oWAeS^we2- z(6DgjGUq*V_VwSo-X4+{PcOhH+mq;gea29vnwy)E7B6ZY&|Po;OL=#O+lfgYue$vf z!T_^$tU(h6A9PmG5#94O1v@}v40i7KN%qi|5M&nwL9g}s#XIO5`f*)A93l$kuky{C z(;Wq#?0S1>$UspvYrmCvu#!J@NFEe4P)XF2d#r72n9scx>rB_r^3_KsdJ2Lzc7l>W zq{@EUe53pwSu(NXZcz60dxqF}wB6OK18>kKte;RdW}W^k54b})udDo-vz=c&3C2Sz zUoH{lO-}liOuW0mUNbT=F#%Q8)5ixwLjK(K+Z`9N4Pn{&T*YXR?p@W`Px*m2bPB1_ zZH*sTRCc+sB(Er&G;t?jzRFT-Y$!Kix^JRjrdrKDaJu)3YN^bhz2@LE-9sR?Si)@X zMil@J`H-YwTFanz_Nkg00r3BAsHk{-V)B9sn0!cX&t?#1!n8P7WREnn{f1F z5BwhUI4i3Xrg(N6{6c{ab9?tWq)DNlXn-C~2{AfQ;MHT$xy?{Y7oMtt>AdGtbEao+ z?d|i{*3r;sG&}nuT{_hJ%SfD#D~3QEWupJ~^$M)~U*#$Ie))(6Uo0)E3rs0Z_&-1x zCiao2O_ll?#t{sAx()IJuGW7*-oZ8W5|9p;-$$tJaA~67nt*Ji0>Po+p6JEOXlt<8 z`+j}>tLqRhzQX2f${-wkrHijr2{8DNE)&MXrW&ipFH?R-31O&jtoP)o?X`aa6^c@p literal 0 HcmV?d00001 diff --git a/apriltag/src/test/python/test_detection.py b/apriltag/src/test/python/test_detection.py new file mode 100644 index 0000000000..c323120a91 --- /dev/null +++ b/apriltag/src/test/python/test_detection.py @@ -0,0 +1,119 @@ +import cv2 +import robotpy_apriltag +from wpimath.geometry import Transform3d + +import math +import pathlib +import pytest + + +def test_point(): + point = robotpy_apriltag.AprilTagDetection.Point() + + x, y = point + + assert x == 0 + assert y == 0 + + +def _load_grayscale_image(fname): + full_path = pathlib.Path(__file__).parent / fname + img = cv2.imread(str(full_path)) + return cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + + +def test_1(): + detector = robotpy_apriltag.AprilTagDetector() + assert detector.addFamily("tag16h5") + assert detector.addFamily("tag36h11") + + img = _load_grayscale_image("tag1_640_480.jpg") + results = detector.detect(img) + + assert len(results) == 1 + assert results[0].getFamily() == "tag36h11" + assert results[0].getId() == 1 + assert results[0].getHamming() == 0 + + estimator = robotpy_apriltag.AprilTagPoseEstimator( + robotpy_apriltag.AprilTagPoseEstimator.Config(0.2, 500, 500, 320, 240) + ) + + est = estimator.estimateOrthogonalIteration(results[0], 50) + assert est.pose2 == Transform3d() + pose = estimator.estimate(results[0]) + assert est.pose1 == pose + + +def test_pose_rotated_x(): + """ + This tag is rotated such that the top is closer to the camera than the bottom. In the camera + frame, with +x to the right, this is a rotation about +X by 45 degrees. + """ + + detector = robotpy_apriltag.AprilTagDetector() + assert detector.addFamily("tag16h5") + + img = _load_grayscale_image("tag2_45deg_X.png") + results = detector.detect(img) + + assert len(results) == 1 + + estimator = robotpy_apriltag.AprilTagPoseEstimator( + robotpy_apriltag.AprilTagPoseEstimator.Config( + 0.2, 500, 500, img.shape[1] / 2.0, img.shape[0] / 2.0 + ) + ) + est = estimator.estimateOrthogonalIteration(results[0], 50) + assert pytest.approx(est.pose1.rotation().x, abs=0.1) == math.radians(45) + assert pytest.approx(est.pose1.rotation().y, abs=0.1) == math.radians(0) + assert pytest.approx(est.pose1.rotation().z, abs=0.1) == math.radians(0) + + +def test_pose_rotated_y(): + """ + This tag is rotated such that the right is closer to the camera than the left. In the camera + frame, with +y down, this is a rotation of 45 degrees about +y. + """ + + detector = robotpy_apriltag.AprilTagDetector() + assert detector.addFamily("tag16h5") + + img = _load_grayscale_image("tag2_45deg_y.png") + results = detector.detect(img) + + assert len(results) == 1 + + estimator = robotpy_apriltag.AprilTagPoseEstimator( + robotpy_apriltag.AprilTagPoseEstimator.Config( + 0.2, 500, 500, img.shape[1] / 2.0, img.shape[0] / 2.0 + ) + ) + est = estimator.estimateOrthogonalIteration(results[0], 50) + assert pytest.approx(est.pose1.rotation().x, abs=0.1) == math.radians(0) + assert pytest.approx(est.pose1.rotation().y, abs=0.1) == math.radians(45) + assert pytest.approx(est.pose1.rotation().z, abs=0.1) == math.radians(0) + + +def test_pose_straight_on(): + """ + This tag is facing right at the camera -- no rotation should be observed. + """ + + detector = robotpy_apriltag.AprilTagDetector() + assert detector.addFamily("tag16h5") + + img = _load_grayscale_image("tag2_16h5_straight.png") + results = detector.detect(img) + + assert len(results) == 1 + + estimator = robotpy_apriltag.AprilTagPoseEstimator( + robotpy_apriltag.AprilTagPoseEstimator.Config( + 0.2, 500, 500, img.shape[1] / 2.0, img.shape[0] / 2.0 + ) + ) + est = estimator.estimateOrthogonalIteration(results[0], 50) + assert pytest.approx(est.pose1.rotation().x, abs=0.1) == math.radians(0) + assert pytest.approx(est.pose1.rotation().y, abs=0.1) == math.radians(0) + assert pytest.approx(est.pose1.rotation().z, abs=0.1) == math.radians(0) diff --git a/datalog/.styleguide b/datalog/.styleguide index 718d2404f4..3916f30045 100644 --- a/datalog/.styleguide +++ b/datalog/.styleguide @@ -6,6 +6,11 @@ cppSrcFileInclude { \.cpp$ } +generatedFileExclude { + src/main/python/ + src/test/python/ +} + licenseUpdateExclude { examples/printlog } diff --git a/datalog/src/main/python/native-pyproject.toml b/datalog/src/main/python/native-pyproject.toml new file mode 100644 index 0000000000..a10fb32e04 --- /dev/null +++ b/datalog/src/main/python/native-pyproject.toml @@ -0,0 +1,40 @@ +[build-system] +build-backend = "hatchling.build" +requires = [ + "hatchling", + "hatch-nativelib~=0.2.0", + "hatch-robotpy~=0.2.1", + "robotpy-native-wpiutil==2027.0.0a2", +] + +[project] +name = "robotpy-native-datalog" +version = "2027.0.0a2" +description = "WPILib Utility Library" +license = "BSD-3-Clause" + +dependencies = [ + "robotpy-native-wpiutil==2027.0.0a2", +] + +[tool.hatch.build.targets.wheel] +packages = ["src/native"] + + +[[tool.hatch.build.hooks.robotpy.maven_lib_download]] +artifact_id = "datalog-cpp" +group_id = "edu.wpi.first.datalog" +repo_url = "https://frcmaven.wpi.edu/artifactory/release-2027" +version = "2027.0.0-alpha-2" + +extract_to = "src/native/datalog" +libs = ["datalog"] + +[[tool.hatch.build.hooks.nativelib.pcfile]] +pcfile = "src/native/datalog/robotpy-native-datalog.pc" +name = "datalog" + +includedir = "src/native/datalog/include" +libdir = "src/native/datalog/lib" +shared_libraries = ["datalog"] +requires = ["robotpy-native-wpiutil"] \ No newline at end of file diff --git a/datalog/src/main/python/pyproject.toml b/datalog/src/main/python/pyproject.toml new file mode 100644 index 0000000000..206936f433 --- /dev/null +++ b/datalog/src/main/python/pyproject.toml @@ -0,0 +1,63 @@ +[build-system] +build-backend = "hatchling.build" +requires = [ + "semiwrap~=0.1.7", + "hatch-meson~=0.1.0b2", + "hatchling", + "robotpy-native-datalog==2027.0.0a2", + "robotpy-wpiutil==2027.0.0a2" +] + +[project] +name = "robotpy-wpilog" +version = "2027.0.0a2" +description = "Binary wrapper for FRC wpilog library" +authors = [ + {name = "RobotPy Development Team", email = "robotpy@googlegroups.com"}, +] +license = "BSD-3-Clause" +dependencies = [ + "robotpy-native-datalog==2027.0.0a2", + "robotpy-wpiutil==2027.0.0a2" +] + +[project.urls] +"Source code" = "https://github.com/robotpy/mostrobotpy" + + +[tool.hatch.build.hooks.robotpy] +version_file = "wpilog/version.py" + +[tool.hatch.build.hooks.semiwrap] + +[tool.hatch.build.hooks.meson] + +[tool.hatch.build.targets.wheel] +packages = ["wpilog"] + + +[tool.semiwrap] +update_init = [ + "wpilog" +] +scan_headers_ignore = [ + # wpi/datalog + "wpi/datalog/DataLog_c.h", + "wpi/datalog/DataLogReaderThread.h", + "wpi/datalog/FileLogger.h", +] + +[tool.semiwrap.extension_modules."wpilog._wpilog"] +name = "wpilog" +wraps = ["robotpy-native-datalog"] +depends = ["wpiutil"] + +[tool.semiwrap.extension_modules."wpilog._wpilog".headers] +# wpi/datalog +DataLog = "wpi/datalog/DataLog.h" +DataLogBackgroundWriter = "wpi/datalog/DataLogBackgroundWriter.h" +DataLogReader = "wpi/datalog/DataLogReader.h" +# DataLogReaderThread = "wpi/datalog/DataLogReaderThread.h" +DataLogWriter = "wpi/datalog/DataLogWriter.h" +# DataLog_c = "wpi/datalog/DataLog_c.h" +# FileLogger = "wpi/datalog/FileLogger.h" \ No newline at end of file diff --git a/datalog/src/main/python/semiwrap/DataLog.yml b/datalog/src/main/python/semiwrap/DataLog.yml new file mode 100644 index 0000000000..712d364d50 --- /dev/null +++ b/datalog/src/main/python/semiwrap/DataLog.yml @@ -0,0 +1,431 @@ +# defaults: +# ignore: true +# report_ignored_missing: false +enums: + ControlRecordType: +classes: + wpi::log::DataLog: + attributes: + kBlockSize: + s_defaultMessageLog: + ignore: true + m_msglog: + ignore: true + methods: + DataLog: + ignore: true + Flush: + Pause: + Resume: + Stop: + HasSchema: + AddSchema: + overloads: + std::string_view, std::string_view, std::span, int64_t: + std::string_view, std::string_view, std::string_view, int64_t: + AddProtobufSchema: + ignore: true + AddStructSchema: + param_override: + info: + name: type + cpp_code: | + [](DataLog &self, const py::type &t, int64_t timestamp) { + WPyStructInfo info(t); + return self.AddStructSchema(info, timestamp); + } + + Start: + Finish: + SetMetadata: + AppendRaw: + AppendRaw2: + AppendBoolean: + AppendInteger: + AppendFloat: + AppendDouble: + AppendString: + AppendBooleanArray: + overloads: + int, std::span, int64_t: + int, std::span, int64_t: + ignore: true + int, std::span, int64_t: + ignore: true + AppendIntegerArray: + AppendFloatArray: + AppendDoubleArray: + AppendStringArray: + overloads: + int, std::span, int64_t: + ignore: true + int, std::span, int64_t: + int, std::span, int64_t: + ignore: true + StartFile: + FlushBufs: + ignore: true + ReleaseBufs: + ignore: true + BufferHalfFull: + BufferFull: + wpi::log::DataLogEntry: + force_no_trampoline: true + methods: + SetMetadata: + Finish: + wpi::log::DataLogValueEntryImpl: + template_params: + - T + force_no_trampoline: true + attributes: + m_mutex: + ignore: true + m_lastValue: + ignore: true + methods: + HasLastValue: + GetLastValue: + DataLogValueEntryImpl: + overloads: + "": + ignore: true + DataLog&, std::string_view, std::string_view, std::string_view, int64_t: + wpi::log::RawLogEntry: + force_no_trampoline: true + attributes: + kDataType: + methods: + RawLogEntry: + overloads: + "": + ignore: true + DataLog&, std::string_view, int64_t: + DataLog&, std::string_view, std::string_view, int64_t: + DataLog&, std::string_view, std::string_view, std::string_view, int64_t: + Append: + Update: + wpi::log::BooleanLogEntry: + force_no_trampoline: true + attributes: + kDataType: + methods: + BooleanLogEntry: + overloads: + "": + ignore: true + DataLog&, std::string_view, int64_t: + DataLog&, std::string_view, std::string_view, int64_t: + Append: + Update: + wpi::log::IntegerLogEntry: + force_no_trampoline: true + attributes: + kDataType: + methods: + IntegerLogEntry: + overloads: + "": + ignore: true + DataLog&, std::string_view, int64_t: + DataLog&, std::string_view, std::string_view, int64_t: + Append: + Update: + wpi::log::FloatLogEntry: + force_no_trampoline: true + attributes: + kDataType: + methods: + FloatLogEntry: + overloads: + "": + ignore: true + DataLog&, std::string_view, int64_t: + DataLog&, std::string_view, std::string_view, int64_t: + Append: + Update: + wpi::log::DoubleLogEntry: + force_no_trampoline: true + attributes: + kDataType: + methods: + DoubleLogEntry: + overloads: + "": + ignore: true + DataLog&, std::string_view, int64_t: + DataLog&, std::string_view, std::string_view, int64_t: + Append: + Update: + wpi::log::StringLogEntry: + force_no_trampoline: true + attributes: + kDataType: + methods: + StringLogEntry: + overloads: + "": + ignore: true + DataLog&, std::string_view, int64_t: + DataLog&, std::string_view, std::string_view, int64_t: + DataLog&, std::string_view, std::string_view, std::string_view, int64_t: + Append: + Update: + wpi::log::BooleanArrayLogEntry: + force_no_trampoline: true + attributes: + kDataType: + methods: + BooleanArrayLogEntry: + overloads: + "": + ignore: true + DataLog&, std::string_view, int64_t: + DataLog&, std::string_view, std::string_view, int64_t: + Append: + overloads: + std::span, int64_t: + std::initializer_list, int64_t: + ignore: true + std::span, int64_t: + ignore: true + std::initializer_list, int64_t: + ignore: true + std::span, int64_t: + ignore: true + Update: + overloads: + std::span, int64_t: + std::initializer_list, int64_t: + ignore: true + std::span, int64_t: + std::initializer_list, int64_t: + ignore: true + std::span, int64_t: + ignore: true + wpi::log::IntegerArrayLogEntry: + force_no_trampoline: true + attributes: + kDataType: + methods: + IntegerArrayLogEntry: + overloads: + "": + ignore: true + DataLog&, std::string_view, int64_t: + DataLog&, std::string_view, std::string_view, int64_t: + Append: + overloads: + std::span, int64_t: + std::initializer_list, int64_t: + ignore: true + Update: + overloads: + std::span, int64_t: + std::initializer_list, int64_t: + ignore: true + wpi::log::FloatArrayLogEntry: + force_no_trampoline: true + attributes: + kDataType: + methods: + FloatArrayLogEntry: + overloads: + "": + ignore: true + DataLog&, std::string_view, int64_t: + DataLog&, std::string_view, std::string_view, int64_t: + Append: + overloads: + std::span, int64_t: + std::initializer_list, int64_t: + ignore: true + Update: + overloads: + std::span, int64_t: + std::initializer_list, int64_t: + ignore: true + wpi::log::DoubleArrayLogEntry: + force_no_trampoline: true + attributes: + kDataType: + methods: + DoubleArrayLogEntry: + overloads: + "": + ignore: true + DataLog&, std::string_view, int64_t: + DataLog&, std::string_view, std::string_view, int64_t: + Append: + overloads: + std::span, int64_t: + std::initializer_list, int64_t: + ignore: true + Update: + overloads: + std::span, int64_t: + std::initializer_list, int64_t: + ignore: true + wpi::log::StringArrayLogEntry: + force_no_trampoline: true + attributes: + kDataType: + methods: + StringArrayLogEntry: + overloads: + "": + ignore: true + DataLog&, std::string_view, int64_t: + DataLog&, std::string_view, std::string_view, int64_t: + Append: + overloads: + std::span, int64_t: + ignore: true + std::span, int64_t: + std::initializer_list, int64_t: + ignore: true + Update: + overloads: + std::span, int64_t: + ignore: true + std::span, int64_t: + std::initializer_list, int64_t: + ignore: true + wpi::log::StructLogEntry: + force_no_trampoline: true + template_params: + - T + - I + methods: + StructLogEntry: + overloads: + "": + ignore: true + DataLog&, std::string_view, I..., int64_t: + param_override: + info: + name: type + cpp_code: | + [](DataLog &log, std::string_view name, const py::type &t, int64_t timestamp) { + WPyStructInfo info(t); + return std::make_shared>(log, name, info, timestamp); + } + DataLog&, std::string_view, std::string_view, I..., int64_t: + param_override: + info: + name: type + cpp_code: | + [](DataLog &log, std::string_view name, std::string_view metadata, const py::type &t, int64_t timestamp) { + WPyStructInfo info(t); + return std::make_shared>(log, name, metadata, info, timestamp); + } + Append: + Update: + HasLastValue: + GetLastValue: + wpi::log::StructArrayLogEntry: + force_no_trampoline: true + template_params: + - T + - I + methods: + StructArrayLogEntry: + overloads: + "": + ignore: true + DataLog&, std::string_view, I..., int64_t: + param_override: + info: + name: type + cpp_code: | + [](DataLog &log, std::string_view name, const py::type &t, int64_t timestamp) { + WPyStructInfo info(t); + return std::make_shared>(log, name, info, timestamp); + } + DataLog&, std::string_view, std::string_view, I..., int64_t: + param_override: + info: + name: type + cpp_code: | + [](DataLog &log, std::string_view name, std::string_view metadata, const py::type &t, int64_t timestamp) { + WPyStructInfo info(t); + return std::make_shared>(log, name, metadata, info, timestamp); + } + Append: + overloads: + U&&, int64_t: + ignore: true + std::span, int64_t: + Update: + HasLastValue: + GetLastValue: + wpi::log::ProtobufLogEntry: + force_no_trampoline: true + ignore: true + methods: + ProtobufLogEntry: + overloads: + "": + DataLog&, std::string_view, int64_t: + DataLog&, std::string_view, std::string_view, int64_t: + Append: + Update: + HasLastValue: + GetLastValue: + +templates: + StructLogEntry: + qualname: wpi::log::StructLogEntry + params: + - WPyStruct + - WPyStructInfo + StructArrayLogEntry: + qualname: wpi::log::StructArrayLogEntry + params: + - WPyStruct + - WPyStructInfo + + _RawLogEntryImpl: + qualname: wpi::log::DataLogValueEntryImpl + params: + - std::vector + _BooleanLogEntryImpl: + qualname: wpi::log::DataLogValueEntryImpl + params: + - bool + _IntegerLogEntryImpl: + qualname: wpi::log::DataLogValueEntryImpl + params: + - int64_t + _FloatLogEntryImpl: + qualname: wpi::log::DataLogValueEntryImpl + params: + - float + _DoubleLogEntryImpl: + qualname: wpi::log::DataLogValueEntryImpl + params: + - double + _StringLogEntryImpl: + qualname: wpi::log::DataLogValueEntryImpl + params: + - std::string + _BooleanArrayLogEntryImpl: + qualname: wpi::log::DataLogValueEntryImpl + params: + - std::vector + _IntegerArrayLogEntryImpl: + qualname: wpi::log::DataLogValueEntryImpl + params: + - std::vector + _FloatArrayLogEntryImpl: + qualname: wpi::log::DataLogValueEntryImpl + params: + - std::vector + _DoubleArrayLogEntryImpl: + qualname: wpi::log::DataLogValueEntryImpl + params: + - std::vector + _StringArrayLogEntryImpl: + qualname: wpi::log::DataLogValueEntryImpl + params: + - std::vector diff --git a/datalog/src/main/python/semiwrap/DataLogBackgroundWriter.yml b/datalog/src/main/python/semiwrap/DataLogBackgroundWriter.yml new file mode 100644 index 0000000000..72aea6bb50 --- /dev/null +++ b/datalog/src/main/python/semiwrap/DataLogBackgroundWriter.yml @@ -0,0 +1,16 @@ +classes: + wpi::log::DataLogBackgroundWriter: + methods: + DataLogBackgroundWriter: + overloads: + std::string_view, std::string_view, double, std::string_view: + wpi::Logger&, std::string_view, std::string_view, double, std::string_view: + ignore: true + std::function data)>, double, std::string_view: + wpi::Logger&, std::function data)>, double, std::string_view: + ignore: true + SetFilename: + Flush: + Pause: + Resume: + Stop: diff --git a/datalog/src/main/python/semiwrap/DataLogReader.yml b/datalog/src/main/python/semiwrap/DataLogReader.yml new file mode 100644 index 0000000000..558f7abc82 --- /dev/null +++ b/datalog/src/main/python/semiwrap/DataLogReader.yml @@ -0,0 +1,332 @@ +classes: + wpi::log::StartRecordData: + attributes: + entry: + access: readonly + name: + access: readonly + type: + access: readonly + metadata: + access: readonly + wpi::log::MetadataRecordData: + attributes: + entry: + access: readonly + metadata: + access: readonly + wpi::log::DataLogRecord: + methods: + DataLogRecord: + overloads: + "": + ignore: true + int, int64_t, std::span: + ignore: true + GetEntry: + GetTimestamp: + GetSize: + GetRaw: + no_release_gil: true + cpp_code: | + [](const DataLogRecord *self) { + auto data = self->GetRaw(); + return py::bytes((char*)data.data(), data.size()); + } + IsControl: + IsStart: + IsFinish: + IsSetMetadata: + GetStartData: + no_release_gil: true + param_override: + out: + ignore: true + doc: | + Decodes a start control record. Raises TypeError on error. + cpp_code: | + [](const DataLogRecord *self) { + auto ptr = std::make_unique(); + if (!self->GetStartData(ptr.get())) { + throw py::type_error("not a start record"); + } + return ptr; + } + return_value_policy: reference_internal + GetFinishEntry: + no_release_gil: true + param_override: + out: + ignore: true + doc: | + Decodes a finish control record. Raises TypeError on error. + cpp_code: | + [](const DataLogRecord *self) { + int value; + if (!self->GetFinishEntry(&value)) { + throw py::type_error("not a finish entry"); + } + return value; + } + GetSetMetadataData: + no_release_gil: true + param_override: + out: + ignore: true + doc: | + Decodes a set metadata control record. Raises TypeError on error. + cpp_code: | + [](const DataLogRecord *self) { + auto ptr = std::make_unique(); + if (!self->GetSetMetadataData(ptr.get())) { + throw py::type_error("not a metadata control record"); + } + return ptr; + } + return_value_policy: reference_internal + GetBoolean: + no_release_gil: true + param_override: + value: + ignore: true + doc: | + Decodes a data record as a boolean. Note if the data type (as indicated in + the corresponding start control record for this entry) is not "boolean", + invalid results may be returned or TypeError will be raised. + cpp_code: | + [](const DataLogRecord *self) { + bool value; + if (!self->GetBoolean(&value)) { + throw py::type_error("not a boolean"); + } + return value; + } + GetInteger: + no_release_gil: true + param_override: + value: + ignore: true + doc: | + Decodes a data record as an integer. Note if the data type (as indicated in + the corresponding start control record for this entry) is not "int64", + invalid results may be returned or TypeError will be raised. + cpp_code: | + [](const DataLogRecord *self) { + int64_t value; + if (!self->GetInteger(&value)) { + throw py::type_error("not an integer"); + } + return value; + } + GetFloat: + no_release_gil: true + param_override: + value: + ignore: true + doc: | + Decodes a data record as a float. Note if the data type (as indicated in + the corresponding start control record for this entry) is not "float", + invalid results may be returned or TypeError will be raised. + cpp_code: | + [](const DataLogRecord *self) { + float value; + if (!self->GetFloat(&value)) { + throw py::type_error("not a float"); + } + return value; + } + GetDouble: + no_release_gil: true + param_override: + value: + ignore: true + doc: | + Decodes a data record as a double. Note if the data type (as indicated in + the corresponding start control record for this entry) is not "double", + invalid results may be returned or TypeError will be raised. + cpp_code: | + [](const DataLogRecord *self) { + double value; + if (!self->GetDouble(&value)) { + throw py::type_error("not a double"); + } + return value; + } + GetString: + no_release_gil: true + param_override: + value: + ignore: true + doc: | + Decodes a data record as a string. Note if the data type (as indicated in + the corresponding start control record for this entry) is not "string", + invalid results may be returned or TypeError will be raised. + cpp_code: | + [](const DataLogRecord *self) { + std::string_view value; + if (!self->GetString(&value)) { + throw py::type_error("not a string"); + } + return value; + } + GetBooleanArray: + no_release_gil: true + param_override: + arr: + ignore: true + doc: | + Decodes a data record as a boolean array. Note if the data type (as + indicated in the corresponding start control record for this entry) is not + "boolean[]", invalid results may be returned or a TypeError may be raised. + cpp_code: | + [](const DataLogRecord *self) { + std::vector arr; + if (!self->GetBooleanArray(&arr)) { + throw py::type_error("not a boolean array"); + } + py::list l(arr.size()); + for (size_t i = 0; i < arr.size(); i++) { + auto b = py::bool_(arr[i]); + PyList_SET_ITEM(l.ptr(), i, b.release().ptr()); + } + return l; + } + GetIntegerArray: + no_release_gil: true + param_override: + arr: + ignore: true + doc: | + Decodes a data record as an integer array. Note if the data type (as + indicated in the corresponding start control record for this entry) is not + "int64[]", invalid results may be returned or a TypeError may be raised. + cpp_code: | + [](const DataLogRecord *self) { + std::vector arr; + if (!self->GetIntegerArray(&arr)) { + throw py::type_error("not an integer array"); + } + return arr; + } + GetFloatArray: + no_release_gil: true + param_override: + arr: + ignore: true + doc: | + Decodes a data record as a float array. Note if the data type (as + indicated in the corresponding start control record for this entry) is not + "float[]", invalid results may be returned or a TypeError may be raised. + cpp_code: | + [](const DataLogRecord *self) { + std::vector arr; + if (!self->GetFloatArray(&arr)) { + throw py::type_error("not a float array"); + } + return arr; + } + GetDoubleArray: + no_release_gil: true + param_override: + arr: + ignore: true + doc: | + Decodes a data record as a double array. Note if the data type (as + indicated in the corresponding start control record for this entry) is not + "double[]", invalid results may be returned or a TypeError may be raised. + cpp_code: | + [](const DataLogRecord *self) { + std::vector arr; + if (!self->GetDoubleArray(&arr)) { + throw py::type_error("not a double array"); + } + return arr; + } + GetStringArray: + no_release_gil: true + param_override: + arr: + ignore: true + doc: | + Decodes a data record as a string array. Note if the data type (as + indicated in the corresponding start control record for this entry) is not + "string[]", invalid results may be returned or a TypeError may be raised. + cpp_code: | + [](const DataLogRecord *self) { + std::vector arr; + if (!self->GetStringArray(&arr)) { + throw py::type_error("not a string array"); + } + return arr; + } + wpi::log::DataLogIterator: + ignore: true + wpi::log::DataLogReader: + typealias: + - wpi::MemoryBuffer + methods: + DataLogReader: + ignore: true + IsValid: + GetVersion: + GetExtraHeader: + GetBufferIdentifier: + begin: + ignore: true + end: + ignore: true + +inline_code: | + cls_StartRecordData + .def("__repr__", [](const wpi::log::StartRecordData &data) -> std::string { + return "StartRecordData(entry=" + std::to_string(data.entry) + ", " + "name=\"" + std::string(data.name) + "\", " + "type=\"" + std::string(data.type) + "\", " + "metadata=\"" + std::string(data.metadata) + "\")"; + }); + + cls_MetadataRecordData + .def("__repr__", [](const wpi::log::MetadataRecordData &data) -> std::string { + return "MetadataRecordData(entry=" + std::to_string(data.entry) + ", " + "metadata=\"" + std::string(data.metadata) + "\")"; + }); + + + cls_DataLogReader + .def(py::init([](const std::string &filename) { + auto mbuf = wpi::MemoryBuffer::GetFile(filename); + if (!mbuf) { + py::gil_scoped_acquire gil; + #ifdef _WIN32 + PyErr_SetFromWindowsErr(mbuf.error().value()); + #else + errno = mbuf.error().value(); + PyErr_SetFromErrno(PyExc_OSError); + #endif + throw py::error_already_set(); + } + + return std::make_shared(std::move(*mbuf)); + }), + release_gil(), py::arg("filename")) + + .def(py::init([](const py::buffer &buffer, const std::string &name) { + auto req = buffer.request(); + if (req.itemsize != 1) { + throw py::value_error("buffer must only contain bytes"); + } else if (req.ndim != 1) { + throw py::value_error("buffer must only have a single dimension"); + } + + auto mbuf = wpi::MemoryBuffer::GetMemBuffer(std::span((uint8_t*)req.ptr, req.size), name); + + { + py::gil_scoped_release gil; + return std::make_shared(std::move(mbuf)); + } + }), + py::arg("buffer"), py::arg("name") = "", + py::keep_alive<1, 2>()) + .def("__iter__", [](wpi::log::DataLogReader * that) { + return py::make_iterator(that->begin(), that->end()); + }, py::keep_alive<0,1>()); diff --git a/datalog/src/main/python/semiwrap/DataLogWriter.yml b/datalog/src/main/python/semiwrap/DataLogWriter.yml new file mode 100644 index 0000000000..716cc81df3 --- /dev/null +++ b/datalog/src/main/python/semiwrap/DataLogWriter.yml @@ -0,0 +1,28 @@ +classes: + wpi::log::DataLogWriter: + methods: + DataLogWriter: + overloads: + std::string_view, std::error_code&, std::string_view: + cpp_code: | + [](std::string_view filename, std::string_view extraHeader) { + std::error_code ec; + auto writer = std::make_unique(filename, ec, extraHeader); + if (ec) { + throw std::system_error(ec); + } + return writer; + } + param_override: + ec: + ignore: true + wpi::Logger&, std::string_view, std::error_code&, std::string_view: + ignore: true + std::unique_ptr, std::string_view: + ignore: true + wpi::Logger&, std::unique_ptr, std::string_view: + ignore: true + Flush: + Stop: + GetStream: + ignore: true diff --git a/datalog/src/main/python/src/main.cpp b/datalog/src/main/python/src/main.cpp new file mode 100644 index 0000000000..5b9f043aef --- /dev/null +++ b/datalog/src/main/python/src/main.cpp @@ -0,0 +1,6 @@ + +#include + +SEMIWRAP_PYBIND11_MODULE(m) { + initWrapper(m); +} diff --git a/datalog/src/main/python/wpilog/__init__.py b/datalog/src/main/python/wpilog/__init__.py new file mode 100644 index 0000000000..abbd3cefa2 --- /dev/null +++ b/datalog/src/main/python/wpilog/__init__.py @@ -0,0 +1,52 @@ +from . import _init__wpilog + +# autogenerated by 'semiwrap create-imports wpilog wpilog._wpilog' +from ._wpilog import ( + BooleanArrayLogEntry, + BooleanLogEntry, + ControlRecordType, + DataLog, + DataLogBackgroundWriter, + DataLogEntry, + DataLogReader, + DataLogRecord, + DataLogWriter, + DoubleArrayLogEntry, + DoubleLogEntry, + FloatArrayLogEntry, + FloatLogEntry, + IntegerArrayLogEntry, + IntegerLogEntry, + MetadataRecordData, + RawLogEntry, + StartRecordData, + StringArrayLogEntry, + StringLogEntry, + StructArrayLogEntry, + StructLogEntry, +) + +__all__ = [ + "BooleanArrayLogEntry", + "BooleanLogEntry", + "ControlRecordType", + "DataLog", + "DataLogBackgroundWriter", + "DataLogEntry", + "DataLogReader", + "DataLogRecord", + "DataLogWriter", + "DoubleArrayLogEntry", + "DoubleLogEntry", + "FloatArrayLogEntry", + "FloatLogEntry", + "IntegerArrayLogEntry", + "IntegerLogEntry", + "MetadataRecordData", + "RawLogEntry", + "StartRecordData", + "StringArrayLogEntry", + "StringLogEntry", + "StructArrayLogEntry", + "StructLogEntry", +] diff --git a/datalog/src/main/python/wpilog/py.typed b/datalog/src/main/python/wpilog/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/datalog/src/test/python/test_wpilog.py b/datalog/src/test/python/test_wpilog.py new file mode 100644 index 0000000000..594cc34109 --- /dev/null +++ b/datalog/src/test/python/test_wpilog.py @@ -0,0 +1,5 @@ +import wpilog + + +def test_existance(): + pass diff --git a/hal/.styleguide b/hal/.styleguide index 2386f43145..86d583b857 100644 --- a/hal/.styleguide +++ b/hal/.styleguide @@ -18,6 +18,9 @@ generatedFileExclude { hal/src/main/native/systemcore/rev/ UsageReporting\.h$ src/generated/main/native/cpp + + src/main/python/ + src/test/python/ } modifiableFileExclude { diff --git a/hal/src/main/python/README.md b/hal/src/main/python/README.md new file mode 100644 index 0000000000..a37a6cdade --- /dev/null +++ b/hal/src/main/python/README.md @@ -0,0 +1,7 @@ +robotpy-hal +=========== + +Python wrappers around the WPILib HAL. + +API Documentation can be found at +https://robotpy.readthedocs.io/projects/hal/en/latest diff --git a/hal/src/main/python/hal/__init__.py b/hal/src/main/python/hal/__init__.py new file mode 100644 index 0000000000..adbb2f20b6 --- /dev/null +++ b/hal/src/main/python/hal/__init__.py @@ -0,0 +1,12 @@ +from .version import version as __version__ + +# Only needed for side effects +from . import _initialize +from .exceptions import HALError + +from . import _init__wpiHal +from ._wpiHal import * + +from ._wpiHal import __hal_simulation__ + +del _init__wpiHal diff --git a/hal/src/main/python/hal/_initialize.py b/hal/src/main/python/hal/_initialize.py new file mode 100644 index 0000000000..e6fe6541e3 --- /dev/null +++ b/hal/src/main/python/hal/_initialize.py @@ -0,0 +1,9 @@ +from . import exceptions, _init__wpiHal, _wpiHal + +# Always initialize HAL here, disable extension notice because we'll handle +# that for users +_sse = getattr(_wpiHal, "setShowExtensionsNotFoundMessages", None) +if _sse: + _wpiHal.setShowExtensionsNotFoundMessages(False) + +_wpiHal.initialize(500, 0) diff --git a/hal/src/main/python/hal/exceptions.py b/hal/src/main/python/hal/exceptions.py new file mode 100644 index 0000000000..40dcb194b4 --- /dev/null +++ b/hal/src/main/python/hal/exceptions.py @@ -0,0 +1,2 @@ +class HALError(RuntimeError): + pass diff --git a/hal/src/main/python/hal/py.typed b/hal/src/main/python/hal/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/hal/src/main/python/hal/simulation/__init__.py b/hal/src/main/python/hal/simulation/__init__.py new file mode 100644 index 0000000000..9617a1f11c --- /dev/null +++ b/hal/src/main/python/hal/simulation/__init__.py @@ -0,0 +1,4 @@ +from . import _init__simulation +from ._simulation import * + +del _init__simulation diff --git a/hal/src/main/python/hal/simulation/main.cpp b/hal/src/main/python/hal/simulation/main.cpp new file mode 100644 index 0000000000..e7f1868697 --- /dev/null +++ b/hal/src/main/python/hal/simulation/main.cpp @@ -0,0 +1,30 @@ + +#include +#include + +#include "sim_cb.h" +#include "sim_value_cb.h" + +void HALSIM_ResetGlobalHandles(); + +SEMIWRAP_PYBIND11_MODULE(m) { + + py::class_ cls_SimCB(m, "SimCB"); + cls_SimCB.doc() = "Simulation callback handle"; + cls_SimCB.def("cancel", &SimCB::Cancel, py::doc("Cancel the callback")); + + py::class_ cls_SimValueCB(m, "SimValueCB"); + cls_SimValueCB.doc() = "Simulation callback handle"; + cls_SimValueCB.def("cancel", &SimValueCB::Cancel, py::doc("Cancel the callback")); + + initWrapper(m); + + m.def( + "resetGlobalHandles", + []() { +#ifndef __FRC_SYSTEMCORE__ + HALSIM_ResetGlobalHandles(); +#endif + }, + release_gil()); +} \ No newline at end of file diff --git a/hal/src/main/python/hal/simulation/resethandles.cpp b/hal/src/main/python/hal/simulation/resethandles.cpp new file mode 100644 index 0000000000..5caf333a39 --- /dev/null +++ b/hal/src/main/python/hal/simulation/resethandles.cpp @@ -0,0 +1,29 @@ + + +#ifndef __FRC_SYSTEMCORE__ +#include +#include +#include + +void HALSIM_ResetGlobalHandles() { + + // if we just reset the handles, notifiers might hang forever, + // so we just enumerate and cancel them all + auto sz = HALSIM_GetNotifierInfo(nullptr, 0); + if (sz > 0) { + struct HALSIM_NotifierInfo *info = new struct HALSIM_NotifierInfo[sz]; + HALSIM_GetNotifierInfo(info, sz); + + for (int i = 0; i < sz; i++) { + HAL_CleanNotifier(info->handle); + } + } + + hal::HandleBase::ResetGlobalHandles(); +} + +#else + +void HALSIM_ResetGlobalHandles() {} + +#endif diff --git a/hal/src/main/python/hal/simulation/sim_cb.h b/hal/src/main/python/hal/simulation/sim_cb.h new file mode 100644 index 0000000000..8b9d48d048 --- /dev/null +++ b/hal/src/main/python/hal/simulation/sim_cb.h @@ -0,0 +1,33 @@ + +#pragma once + +class SimCB { +public: + + SimCB(std::function fn, std::function cancel) : + m_fn(fn), + m_cancel(cancel) + {} + + void SetUID(int32_t uid) { + m_uid = uid; + } + + ~SimCB() { + Cancel(); + } + + void Cancel() { + if (m_valid) { + m_cancel(m_uid); + m_valid = false; + } + } + + std::function m_fn; + +private: + bool m_valid = true; + int32_t m_uid; + std::function m_cancel; +}; \ No newline at end of file diff --git a/hal/src/main/python/hal/simulation/sim_value_cb.h b/hal/src/main/python/hal/simulation/sim_value_cb.h new file mode 100644 index 0000000000..d7477caaa8 --- /dev/null +++ b/hal/src/main/python/hal/simulation/sim_value_cb.h @@ -0,0 +1,37 @@ + +#pragma once + +#include + +class SimValueCB { +public: + + using FnType = std::function; + + SimValueCB(FnType fn, std::function cancel) : + m_fn(fn), + m_cancel(cancel) + {} + + void SetUID(int32_t uid) { + m_uid = uid; + } + + ~SimValueCB() { + Cancel(); + } + + void Cancel() { + if (m_valid) { + m_cancel(m_uid); + m_valid = false; + } + } + + FnType m_fn; + +private: + bool m_valid = true; + int32_t m_uid; + std::function m_cancel; +}; \ No newline at end of file diff --git a/hal/src/main/python/hal/src/ds_types_fmt.h b/hal/src/main/python/hal/src/ds_types_fmt.h new file mode 100644 index 0000000000..04bffd2329 --- /dev/null +++ b/hal/src/main/python/hal/src/ds_types_fmt.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "hal/DriverStationTypes.h" + +namespace pybind11 { + +template <> +struct format_descriptor { + static constexpr const char c = 'B'; + static constexpr const char value[2] = {c, '\0'}; + static std::string format() { return std::string(1, c); } +}; + +} \ No newline at end of file diff --git a/hal/src/main/python/hal/src/hal.cpp b/hal/src/main/python/hal/src/hal.cpp new file mode 100644 index 0000000000..5051977439 --- /dev/null +++ b/hal/src/main/python/hal/src/hal.cpp @@ -0,0 +1,110 @@ + + +#include +#include +#include +#include + +using namespace pybind11::literals; + +static py::module_ sys_module; + +SEMIWRAP_PYBIND11_MODULE(m) { + + // Add this manually so it can be used from SimValue + py::enum_(m, "Type") + .value("UNASSIGNED", HAL_Type::HAL_UNASSIGNED) + .value("BOOLEAN", HAL_Type::HAL_BOOLEAN) + .value("DOUBLE", HAL_Type::HAL_DOUBLE) + .value("ENUM", HAL_Type::HAL_ENUM) + .value("INT", HAL_Type::HAL_INT) + .value("LONG", HAL_Type::HAL_LONG); + + // Add this manually because it would be annoying to do otherwise + py::class_(m, "Value") + .def_readonly("type", &HAL_Value::type) + .def_property_readonly("value", + [](const HAL_Value &self) -> py::object { + switch (self.type) { + case HAL_BOOLEAN: + return py::bool_(self.data.v_boolean); + case HAL_DOUBLE: + return py::float_(self.data.v_double); + case HAL_ENUM: + return py::int_(self.data.v_enum); + case HAL_INT: + return py::int_(self.data.v_int); + case HAL_LONG: + return py::int_(self.data.v_long); + default: + return py::none(); + } + } + ) + .def("__repr__", [](const HAL_Value &self) -> py::str { + switch (self.type) { + case HAL_BOOLEAN: + return ""; + case HAL_DOUBLE: + return ""; + case HAL_ENUM: + return ""; + case HAL_INT: + return ""; + case HAL_LONG: + return ""; + default: + return ""; + } + }); + + initWrapper(m); + +#ifdef __FRC_SYSTEMCORE__ + m.attr("__halplatform__") = "Systemcore"; + m.attr("__hal_simulation__") = false; +#else + m.attr("__halplatform__") = "sim"; + m.attr("__hal_simulation__") = true; + + m.def("__test_senderr", []() { + HAL_SendError(1, 2, 0, "\xfa" "badmessage", "location", "callstack", 1); + }, release_gil()); + +#endif + + // Redirect stderr to python stderr + sys_module = py::module_::import("sys"); + + HAL_SetPrintErrorImpl([](const char *line, size_t size) { + if (size == 0) { + return; + } + + py::gil_scoped_acquire lock; + PyObject *o = PyUnicode_DecodeUTF8(line, size, "replace"); + if (o == nullptr) { + PyErr_Clear(); + py::print(py::bytes(line, size), "file"_a=sys_module.attr("stderr")); + } else { + py::print(py::reinterpret_steal(o), "file"_a=sys_module.attr("stderr")); + } + }); + + // Do cleanup on module unload + static int unused; // the capsule needs something to reference + py::capsule cleanup(&unused, [](void *) { + { + py::gil_scoped_acquire lock; + HAL_SetPrintErrorImpl(nullptr); + sys_module.dec_ref(); + sys_module.release(); + } + + { + py::gil_scoped_release unlock; + HAL_Shutdown(); + } + }); + m.add_object("_cleanup", cleanup); +} diff --git a/hal/src/main/python/native-pyproject.toml b/hal/src/main/python/native-pyproject.toml new file mode 100644 index 0000000000..ec538b5ff9 --- /dev/null +++ b/hal/src/main/python/native-pyproject.toml @@ -0,0 +1,41 @@ +[build-system] +build-backend = "hatchling.build" +requires = [ + "hatchling", + "hatch-nativelib~=0.2.0", + "hatch-robotpy~=0.2.1", + "robotpy-native-wpiutil==2027.0.0a2", + "robotpy-native-ntcore==2027.0.0a2", +] + +[project] +name = "robotpy-native-wpihal" +version = "2027.0.0a2" +description = "WPILib HAL implementation" +license = "BSD-3-Clause" + +dependencies = [ + "robotpy-native-wpiutil==2027.0.0a2", + "robotpy-native-ntcore==2027.0.0a2", +] + +[tool.hatch.build.targets.wheel] +packages = ["src/native"] + +[[tool.hatch.build.hooks.robotpy.maven_lib_download]] +artifact_id = "hal-cpp" +group_id = "edu.wpi.first.hal" +repo_url = "https://frcmaven.wpi.edu/artifactory/release-2027" +version = "2027.0.0-alpha-2" + +extract_to = "src/native/wpihal" +libs = ["wpiHal"] + +[[tool.hatch.build.hooks.nativelib.pcfile]] +pcfile = "src/native/wpihal/robotpy-native-wpihal.pc" +name = "wpihal" + +includedir = "src/native/wpihal/include" +libdir = "src/native/wpihal/lib" +shared_libraries = ["wpiHal"] +requires = ["robotpy-native-wpiutil", "robotpy-native-ntcore"] diff --git a/hal/src/main/python/pyproject.toml b/hal/src/main/python/pyproject.toml new file mode 100644 index 0000000000..3b3e05a70b --- /dev/null +++ b/hal/src/main/python/pyproject.toml @@ -0,0 +1,169 @@ +[build-system] +build-backend = "hatchling.build" +requires = [ + "semiwrap~=0.1.7", + "hatch-meson~=0.1.0b2", + "hatchling", + "pyntcore==2027.0.0a2", + "robotpy-native-wpihal==2027.0.0a2", + "robotpy-wpiutil==2027.0.0a2", +] + +[project] +name = "robotpy-hal" +version = "2027.0.0a2" +description = "Binary wrapper for FRC HAL" +authors = [ + {name = "RobotPy Development Team", email = "robotpy@googlegroups.com"}, +] +license = "BSD-3-Clause" +dependencies = [ + "pyntcore==2027.0.0a2", + "robotpy-native-wpihal==2027.0.0a2", + "robotpy-wpiutil==2027.0.0a2", +] + +[project.urls] +"Source code" = "https://github.com/robotpy/mostrobotpy" + + +[tool.hatch.build.hooks.robotpy] +version_file = "hal/version.py" + +[tool.hatch.build.hooks.semiwrap] + +[tool.hatch.build.hooks.meson] + +[tool.hatch.build.targets.wheel] +packages = ["hal"] + + +[tool.semiwrap] +update_init = [] +scan_headers_ignore = [ + "hal/ChipObject.h", + "hal/DMA.h", + "hal/Errors.h", + "hal/HAL.h", + "hal/IMU.h", + "hal/IMUTypes.h", + "hal/SystemServer.h", + "hal/Types.h", + "hal/Value.h", + + "hal/cpp/SerialHelper.h", + "hal/cpp/UnsafeDIO.h", + "hal/cpp/fpga_clock.h", + + "hal/handles/DigitalHandleResource.h", + "hal/handles/IndexedClassedHandleResource.h", + "hal/handles/IndexedHandleResource.h", + "hal/handles/LimitedClassedHandleResource.h", + "hal/handles/LimitedHandleResource.h", + "hal/handles/UnlimitedHandleResource.h", + + "hal/proto/*", + + "hal/roborio/HMB.h", + "hal/roborio/InterruptManager.h", + + "hal/simulation/CanData.h", + "hal/simulation/I2CData.h", + "hal/simulation/NotifyListener.h", + "hal/simulation/SPIData.h", + "hal/simulation/SimCallbackRegistry.h", + "hal/simulation/SimDataValue.h", + + # TODO: might want this in the future + "mrc/*", + + "src/ds_types_fmt.h", + "sim_cb.h", + "sim_value_cb.h", +] + +[tool.semiwrap.extension_modules."hal._wpiHal"] +name = "wpihal" +wraps = ["robotpy-native-wpihal"] +depends = ["wpiutil", "ntcore"] + +[tool.semiwrap.extension_modules."hal._wpiHal".headers] +# hal +AddressableLED = "hal/AddressableLED.h" +AddressableLEDTypes = "hal/AddressableLEDTypes.h" +AnalogInput = "hal/AnalogInput.h" +CAN = "hal/CAN.h" +CANAPI = "hal/CANAPI.h" +CANAPITypes = "hal/CANAPITypes.h" +CTREPCM = "hal/CTREPCM.h" +Constants = "hal/Constants.h" +Counter = "hal/Counter.h" +DIO = "hal/DIO.h" +# DMA = "hal/DMA.h" +DriverStation = "hal/DriverStation.h" +DriverStationTypes = "hal/DriverStationTypes.h" +DutyCycle = "hal/DutyCycle.h" +Encoder = "hal/Encoder.h" +# Errors = "hal/Errors.h" +Extensions = "hal/Extensions.h" +# HAL = "hal/HAL.h" +HALBase = "hal/HALBase.h" +I2C = "hal/I2C.h" +I2CTypes = "hal/I2CTypes.h" +# IMU = "hal/IMU.h" +# IMUTypes = "hal/IMUTypes.h" +Main = "hal/Main.h" +Notifier = "hal/Notifier.h" +PWM = "hal/PWM.h" +Ports = "hal/Ports.h" +Power = "hal/Power.h" +PowerDistribution = "hal/PowerDistribution.h" +REVPH = "hal/REVPH.h" +SerialPort = "hal/SerialPort.h" +SimDevice = "hal/SimDevice.h" +UsageReporting = "hal/UsageReporting.h" +Threads = "hal/Threads.h" +# Types = "hal/Types.h" +# Value = "hal/Value.h" + +# hal/cpp +# fpga_clock = "hal/cpp/fpga_clock.h" + +# hal/handles +# DigitalHandleResource = "hal/handles/DigitalHandleResource.h" +HandlesInternal = "hal/handles/HandlesInternal.h" +# IndexedClassedHandleResource = "hal/handles/IndexedClassedHandleResource.h" +# IndexedHandleResource = "hal/handles/IndexedHandleResource.h" +# LimitedClassedHandleResource = "hal/handles/LimitedClassedHandleResource.h" +# LimitedHandleResource = "hal/handles/LimitedHandleResource.h" +# UnlimitedHandleResource = "hal/handles/UnlimitedHandleResource.h" + + +[tool.semiwrap.extension_modules."hal.simulation._simulation"] +name = "hal_simulation" +wraps = ["robotpy-native-wpihal"] +depends = ["wpiutil", "ntcore"] +yaml_path = "semiwrap/simulation" + +[tool.semiwrap.extension_modules."hal.simulation._simulation".headers] +AddressableLEDData = "hal/simulation/AddressableLEDData.h" +AnalogInData = "hal/simulation/AnalogInData.h" +CTREPCMData = "hal/simulation/CTREPCMData.h" +# CanData = "hal/simulation/CanData.h" +DIOData = "hal/simulation/DIOData.h" +DigitalPWMData = "hal/simulation/DigitalPWMData.h" +DriverStationData = "hal/simulation/DriverStationData.h" +DutyCycleData = "hal/simulation/DutyCycleData.h" +EncoderData = "hal/simulation/EncoderData.h" +# I2CData = "hal/simulation/I2CData.h" +MockHooks = "hal/simulation/MockHooks.h" +NotifierData = "hal/simulation/NotifierData.h" +# NotifyListener = "hal/simulation/NotifyListener.h" +PWMData = "hal/simulation/PWMData.h" +PowerDistributionData = "hal/simulation/PowerDistributionData.h" +REVPHData = "hal/simulation/REVPHData.h" +Reset = "hal/simulation/Reset.h" +RoboRioData = "hal/simulation/RoboRioData.h" +# SimCallbackRegistry = "hal/simulation/SimCallbackRegistry.h" +# SimDataValue = "hal/simulation/SimDataValue.h" +SimDeviceData = "hal/simulation/SimDeviceData.h" diff --git a/hal/src/main/python/semiwrap/AddressableLED.yml b/hal/src/main/python/semiwrap/AddressableLED.yml new file mode 100644 index 0000000000..b211a42ac3 --- /dev/null +++ b/hal/src/main/python/semiwrap/AddressableLED.yml @@ -0,0 +1,9 @@ +strip_prefixes: +- HAL_ + +functions: + HAL_InitializeAddressableLED: + HAL_FreeAddressableLED: + HAL_SetAddressableLEDLength: + HAL_SetAddressableLEDStart: + HAL_SetAddressableLEDData: diff --git a/hal/src/main/python/semiwrap/AddressableLEDTypes.yml b/hal/src/main/python/semiwrap/AddressableLEDTypes.yml new file mode 100644 index 0000000000..e53f0a0dab --- /dev/null +++ b/hal/src/main/python/semiwrap/AddressableLEDTypes.yml @@ -0,0 +1,14 @@ +strip_prefixes: +- HAL_ + +classes: + HAL_AddressableLEDData: + attributes: + b: + g: + r: +enums: + HAL_AddressableLEDColorOrder: +functions: + format_as: + ignore: true diff --git a/hal/src/main/python/semiwrap/AnalogInput.yml b/hal/src/main/python/semiwrap/AnalogInput.yml new file mode 100644 index 0000000000..c1e831f142 --- /dev/null +++ b/hal/src/main/python/semiwrap/AnalogInput.yml @@ -0,0 +1,23 @@ +strip_prefixes: +- HAL_ + +functions: + HAL_InitializeAnalogInputPort: + HAL_FreeAnalogInputPort: + HAL_CheckAnalogModule: + HAL_CheckAnalogInputChannel: + HAL_SetAnalogInputSimDevice: + HAL_SetAnalogSampleRate: + HAL_GetAnalogSampleRate: + HAL_SetAnalogAverageBits: + HAL_GetAnalogAverageBits: + HAL_SetAnalogOversampleBits: + HAL_GetAnalogOversampleBits: + HAL_GetAnalogValue: + HAL_GetAnalogAverageValue: + HAL_GetAnalogVoltsToValue: + HAL_GetAnalogVoltage: + HAL_GetAnalogAverageVoltage: + HAL_GetAnalogLSBWeight: + HAL_GetAnalogOffset: + HAL_GetAnalogValueToVolts: diff --git a/hal/src/main/python/semiwrap/CAN.yml b/hal/src/main/python/semiwrap/CAN.yml new file mode 100644 index 0000000000..5ee43184a3 --- /dev/null +++ b/hal/src/main/python/semiwrap/CAN.yml @@ -0,0 +1,16 @@ +strip_prefixes: +- HAL_ + +functions: + HAL_CAN_SendMessage: + HAL_CAN_ReceiveMessage: + HAL_CAN_OpenStreamSession: + HAL_CAN_CloseStreamSession: + HAL_CAN_ReadStreamSession: + ignore: true # TODO: an array of messages + HAL_CAN_GetCANStatus: +classes: + HAL_CANStreamMessage: + attributes: + messageId: + message: diff --git a/hal/src/main/python/semiwrap/CANAPI.yml b/hal/src/main/python/semiwrap/CANAPI.yml new file mode 100644 index 0000000000..146c19e50f --- /dev/null +++ b/hal/src/main/python/semiwrap/CANAPI.yml @@ -0,0 +1,13 @@ +strip_prefixes: +- HAL_ + +functions: + HAL_InitializeCAN: + HAL_CleanCAN: + HAL_WriteCANPacket: + HAL_WriteCANPacketRepeating: + HAL_WriteCANRTRFrame: + HAL_StopCANPacketRepeating: + HAL_ReadCANPacketNew: + HAL_ReadCANPacketLatest: + HAL_ReadCANPacketTimeout: diff --git a/hal/src/main/python/semiwrap/CANAPITypes.yml b/hal/src/main/python/semiwrap/CANAPITypes.yml new file mode 100644 index 0000000000..71dfdbe120 --- /dev/null +++ b/hal/src/main/python/semiwrap/CANAPITypes.yml @@ -0,0 +1,19 @@ +strip_prefixes: +- HAL_ + +enums: + HAL_CANDeviceType: + value_prefix: HAL_CAN_Dev + HAL_CANManufacturer: + value_prefix: HAL_CAN_Man + HAL_CANFlags: +classes: + HAL_CANMessage: + attributes: + flags: + dataSize: + data: + HAL_CANReceiveMessage: + attributes: + timeStamp: + message: diff --git a/hal/src/main/python/semiwrap/CTREPCM.yml b/hal/src/main/python/semiwrap/CTREPCM.yml new file mode 100644 index 0000000000..9414932395 --- /dev/null +++ b/hal/src/main/python/semiwrap/CTREPCM.yml @@ -0,0 +1,26 @@ +strip_prefixes: +- HAL_ + +functions: + HAL_InitializeCTREPCM: + HAL_FreeCTREPCM: + HAL_CheckCTREPCMSolenoidChannel: + HAL_GetCTREPCMCompressor: + HAL_SetCTREPCMClosedLoopControl: + HAL_GetCTREPCMClosedLoopControl: + HAL_GetCTREPCMPressureSwitch: + HAL_GetCTREPCMCompressorCurrent: + HAL_GetCTREPCMCompressorCurrentTooHighFault: + HAL_GetCTREPCMCompressorCurrentTooHighStickyFault: + HAL_GetCTREPCMCompressorShortedStickyFault: + HAL_GetCTREPCMCompressorShortedFault: + HAL_GetCTREPCMCompressorNotConnectedStickyFault: + HAL_GetCTREPCMCompressorNotConnectedFault: + HAL_GetCTREPCMSolenoids: + HAL_SetCTREPCMSolenoids: + HAL_GetCTREPCMSolenoidDisabledList: + HAL_GetCTREPCMSolenoidVoltageStickyFault: + HAL_GetCTREPCMSolenoidVoltageFault: + HAL_ClearAllCTREPCMStickyFaults: + HAL_FireCTREPCMOneShot: + HAL_SetCTREPCMOneShotDuration: diff --git a/hal/src/main/python/semiwrap/Constants.yml b/hal/src/main/python/semiwrap/Constants.yml new file mode 100644 index 0000000000..bf0a5b4b86 --- /dev/null +++ b/hal/src/main/python/semiwrap/Constants.yml @@ -0,0 +1,5 @@ +strip_prefixes: +- HAL_ + +functions: + HAL_GetSystemClockTicksPerMicrosecond: diff --git a/hal/src/main/python/semiwrap/Counter.yml b/hal/src/main/python/semiwrap/Counter.yml new file mode 100644 index 0000000000..70389caa1e --- /dev/null +++ b/hal/src/main/python/semiwrap/Counter.yml @@ -0,0 +1,12 @@ +strip_prefixes: +- HAL_ + +functions: + HAL_InitializeCounter: + HAL_FreeCounter: + HAL_ResetCounter: + HAL_GetCounter: + HAL_GetCounterPeriod: + HAL_SetCounterMaxPeriod: + HAL_GetCounterStopped: + HAL_SetCounterEdgeConfiguration: diff --git a/hal/src/main/python/semiwrap/DIO.yml b/hal/src/main/python/semiwrap/DIO.yml new file mode 100644 index 0000000000..6bae8e7574 --- /dev/null +++ b/hal/src/main/python/semiwrap/DIO.yml @@ -0,0 +1,22 @@ +strip_prefixes: +- HAL_ + +functions: + HAL_InitializeDIOPort: + HAL_CheckDIOChannel: + HAL_FreeDIOPort: + HAL_SetDIOSimDevice: + HAL_AllocateDigitalPWM: + HAL_FreeDigitalPWM: + HAL_SetDigitalPWMRate: + HAL_SetDigitalPWMDutyCycle: + HAL_SetDigitalPWMPPS: + HAL_SetDigitalPWMOutputChannel: + HAL_SetDIO: + HAL_SetDIODirection: + HAL_GetDIO: + HAL_GetDIODirection: + HAL_Pulse: + HAL_PulseMultiple: + HAL_IsPulsing: + HAL_IsAnyPulsing: diff --git a/hal/src/main/python/semiwrap/DriverStation.yml b/hal/src/main/python/semiwrap/DriverStation.yml new file mode 100644 index 0000000000..98f3787800 --- /dev/null +++ b/hal/src/main/python/semiwrap/DriverStation.yml @@ -0,0 +1,42 @@ +strip_prefixes: +- HAL_ + +functions: + HAL_SendError: + HAL_SetPrintErrorImpl: + ignore: true + HAL_SendConsoleLine: + HAL_GetControlWord: + HAL_GetAllianceStation: + HAL_GetJoystickAxes: + HAL_GetJoystickPOVs: + HAL_GetJoystickButtons: + HAL_GetAllJoystickData: + HAL_GetJoystickDescriptor: + HAL_GetJoystickType: + HAL_GetJoystickName: + param_override: + name: + ignore: true + cpp_code: | + [](int32_t joystickNum) { + WPI_String name; + HAL_GetJoystickName(&name, joystickNum); + std::string sname(wpi::to_string_view(&name)); + WPI_FreeString(&name); + return sname; + } + HAL_GetJoystickAxisType: + HAL_SetJoystickOutputs: + HAL_GetMatchTime: + HAL_GetOutputsEnabled: + HAL_GetMatchInfo: + HAL_RefreshDSData: + HAL_ProvideNewDataEventHandle: + HAL_RemoveNewDataEventHandle: + HAL_ObserveUserProgramStarting: + HAL_ObserveUserProgramDisabled: + HAL_ObserveUserProgramAutonomous: + HAL_ObserveUserProgramTeleop: + HAL_ObserveUserProgramTest: + HAL_GetJoystickIsGamepad: diff --git a/hal/src/main/python/semiwrap/DriverStationTypes.yml b/hal/src/main/python/semiwrap/DriverStationTypes.yml new file mode 100644 index 0000000000..63662e6eb6 --- /dev/null +++ b/hal/src/main/python/semiwrap/DriverStationTypes.yml @@ -0,0 +1,53 @@ +strip_prefixes: +- HAL_ + +extra_includes: +- src/ds_types_fmt.h + +enums: + HAL_AllianceStationID: + value_prefix: HAL_AllianceStationID + HAL_JoystickPOV: + value_prefix: HAL_JoystickPOV + HAL_MatchType: + value_prefix: HAL_kMatchType +classes: + HAL_ControlWord: + attributes: + enabled: + autonomous: + test: + eStop: + fmsAttached: + dsAttached: + control_reserved: + HAL_JoystickAxes: + attributes: + count: + axes: + raw: + HAL_JoystickPOVs: + attributes: + count: + povs: + HAL_JoystickButtons: + attributes: + buttons: + count: + HAL_JoystickDescriptor: + attributes: + type: + name: + axisCount: + axisTypes: + buttonCount: + povCount: + isGamepad: + HAL_MatchInfo: + attributes: + eventName: + matchType: + matchNumber: + replayNumber: + gameSpecificMessage: + gameSpecificMessageSize: diff --git a/hal/src/main/python/semiwrap/DutyCycle.yml b/hal/src/main/python/semiwrap/DutyCycle.yml new file mode 100644 index 0000000000..b3369df5de --- /dev/null +++ b/hal/src/main/python/semiwrap/DutyCycle.yml @@ -0,0 +1,11 @@ +strip_prefixes: +- HAL_ + +functions: + HAL_InitializeDutyCycle: + HAL_FreeDutyCycle: + HAL_SetDutyCycleSimDevice: + ifndef: __FRC_SYSTEMCORE__ + HAL_GetDutyCycleFrequency: + HAL_GetDutyCycleOutput: + HAL_GetDutyCycleHighTime: diff --git a/hal/src/main/python/semiwrap/Encoder.yml b/hal/src/main/python/semiwrap/Encoder.yml new file mode 100644 index 0000000000..3e440e11ba --- /dev/null +++ b/hal/src/main/python/semiwrap/Encoder.yml @@ -0,0 +1,31 @@ +strip_prefixes: +- HAL_ + +enums: + HAL_EncoderIndexingType: + value_prefix: HAL + HAL_EncoderEncodingType: + value_prefix: HAL +functions: + HAL_InitializeEncoder: + HAL_FreeEncoder: + HAL_SetEncoderSimDevice: + HAL_GetEncoder: + HAL_GetEncoderRaw: + HAL_GetEncoderEncodingScale: + HAL_ResetEncoder: + HAL_GetEncoderPeriod: + HAL_SetEncoderMaxPeriod: + HAL_GetEncoderStopped: + HAL_GetEncoderDirection: + HAL_GetEncoderDistance: + HAL_GetEncoderRate: + HAL_SetEncoderMinRate: + HAL_SetEncoderDistancePerPulse: + HAL_SetEncoderReverseDirection: + HAL_SetEncoderSamplesToAverage: + HAL_GetEncoderSamplesToAverage: + HAL_GetEncoderFPGAIndex: + HAL_GetEncoderDecodingScaleFactor: + HAL_GetEncoderDistancePerPulse: + HAL_GetEncoderEncodingType: diff --git a/hal/src/main/python/semiwrap/Extensions.yml b/hal/src/main/python/semiwrap/Extensions.yml new file mode 100644 index 0000000000..d864791720 --- /dev/null +++ b/hal/src/main/python/semiwrap/Extensions.yml @@ -0,0 +1,16 @@ +strip_prefixes: +- HAL_ + +functions: + HAL_LoadOneExtension: + ifndef: __FRC_SYSTEMCORE__ + HAL_LoadExtensions: + ifndef: __FRC_SYSTEMCORE__ + HAL_RegisterExtension: + ignore: true + HAL_RegisterExtensionListener: + ignore: true + HAL_SetShowExtensionsNotFoundMessages: + ifndef: __FRC_SYSTEMCORE__ + HAL_OnShutdown: + ignore: true diff --git a/hal/src/main/python/semiwrap/HALBase.yml b/hal/src/main/python/semiwrap/HALBase.yml new file mode 100644 index 0000000000..7a11cc5eef --- /dev/null +++ b/hal/src/main/python/semiwrap/HALBase.yml @@ -0,0 +1,47 @@ +strip_prefixes: +- HAL_ + +enums: + HAL_RuntimeType: +functions: + HAL_GetErrorMessage: + HAL_GetFPGAVersion: + HAL_GetFPGARevision: + HAL_GetSerialNumber: + param_override: + serialNumber: + ignore: true + cpp_code: | + []() { + WPI_String s; + HAL_GetSerialNumber(&s); + std::string ss(wpi::to_string_view(&s)); + WPI_FreeString(&s); + return ss; + } + HAL_GetComments: + param_override: + comments: + ignore: true + cpp_code: | + []() { + WPI_String s; + HAL_GetComments(&s); + std::string ss(wpi::to_string_view(&s)); + WPI_FreeString(&s); + return ss; + } + HAL_GetTeamNumber: + HAL_GetRuntimeType: + HAL_GetSystemActive: + HAL_GetBrownedOut: + HAL_GetFPGATime: + HAL_ExpandFPGATime: + HAL_GetRSLState: + HAL_GetSystemTimeValid: + HAL_Initialize: + HAL_Shutdown: + HAL_SimPeriodicBefore: + HAL_SimPeriodicAfter: + HAL_GetLastError: + HAL_GetCommsDisableCount: diff --git a/hal/src/main/python/semiwrap/HandlesInternal.yml b/hal/src/main/python/semiwrap/HandlesInternal.yml new file mode 100644 index 0000000000..fb088df26b --- /dev/null +++ b/hal/src/main/python/semiwrap/HandlesInternal.yml @@ -0,0 +1,15 @@ +strip_prefixes: +- HAL_ + +enums: + HAL_HandleEnum: +functions: + getHandleIndex: + getHandleType: + isHandleType: + isHandleCorrectVersion: + getHandleTypedIndex: + createHandle: +classes: + hal::HandleBase: + ignore: true diff --git a/hal/src/main/python/semiwrap/I2C.yml b/hal/src/main/python/semiwrap/I2C.yml new file mode 100644 index 0000000000..9a6e020701 --- /dev/null +++ b/hal/src/main/python/semiwrap/I2C.yml @@ -0,0 +1,16 @@ +strip_prefixes: +- HAL_ + +functions: + HAL_InitializeI2C: + HAL_TransactionI2C: + buffers: + - {type: IN, src: dataToSend, len: sendSize} + - {type: OUT, src: dataReceived, len: receiveSize} + HAL_WriteI2C: + buffers: + - {type: IN, src: dataToSend, len: sendSize} + HAL_ReadI2C: + buffers: + - {type: OUT, src: buffer, len: count} + HAL_CloseI2C: diff --git a/hal/src/main/python/semiwrap/I2CTypes.yml b/hal/src/main/python/semiwrap/I2CTypes.yml new file mode 100644 index 0000000000..6286d100e5 --- /dev/null +++ b/hal/src/main/python/semiwrap/I2CTypes.yml @@ -0,0 +1,6 @@ +strip_prefixes: +- HAL_ + +enums: + HAL_I2CPort: + value_prefix: HAL_I2C diff --git a/hal/src/main/python/semiwrap/Main.yml b/hal/src/main/python/semiwrap/Main.yml new file mode 100644 index 0000000000..480034fddb --- /dev/null +++ b/hal/src/main/python/semiwrap/Main.yml @@ -0,0 +1,9 @@ +strip_prefixes: +- HAL_ + +functions: + HAL_SetMain: + ignore: true # TODO + HAL_HasMain: + HAL_RunMain: + HAL_ExitMain: diff --git a/hal/src/main/python/semiwrap/Notifier.yml b/hal/src/main/python/semiwrap/Notifier.yml new file mode 100644 index 0000000000..f128b5e715 --- /dev/null +++ b/hal/src/main/python/semiwrap/Notifier.yml @@ -0,0 +1,12 @@ +strip_prefixes: +- HAL_ + +functions: + HAL_InitializeNotifier: + HAL_SetNotifierName: + HAL_StopNotifier: + HAL_CleanNotifier: + HAL_UpdateNotifierAlarm: + HAL_CancelNotifierAlarm: + HAL_WaitForNotifierAlarm: + HAL_SetNotifierThreadPriority: diff --git a/hal/src/main/python/semiwrap/PWM.yml b/hal/src/main/python/semiwrap/PWM.yml new file mode 100644 index 0000000000..099db20b32 --- /dev/null +++ b/hal/src/main/python/semiwrap/PWM.yml @@ -0,0 +1,11 @@ +strip_prefixes: +- HAL_ + +functions: + HAL_InitializePWMPort: + HAL_FreePWMPort: + HAL_CheckPWMChannel: + HAL_SetPWMPulseTimeMicroseconds: + HAL_GetPWMPulseTimeMicroseconds: + HAL_SetPWMSimDevice: + HAL_SetPWMOutputPeriod: diff --git a/hal/src/main/python/semiwrap/Ports.yml b/hal/src/main/python/semiwrap/Ports.yml new file mode 100644 index 0000000000..f3e422dd42 --- /dev/null +++ b/hal/src/main/python/semiwrap/Ports.yml @@ -0,0 +1,22 @@ +strip_prefixes: +- HAL_ + +functions: + HAL_GetNumAnalogInputs: + HAL_GetNumCounters: + HAL_GetNumDigitalChannels: + HAL_GetNumPWMChannels: + HAL_GetNumDigitalPWMOutputs: + HAL_GetNumEncoders: + HAL_GetNumInterrupts: + HAL_GetNumDutyCycles: + HAL_GetNumAddressableLEDs: + HAL_GetNumCTREPCMModules: + HAL_GetNumCTRESolenoidChannels: + HAL_GetNumCTREPDPModules: + HAL_GetNumCTREPDPChannels: + HAL_GetNumREVPDHModules: + HAL_GetNumREVPDHChannels: + HAL_GetNumREVPHModules: + HAL_GetNumREVPHChannels: + HAL_GetNumCanBuses: diff --git a/hal/src/main/python/semiwrap/Power.yml b/hal/src/main/python/semiwrap/Power.yml new file mode 100644 index 0000000000..a133b8886d --- /dev/null +++ b/hal/src/main/python/semiwrap/Power.yml @@ -0,0 +1,14 @@ +strip_prefixes: +- HAL_ + +functions: + HAL_GetVinVoltage: + HAL_GetUserVoltage3V3: + HAL_GetUserCurrent3V3: + HAL_GetUserActive3V3: + HAL_GetUserCurrentFaults3V3: + HAL_SetUserRailEnabled3V3: + HAL_GetBrownoutVoltage: + HAL_SetBrownoutVoltage: + HAL_GetCPUTemp: + HAL_ResetUserCurrentFaults: diff --git a/hal/src/main/python/semiwrap/PowerDistribution.yml b/hal/src/main/python/semiwrap/PowerDistribution.yml new file mode 100644 index 0000000000..271495d7a7 --- /dev/null +++ b/hal/src/main/python/semiwrap/PowerDistribution.yml @@ -0,0 +1,105 @@ +strip_prefixes: +- HAL_ + +enums: + HAL_PowerDistributionType: +functions: + HAL_InitializePowerDistribution: + HAL_GetPowerDistributionModuleNumber: + HAL_CleanPowerDistribution: + HAL_CheckPowerDistributionChannel: + HAL_CheckPowerDistributionModule: + HAL_GetPowerDistributionType: + HAL_GetPowerDistributionNumChannels: + HAL_GetPowerDistributionTemperature: + HAL_GetPowerDistributionVoltage: + HAL_GetPowerDistributionChannelCurrent: + HAL_GetPowerDistributionAllChannelCurrents: + HAL_GetPowerDistributionTotalCurrent: + HAL_GetPowerDistributionTotalPower: + HAL_GetPowerDistributionTotalEnergy: + HAL_ResetPowerDistributionTotalEnergy: + HAL_ClearPowerDistributionStickyFaults: + HAL_SetPowerDistributionSwitchableChannel: + HAL_GetPowerDistributionSwitchableChannel: + HAL_GetPowerDistributionVersion: + HAL_GetPowerDistributionFaults: + HAL_GetPowerDistributionStickyFaults: + HAL_StartPowerDistributionStream: + ifdef: __FRC_SYSTEMCORE__ + HAL_GetPowerDistributionStreamData: + ifdef: __FRC_SYSTEMCORE__ + HAL_FreePowerDistributionStreamData: + ifdef: __FRC_SYSTEMCORE__ + HAL_StopPowerDistributionStream: + ifdef: __FRC_SYSTEMCORE__ +classes: + HAL_PowerDistributionVersion: + attributes: + firmwareMajor: + firmwareMinor: + firmwareFix: + hardwareMinor: + hardwareMajor: + uniqueId: + HAL_PowerDistributionFaults: + attributes: + channel0BreakerFault: + channel1BreakerFault: + channel2BreakerFault: + channel3BreakerFault: + channel4BreakerFault: + channel5BreakerFault: + channel6BreakerFault: + channel7BreakerFault: + channel8BreakerFault: + channel9BreakerFault: + channel10BreakerFault: + channel11BreakerFault: + channel12BreakerFault: + channel13BreakerFault: + channel14BreakerFault: + channel15BreakerFault: + channel16BreakerFault: + channel17BreakerFault: + channel18BreakerFault: + channel19BreakerFault: + channel20BreakerFault: + channel21BreakerFault: + channel22BreakerFault: + channel23BreakerFault: + brownout: + canWarning: + hardwareFault: + HAL_PowerDistributionStickyFaults: + attributes: + channel0BreakerFault: + channel1BreakerFault: + channel2BreakerFault: + channel3BreakerFault: + channel4BreakerFault: + channel5BreakerFault: + channel6BreakerFault: + channel7BreakerFault: + channel8BreakerFault: + channel9BreakerFault: + channel10BreakerFault: + channel11BreakerFault: + channel12BreakerFault: + channel13BreakerFault: + channel14BreakerFault: + channel15BreakerFault: + channel16BreakerFault: + channel17BreakerFault: + channel18BreakerFault: + channel19BreakerFault: + channel20BreakerFault: + channel21BreakerFault: + channel22BreakerFault: + channel23BreakerFault: + brownout: + canWarning: + canBusOff: + hasReset: + hardwareFault: + firmwareFault: diff --git a/hal/src/main/python/semiwrap/REVPH.yml b/hal/src/main/python/semiwrap/REVPH.yml new file mode 100644 index 0000000000..32532b9c18 --- /dev/null +++ b/hal/src/main/python/semiwrap/REVPH.yml @@ -0,0 +1,82 @@ +strip_prefixes: +- HAL_ + +enums: + HAL_REVPHCompressorConfigType: +functions: + HAL_InitializeREVPH: + HAL_FreeREVPH: + HAL_CheckREVPHSolenoidChannel: + HAL_CheckREVPHModuleNumber: + HAL_GetREVPHCompressor: + HAL_SetREVPHCompressorConfig: + HAL_SetREVPHClosedLoopControlDisabled: + HAL_SetREVPHClosedLoopControlDigital: + HAL_SetREVPHClosedLoopControlAnalog: + HAL_SetREVPHClosedLoopControlHybrid: + HAL_GetREVPHCompressorConfig: + HAL_GetREVPHPressureSwitch: + HAL_GetREVPHCompressorCurrent: + HAL_GetREVPHAnalogVoltage: + HAL_GetREVPHVoltage: + HAL_GetREVPH5VVoltage: + HAL_GetREVPHSolenoidCurrent: + HAL_GetREVPHSolenoidVoltage: + HAL_GetREVPHVersion: + HAL_GetREVPHSolenoids: + HAL_SetREVPHSolenoids: + HAL_FireREVPHOneShot: + HAL_GetREVPHFaults: + HAL_GetREVPHStickyFaults: + HAL_ClearREVPHStickyFaults: + HAL_GetREVPHSolenoidDisabledList: +classes: + HAL_REVPHVersion: + attributes: + firmwareMajor: + firmwareMinor: + firmwareFix: + hardwareMinor: + hardwareMajor: + uniqueId: + HAL_REVPHCompressorConfig: + attributes: + minAnalogVoltage: + maxAnalogVoltage: + forceDisable: + useDigital: + HAL_REVPHFaults: + attributes: + channel0Fault: + channel1Fault: + channel2Fault: + channel3Fault: + channel4Fault: + channel5Fault: + channel6Fault: + channel7Fault: + channel8Fault: + channel9Fault: + channel10Fault: + channel11Fault: + channel12Fault: + channel13Fault: + channel14Fault: + channel15Fault: + compressorOverCurrent: + compressorOpen: + solenoidOverCurrent: + brownout: + canWarning: + hardwareFault: + HAL_REVPHStickyFaults: + attributes: + compressorOverCurrent: + compressorOpen: + solenoidOverCurrent: + brownout: + canWarning: + canBusOff: + hasReset: + hardwareFault: + firmwareFault: diff --git a/hal/src/main/python/semiwrap/SerialPort.yml b/hal/src/main/python/semiwrap/SerialPort.yml new file mode 100644 index 0000000000..fb197b3dd3 --- /dev/null +++ b/hal/src/main/python/semiwrap/SerialPort.yml @@ -0,0 +1,30 @@ +strip_prefixes: +- HAL_ + +enums: + HAL_SerialPort: +functions: + HAL_InitializeSerialPort: + HAL_InitializeSerialPortDirect: + HAL_GetSerialFD: + HAL_SetSerialBaudRate: + HAL_SetSerialDataBits: + HAL_SetSerialParity: + HAL_SetSerialStopBits: + HAL_SetSerialWriteMode: + HAL_SetSerialFlowControl: + HAL_SetSerialTimeout: + HAL_EnableSerialTermination: + HAL_DisableSerialTermination: + HAL_SetSerialReadBufferSize: + HAL_SetSerialWriteBufferSize: + HAL_GetSerialBytesReceived: + HAL_ReadSerial: + buffers: + - {type: OUT, src: buffer, len: count} + HAL_WriteSerial: + buffers: + - {type: IN, src: buffer, len: count} + HAL_FlushSerial: + HAL_ClearSerial: + HAL_CloseSerial: diff --git a/hal/src/main/python/semiwrap/SimDevice.yml b/hal/src/main/python/semiwrap/SimDevice.yml new file mode 100644 index 0000000000..47cca971b2 --- /dev/null +++ b/hal/src/main/python/semiwrap/SimDevice.yml @@ -0,0 +1,376 @@ +defaults: + ignore: true + report_ignored_missing: false + +extra_includes: +- hal/simulation/SimDeviceData.h + +strip_prefixes: +- HAL_ + +enums: + HAL_SimValueDirection: +classes: + hal::SimValue: + doc: | + Readonly wrapper around a HAL simulator value. + + It is not useful to construct these directly -- they are returned from + :meth:`.SimDeviceSim.getValue` or :meth:`.SimDevice.createValue`. + methods: + SimValue: + overloads: + '': + ignore: true + HAL_SimValueHandle: + param_override: + val: + name: handle + GetValue: + ignore: true + SetValue: + ignore: true + hal::SimInt: + doc: | + Wrapper around a HAL simulator int value handle. + + It is not useful to construct these directly, they are returned + from various functions. + force_no_trampoline: true + methods: + SimInt: + overloads: + '': + ignore: true + HAL_SimValueHandle: + param_override: + val: + name: handle + Get: + Set: + Reset: + hal::SimLong: + doc: | + Wrapper around a HAL simulator long value handle. + + It is not useful to construct these directly, they are returned + from various functions. + force_no_trampoline: true + methods: + SimLong: + overloads: + '': + ignore: true + HAL_SimValueHandle: + param_override: + val: + name: handle + Get: + Set: + Reset: + hal::SimDouble: + doc: | + Wrapper around a HAL simulator double value. + + It is not useful to construct these directly -- they are returned from + :meth:`.SimDeviceSim.getDouble` or :meth:`.SimDevice.createDouble`. + force_no_trampoline: true + methods: + SimDouble: + overloads: + '': + ignore: true + HAL_SimValueHandle: + param_override: + val: + name: handle + Get: + Set: + Reset: + hal::SimEnum: + doc: | + Wrapper around a HAL simulator enum value. + + It is not useful to construct these directly -- they are returned from + :meth:`.SimDeviceSim.getEnum` or :meth:`.SimDevice.createEnum`. + force_no_trampoline: true + methods: + SimEnum: + overloads: + '': + ignore: true + HAL_SimValueHandle: + param_override: + val: + name: handle + Get: + Set: + hal::SimBoolean: + doc: | + Wrapper around a HAL simulator boolean value. + + It is not useful to construct these directly -- they are returned from + :meth:`.SimDeviceSim.getBoolean` or :meth:`.SimDevice.createBoolean`. + force_no_trampoline: true + methods: + SimBoolean: + overloads: + '': + ignore: true + HAL_SimValueHandle: + param_override: + val: + name: handle + Get: + Set: + hal::SimDevice: + doc: | + Wrapper around a HAL simulation 'device' + + This creates a simulated 'device' object that can be interacted with + from user SimDeviceSim objects or via the Simulation GUI. + + .. note:: To interact with an existing device use + :class:`hal.simulation.SimDeviceSim` instead. + force_type_casters: + - wpi::SmallVector + enums: + Direction: + methods: + SimDevice: + overloads: + '': + ignore: true + const char*: + const char*, int: + const char*, int, int: + GetName: + CreateValue: + ignore: true + CreateDouble: + CreateEnum: + overloads: + const char*, int32_t, std::initializer_list, int32_t: + ignore: true + const char*, int32_t, std::span, int32_t: + cpp_code: | + [](SimDevice &self, const char * name, int32_t direction, const wpi::SmallVector &options, int32_t initialValue) { + wpi::SmallVector coptions; + coptions.reserve(options.size()); + for (auto &s: options) { + coptions.push_back(s.c_str()); + } + return self.CreateEnum(name, direction, coptions, initialValue); + } + + CreateEnumDouble: + overloads: + const char*, int32_t, std::initializer_list, std::initializer_list, int32_t: + ignore: true + const char*, int32_t, std::span, std::span, int32_t: + cpp_code: | + [](SimDevice &self, const char * name, int32_t direction, const wpi::SmallVector &options, const wpi::SmallVector &optionValues, int32_t initialValue) { + wpi::SmallVector coptions; + coptions.reserve(options.size()); + for (auto &s: options) { + coptions.push_back(s.c_str()); + } + return self.CreateEnumDouble(name, direction, coptions, optionValues, initialValue); + } + + CreateBoolean: + CreateInt: + CreateLong: + +inline_code: |2 + + cls_SimValue + .def_property_readonly("value", [](const hal::SimValue &self) -> py::object { + HAL_Value value; + { + py::gil_scoped_release release; + value = self.GetValue(); + } + switch (value.type) { + case HAL_BOOLEAN: + return py::bool_(value.data.v_boolean); + case HAL_DOUBLE: + return py::float_(value.data.v_double); + case HAL_ENUM: + return py::int_(value.data.v_enum); + case HAL_INT: + return py::int_(value.data.v_int); + case HAL_LONG: + return py::int_(value.data.v_long); + default: + return py::none(); + } + }) + .def_property_readonly("type", [](const hal::SimValue &self) -> HAL_Type { + py::gil_scoped_release release; + return self.GetValue().type; + }) + .def("__bool__", [](const hal::SimValue &self) -> bool { + return (bool)self; + }) + .def("__repr__", [](const hal::SimValue &self) -> py::str { + if (!self) { + return ""; + } + HAL_Value value; + { + py::gil_scoped_release release; + value = self.GetValue(); + } + switch (value.type) { + case HAL_BOOLEAN: + if (value.data.v_boolean) { + return ""; + } else { + return ""; + } + case HAL_DOUBLE: + return ""; + case HAL_ENUM: + return ""; + case HAL_INT: + return ""; + case HAL_LONG: + return ""; + default: + return ""; + } + }); + + cls_SimBoolean + .def_property("value", &SimBoolean::Get, &SimBoolean::Set, release_gil()) + .def("__repr__", [](const SimBoolean &self) -> py::str { + if (self) { + bool value; + { + py::gil_scoped_release release; + value = self.Get(); + } + return std::string(""; + } else { + return ""; + } + }); + + cls_SimDevice + .def("__bool__", [](const hal::SimDevice &self) -> bool { + return (bool)self; + }) + .def_property_readonly("name", [](const hal::SimDevice &self) -> py::str { + #ifdef __FRC_SYSTEMCORE__ + return ""; + #else + if (!self) { + return ""; + } else { + const char *name; + { + py::gil_scoped_release release; + name = HALSIM_GetSimDeviceName(self); + } + return name; + } + #endif + }) + .def("__repr__", [](const hal::SimDevice &self) -> py::str { + #ifdef __FRC_SYSTEMCORE__ + return ""; + #else + if (!self) { + return ""; + } + const char *name; + { + py::gil_scoped_release release; + name = HALSIM_GetSimDeviceName(self); + } + return py::str("SimDevice(name={!r})").format(py::str(name)); + #endif + }); + + cls_SimDouble + .def_property("value", &SimDouble::Get, &SimDouble::Set, release_gil()) + .def("__repr__", [](const SimDouble &self) -> py::str { + if (self) { + double value; + { + py::gil_scoped_release release; + value = self.Get(); + } + return ""; + } else { + return ""; + } + }); + + cls_SimEnum + .def_property("value", &SimEnum::Get, &SimEnum::Set) + .def("__repr__", [](const SimEnum &self) -> py::str { + #ifdef __FRC_SYSTEMCORE__ + return ""; + #else + if (self) { + int32_t value; + int32_t numOptions; + int32_t numdOptions; + const char ** options; + const double * doptions; + const char * option = ""; + std::string doption; + { + py::gil_scoped_release release; + value = self.Get(); + options = HALSIM_GetSimValueEnumOptions(self, &numOptions); + doptions = HALSIM_GetSimValueEnumDoubleValues(self, &numdOptions); + } + + if (options && value >= 0 && value < numOptions) { + option = options[value]; + } + + if (doptions && value >= 0 && value < numdOptions) { + doption = " dvalue=" + std::to_string(doptions[value]); + } + + return ""; + } else { + return ""; + } + #endif + }); + + cls_SimInt + .def_property("value", &SimInt::Get, &SimInt::Set) + .def("__repr__", [](const SimInt &self) -> py::str { + if (self) { + int32_t value; + { + py::gil_scoped_release release; + value = self.Get(); + } + return ""; + } else { + return ""; + } + }); + + cls_SimLong + .def_property("value", &SimLong::Get, &SimLong::Set) + .def("__repr__", [](const SimLong &self) -> py::str { + if (self) { + int64_t value; + { + py::gil_scoped_release release; + value = self.Get(); + } + return ""; + } else { + return ""; + } + }); diff --git a/hal/src/main/python/semiwrap/Threads.yml b/hal/src/main/python/semiwrap/Threads.yml new file mode 100644 index 0000000000..e16ee2e61a --- /dev/null +++ b/hal/src/main/python/semiwrap/Threads.yml @@ -0,0 +1,12 @@ +strip_prefixes: +- HAL_ + +functions: + HAL_GetThreadPriority: + # no way to get native handle + ignore: true + HAL_GetCurrentThreadPriority: + HAL_SetThreadPriority: + # no way to get native handle + ignore: true + HAL_SetCurrentThreadPriority: diff --git a/hal/src/main/python/semiwrap/UsageReporting.yml b/hal/src/main/python/semiwrap/UsageReporting.yml new file mode 100644 index 0000000000..9b461b6ba3 --- /dev/null +++ b/hal/src/main/python/semiwrap/UsageReporting.yml @@ -0,0 +1,10 @@ +strip_prefixes: +- HAL_ + +functions: + HAL_ReportUsage: + overloads: + const struct WPI_String*, const struct WPI_String*: + ignore: true + std::string_view, std::string_view: + std::string_view, int, std::string_view: diff --git a/hal/src/main/python/semiwrap/simulation/AddressableLEDData.yml b/hal/src/main/python/semiwrap/simulation/AddressableLEDData.yml new file mode 100644 index 0000000000..324cd325cb --- /dev/null +++ b/hal/src/main/python/semiwrap/simulation/AddressableLEDData.yml @@ -0,0 +1,31 @@ +strip_prefixes: +- HALSIM_ + +functions: + HALSIM_ResetAddressableLEDData: + HALSIM_RegisterAddressableLEDInitializedCallback: + ignore: true + HALSIM_CancelAddressableLEDInitializedCallback: + ignore: true + HALSIM_GetAddressableLEDInitialized: + HALSIM_SetAddressableLEDInitialized: + HALSIM_RegisterAddressableLEDStartCallback: + ignore: true + HALSIM_CancelAddressableLEDStartCallback: + ignore: true + HALSIM_GetAddressableLEDStart: + HALSIM_SetAddressableLEDStart: + HALSIM_RegisterAddressableLEDLengthCallback: + ignore: true + HALSIM_CancelAddressableLEDLengthCallback: + ignore: true + HALSIM_GetAddressableLEDLength: + HALSIM_SetAddressableLEDLength: + HALSIM_RegisterAddressableLEDDataCallback: + ignore: true + HALSIM_CancelAddressableLEDDataCallback: + ignore: true + HALSIM_GetAddressableLEDData: + HALSIM_SetAddressableLEDData: + HALSIM_RegisterAddressableLEDAllCallbacks: + ignore: true diff --git a/hal/src/main/python/semiwrap/simulation/AnalogInData.yml b/hal/src/main/python/semiwrap/simulation/AnalogInData.yml new file mode 100644 index 0000000000..5b05933578 --- /dev/null +++ b/hal/src/main/python/semiwrap/simulation/AnalogInData.yml @@ -0,0 +1,28 @@ +strip_prefixes: +- HALSIM_ + +functions: + HALSIM_ResetAnalogInData: + HALSIM_RegisterAnalogInInitializedCallback: + ignore: true + HALSIM_CancelAnalogInInitializedCallback: + HALSIM_GetAnalogInInitialized: + HALSIM_SetAnalogInInitialized: + HALSIM_GetAnalogInSimDevice: + HALSIM_RegisterAnalogInAverageBitsCallback: + ignore: true + HALSIM_CancelAnalogInAverageBitsCallback: + HALSIM_GetAnalogInAverageBits: + HALSIM_SetAnalogInAverageBits: + HALSIM_RegisterAnalogInOversampleBitsCallback: + ignore: true + HALSIM_CancelAnalogInOversampleBitsCallback: + HALSIM_GetAnalogInOversampleBits: + HALSIM_SetAnalogInOversampleBits: + HALSIM_RegisterAnalogInVoltageCallback: + ignore: true + HALSIM_CancelAnalogInVoltageCallback: + HALSIM_GetAnalogInVoltage: + HALSIM_SetAnalogInVoltage: + HALSIM_RegisterAnalogInAllCallbacks: + ignore: true diff --git a/hal/src/main/python/semiwrap/simulation/CTREPCMData.yml b/hal/src/main/python/semiwrap/simulation/CTREPCMData.yml new file mode 100644 index 0000000000..10c8039370 --- /dev/null +++ b/hal/src/main/python/semiwrap/simulation/CTREPCMData.yml @@ -0,0 +1,47 @@ +strip_prefixes: +- HALSIM_ + +functions: + HALSIM_ResetCTREPCMData: + HALSIM_RegisterCTREPCMInitializedCallback: + ignore: true + HALSIM_CancelCTREPCMInitializedCallback: + ignore: true + HALSIM_GetCTREPCMInitialized: + HALSIM_SetCTREPCMInitialized: + HALSIM_RegisterCTREPCMSolenoidOutputCallback: + ignore: true + HALSIM_CancelCTREPCMSolenoidOutputCallback: + ignore: true + HALSIM_GetCTREPCMSolenoidOutput: + HALSIM_SetCTREPCMSolenoidOutput: + HALSIM_RegisterCTREPCMCompressorOnCallback: + ignore: true + HALSIM_CancelCTREPCMCompressorOnCallback: + ignore: true + HALSIM_GetCTREPCMCompressorOn: + HALSIM_SetCTREPCMCompressorOn: + HALSIM_RegisterCTREPCMClosedLoopEnabledCallback: + ignore: true + HALSIM_CancelCTREPCMClosedLoopEnabledCallback: + ignore: true + HALSIM_GetCTREPCMClosedLoopEnabled: + HALSIM_SetCTREPCMClosedLoopEnabled: + HALSIM_RegisterCTREPCMPressureSwitchCallback: + ignore: true + HALSIM_CancelCTREPCMPressureSwitchCallback: + ignore: true + HALSIM_GetCTREPCMPressureSwitch: + HALSIM_SetCTREPCMPressureSwitch: + HALSIM_RegisterCTREPCMCompressorCurrentCallback: + ignore: true + HALSIM_CancelCTREPCMCompressorCurrentCallback: + ignore: true + HALSIM_GetCTREPCMCompressorCurrent: + HALSIM_SetCTREPCMCompressorCurrent: + HALSIM_GetCTREPCMAllSolenoids: + HALSIM_SetCTREPCMAllSolenoids: + HALSIM_RegisterCTREPCMAllNonSolenoidCallbacks: + ignore: true + HALSIM_RegisterCTREPCMAllSolenoidCallbacks: + ignore: true diff --git a/hal/src/main/python/semiwrap/simulation/DIOData.yml b/hal/src/main/python/semiwrap/simulation/DIOData.yml new file mode 100644 index 0000000000..54829bc36f --- /dev/null +++ b/hal/src/main/python/semiwrap/simulation/DIOData.yml @@ -0,0 +1,33 @@ +strip_prefixes: +- HALSIM_ + +functions: + HALSIM_ResetDIOData: + HALSIM_RegisterDIOInitializedCallback: + ignore: true + HALSIM_CancelDIOInitializedCallback: + HALSIM_GetDIOInitialized: + HALSIM_SetDIOInitialized: + HALSIM_GetDIOSimDevice: + HALSIM_RegisterDIOValueCallback: + ignore: true + HALSIM_CancelDIOValueCallback: + HALSIM_GetDIOValue: + HALSIM_SetDIOValue: + HALSIM_RegisterDIOPulseLengthCallback: + ignore: true + HALSIM_CancelDIOPulseLengthCallback: + HALSIM_GetDIOPulseLength: + HALSIM_SetDIOPulseLength: + HALSIM_RegisterDIOIsInputCallback: + ignore: true + HALSIM_CancelDIOIsInputCallback: + HALSIM_GetDIOIsInput: + HALSIM_SetDIOIsInput: + HALSIM_RegisterDIOFilterIndexCallback: + ignore: true + HALSIM_CancelDIOFilterIndexCallback: + HALSIM_GetDIOFilterIndex: + HALSIM_SetDIOFilterIndex: + HALSIM_RegisterDIOAllCallbacks: + ignore: true diff --git a/hal/src/main/python/semiwrap/simulation/DigitalPWMData.yml b/hal/src/main/python/semiwrap/simulation/DigitalPWMData.yml new file mode 100644 index 0000000000..0a32ac2229 --- /dev/null +++ b/hal/src/main/python/semiwrap/simulation/DigitalPWMData.yml @@ -0,0 +1,23 @@ +strip_prefixes: +- HALSIM_ + +functions: + HALSIM_FindDigitalPWMForChannel: + HALSIM_ResetDigitalPWMData: + HALSIM_RegisterDigitalPWMInitializedCallback: + ignore: true + HALSIM_CancelDigitalPWMInitializedCallback: + HALSIM_GetDigitalPWMInitialized: + HALSIM_SetDigitalPWMInitialized: + HALSIM_RegisterDigitalPWMDutyCycleCallback: + ignore: true + HALSIM_CancelDigitalPWMDutyCycleCallback: + HALSIM_GetDigitalPWMDutyCycle: + HALSIM_SetDigitalPWMDutyCycle: + HALSIM_RegisterDigitalPWMPinCallback: + ignore: true + HALSIM_CancelDigitalPWMPinCallback: + HALSIM_GetDigitalPWMPin: + HALSIM_SetDigitalPWMPin: + HALSIM_RegisterDigitalPWMAllCallbacks: + ignore: true diff --git a/hal/src/main/python/semiwrap/simulation/DriverStationData.yml b/hal/src/main/python/semiwrap/simulation/DriverStationData.yml new file mode 100644 index 0000000000..29b5a91acd --- /dev/null +++ b/hal/src/main/python/semiwrap/simulation/DriverStationData.yml @@ -0,0 +1,113 @@ +strip_prefixes: +- HALSIM_ + +functions: + HALSIM_ResetDriverStationData: + HALSIM_RegisterDriverStationEnabledCallback: + ignore: true + HALSIM_CancelDriverStationEnabledCallback: + HALSIM_GetDriverStationEnabled: + HALSIM_SetDriverStationEnabled: + HALSIM_RegisterDriverStationAutonomousCallback: + ignore: true + HALSIM_CancelDriverStationAutonomousCallback: + HALSIM_GetDriverStationAutonomous: + HALSIM_SetDriverStationAutonomous: + HALSIM_RegisterDriverStationTestCallback: + ignore: true + HALSIM_CancelDriverStationTestCallback: + HALSIM_GetDriverStationTest: + HALSIM_SetDriverStationTest: + HALSIM_RegisterDriverStationEStopCallback: + ignore: true + HALSIM_CancelDriverStationEStopCallback: + HALSIM_GetDriverStationEStop: + HALSIM_SetDriverStationEStop: + HALSIM_RegisterDriverStationFmsAttachedCallback: + ignore: true + HALSIM_CancelDriverStationFmsAttachedCallback: + HALSIM_GetDriverStationFmsAttached: + HALSIM_SetDriverStationFmsAttached: + HALSIM_RegisterDriverStationDsAttachedCallback: + ignore: true + HALSIM_CancelDriverStationDsAttachedCallback: + HALSIM_GetDriverStationDsAttached: + HALSIM_SetDriverStationDsAttached: + HALSIM_RegisterDriverStationAllianceStationIdCallback: + ignore: true + HALSIM_CancelDriverStationAllianceStationIdCallback: + HALSIM_GetDriverStationAllianceStationId: + HALSIM_SetDriverStationAllianceStationId: + HALSIM_RegisterDriverStationMatchTimeCallback: + ignore: true + HALSIM_CancelDriverStationMatchTimeCallback: + HALSIM_GetDriverStationMatchTime: + HALSIM_SetDriverStationMatchTime: + HALSIM_RegisterJoystickAxesCallback: + ignore: true + HALSIM_CancelJoystickAxesCallback: + HALSIM_GetJoystickAxes: + HALSIM_SetJoystickAxes: + HALSIM_RegisterJoystickPOVsCallback: + ignore: true + HALSIM_CancelJoystickPOVsCallback: + HALSIM_GetJoystickPOVs: + HALSIM_SetJoystickPOVs: + HALSIM_RegisterJoystickButtonsCallback: + ignore: true + HALSIM_CancelJoystickButtonsCallback: + HALSIM_GetJoystickButtons: + HALSIM_SetJoystickButtons: + HALSIM_RegisterJoystickDescriptorCallback: + ignore: true + HALSIM_CancelJoystickDescriptorCallback: + HALSIM_GetJoystickDescriptor: + HALSIM_SetJoystickDescriptor: + HALSIM_RegisterJoystickOutputsCallback: + ignore: true + HALSIM_CancelJoystickOutputsCallback: + HALSIM_GetJoystickOutputs: + HALSIM_SetJoystickOutputs: + HALSIM_RegisterMatchInfoCallback: + ignore: true + HALSIM_CancelMatchInfoCallback: + HALSIM_GetMatchInfo: + HALSIM_SetMatchInfo: + HALSIM_SetJoystickButton: + HALSIM_SetJoystickAxis: + HALSIM_SetJoystickPOV: + HALSIM_SetJoystickButtonsValue: + HALSIM_SetJoystickAxisCount: + HALSIM_SetJoystickPOVCount: + HALSIM_SetJoystickButtonCount: + HALSIM_GetJoystickCounts: + HALSIM_SetJoystickType: + HALSIM_SetJoystickName: + cpp_code: | + [](int32_t stick, std::string_view sv) { + auto s = wpi::make_string(sv); + HALSIM_SetJoystickName(stick, &s); + } + HALSIM_SetJoystickAxisType: + HALSIM_SetGameSpecificMessage: + cpp_code: | + [](std::string_view sv) { + auto s = wpi::make_string(sv); + HALSIM_SetGameSpecificMessage(&s); + } + HALSIM_SetEventName: + cpp_code: | + [](std::string_view sv) { + auto s = wpi::make_string(sv); + HALSIM_SetEventName(&s); + } + HALSIM_SetMatchType: + HALSIM_SetMatchNumber: + HALSIM_SetReplayNumber: + HALSIM_RegisterDriverStationAllCallbacks: + ignore: true + HALSIM_RegisterDriverStationNewDataCallback: + ignore: true + HALSIM_CancelDriverStationNewDataCallback: + HALSIM_NotifyDriverStationNewData: + HALSIM_SetJoystickIsGamepad: diff --git a/hal/src/main/python/semiwrap/simulation/DutyCycleData.yml b/hal/src/main/python/semiwrap/simulation/DutyCycleData.yml new file mode 100644 index 0000000000..f9eae6b356 --- /dev/null +++ b/hal/src/main/python/semiwrap/simulation/DutyCycleData.yml @@ -0,0 +1,23 @@ +strip_prefixes: +- HALSIM_ + +functions: + HALSIM_ResetDutyCycleData: + HALSIM_RegisterDutyCycleInitializedCallback: + ignore: true + HALSIM_CancelDutyCycleInitializedCallback: + HALSIM_GetDutyCycleInitialized: + HALSIM_SetDutyCycleInitialized: + HALSIM_GetDutyCycleSimDevice: + HALSIM_RegisterDutyCycleOutputCallback: + ignore: true + HALSIM_CancelDutyCycleOutputCallback: + HALSIM_GetDutyCycleOutput: + HALSIM_SetDutyCycleOutput: + HALSIM_RegisterDutyCycleFrequencyCallback: + ignore: true + HALSIM_CancelDutyCycleFrequencyCallback: + HALSIM_GetDutyCycleFrequency: + HALSIM_SetDutyCycleFrequency: + HALSIM_RegisterDutyCycleAllCallbacks: + ignore: true diff --git a/hal/src/main/python/semiwrap/simulation/EncoderData.yml b/hal/src/main/python/semiwrap/simulation/EncoderData.yml new file mode 100644 index 0000000000..94aba57c66 --- /dev/null +++ b/hal/src/main/python/semiwrap/simulation/EncoderData.yml @@ -0,0 +1,60 @@ +strip_prefixes: +- HALSIM_ + +functions: + HALSIM_FindEncoderForChannel: + HALSIM_ResetEncoderData: + HALSIM_GetEncoderDigitalChannelA: + HALSIM_GetEncoderDigitalChannelB: + HALSIM_RegisterEncoderInitializedCallback: + ignore: true + HALSIM_CancelEncoderInitializedCallback: + HALSIM_GetEncoderInitialized: + HALSIM_SetEncoderInitialized: + HALSIM_GetEncoderSimDevice: + HALSIM_RegisterEncoderCountCallback: + ignore: true + HALSIM_CancelEncoderCountCallback: + HALSIM_GetEncoderCount: + HALSIM_SetEncoderCount: + HALSIM_RegisterEncoderPeriodCallback: + ignore: true + HALSIM_CancelEncoderPeriodCallback: + HALSIM_GetEncoderPeriod: + HALSIM_SetEncoderPeriod: + HALSIM_RegisterEncoderResetCallback: + ignore: true + HALSIM_CancelEncoderResetCallback: + HALSIM_GetEncoderReset: + HALSIM_SetEncoderReset: + HALSIM_RegisterEncoderMaxPeriodCallback: + ignore: true + HALSIM_CancelEncoderMaxPeriodCallback: + HALSIM_GetEncoderMaxPeriod: + HALSIM_SetEncoderMaxPeriod: + HALSIM_RegisterEncoderDirectionCallback: + ignore: true + HALSIM_CancelEncoderDirectionCallback: + HALSIM_GetEncoderDirection: + HALSIM_SetEncoderDirection: + HALSIM_RegisterEncoderReverseDirectionCallback: + ignore: true + HALSIM_CancelEncoderReverseDirectionCallback: + HALSIM_GetEncoderReverseDirection: + HALSIM_SetEncoderReverseDirection: + HALSIM_RegisterEncoderSamplesToAverageCallback: + ignore: true + HALSIM_CancelEncoderSamplesToAverageCallback: + HALSIM_GetEncoderSamplesToAverage: + HALSIM_SetEncoderSamplesToAverage: + HALSIM_RegisterEncoderDistancePerPulseCallback: + ignore: true + HALSIM_CancelEncoderDistancePerPulseCallback: + HALSIM_GetEncoderDistancePerPulse: + HALSIM_SetEncoderDistancePerPulse: + HALSIM_RegisterEncoderAllCallbacks: + ignore: true + HALSIM_SetEncoderDistance: + HALSIM_GetEncoderDistance: + HALSIM_SetEncoderRate: + HALSIM_GetEncoderRate: diff --git a/hal/src/main/python/semiwrap/simulation/MockHooks.yml b/hal/src/main/python/semiwrap/simulation/MockHooks.yml new file mode 100644 index 0000000000..367131c8b2 --- /dev/null +++ b/hal/src/main/python/semiwrap/simulation/MockHooks.yml @@ -0,0 +1,59 @@ +extra_includes: +- sim_cb.h +- pybind11/functional.h + +strip_prefixes: +- HALSIM_ + +functions: + HALSIM_SetRuntimeType: + HALSIM_WaitForProgramStart: + HALSIM_SetProgramStarted: + HALSIM_GetProgramStarted: + HALSIM_RestartTiming: + HALSIM_PauseTiming: + HALSIM_ResumeTiming: + HALSIM_IsTimingPaused: + HALSIM_StepTiming: + HALSIM_StepTimingAsync: + + # following functions are ignored for raw function pointers + + HALSIM_SetSendError: + ignore: true + HALSIM_SetSendConsoleLine: + ignore: true + + HALSIM_RegisterSimPeriodicBeforeCallback: + param_override: + param: + ignore: true + cpp_code: | + [](std::function fn) -> std::unique_ptr { + auto cb = std::make_unique(fn, HALSIM_CancelSimPeriodicBeforeCallback); + auto uid = HALSIM_RegisterSimPeriodicBeforeCallback([](void *param) { + ((SimCB*)param)->m_fn(); + }, cb.get()); + cb->SetUID(uid); + return std::move(cb); + } + HALSIM_CancelSimPeriodicBeforeCallback: + ignore: true + + HALSIM_RegisterSimPeriodicAfterCallback: + param_override: + param: + ignore: true + cpp_code: | + [](std::function fn) -> std::unique_ptr { + auto cb = std::make_unique(fn, HALSIM_CancelSimPeriodicAfterCallback); + auto uid = HALSIM_RegisterSimPeriodicAfterCallback([](void *param) { + ((SimCB*)param)->m_fn(); + }, cb.get()); + cb->SetUID(uid); + return std::move(cb); + } + HALSIM_CancelSimPeriodicAfterCallback: + ignore: true + + HALSIM_CancelAllSimPeriodicCallbacks: diff --git a/hal/src/main/python/semiwrap/simulation/NotifierData.yml b/hal/src/main/python/semiwrap/simulation/NotifierData.yml new file mode 100644 index 0000000000..3279e9ce21 --- /dev/null +++ b/hal/src/main/python/semiwrap/simulation/NotifierData.yml @@ -0,0 +1,14 @@ +strip_prefixes: +- HALSIM_ + +functions: + HALSIM_GetNextNotifierTimeout: + HALSIM_GetNumNotifiers: + HALSIM_GetNotifierInfo: +classes: + HALSIM_NotifierInfo: + attributes: + handle: + name: + timeout: + waitTimeValid: diff --git a/hal/src/main/python/semiwrap/simulation/PWMData.yml b/hal/src/main/python/semiwrap/simulation/PWMData.yml new file mode 100644 index 0000000000..cd1c5b1a94 --- /dev/null +++ b/hal/src/main/python/semiwrap/simulation/PWMData.yml @@ -0,0 +1,25 @@ +strip_prefixes: +- HALSIM_ + +functions: + HALSIM_ResetPWMData: + HALSIM_RegisterPWMInitializedCallback: + ignore: true + HALSIM_CancelPWMInitializedCallback: + HALSIM_GetPWMInitialized: + HALSIM_SetPWMInitialized: + HALSIM_RegisterPWMPulseMicrosecondCallback: + ignore: true + HALSIM_CancelPWMPulseMicrosecondCallback: + ignore: true + HALSIM_GetPWMPulseMicrosecond: + HALSIM_SetPWMPulseMicrosecond: + HALSIM_RegisterPWMAllCallbacks: + ignore: true + HALSIM_GetPWMSimDevice: + HALSIM_RegisterPWMOutputPeriodCallback: + ignore: true + HALSIM_CancelPWMOutputPeriodCallback: + ignore: true + HALSIM_GetPWMOutputPeriod: + HALSIM_SetPWMOutputPeriod: diff --git a/hal/src/main/python/semiwrap/simulation/PowerDistributionData.yml b/hal/src/main/python/semiwrap/simulation/PowerDistributionData.yml new file mode 100644 index 0000000000..5521218bd5 --- /dev/null +++ b/hal/src/main/python/semiwrap/simulation/PowerDistributionData.yml @@ -0,0 +1,29 @@ +strip_prefixes: +- HALSIM_ + +functions: + HALSIM_ResetPowerDistributionData: + HALSIM_RegisterPowerDistributionInitializedCallback: + ignore: true + HALSIM_CancelPowerDistributionInitializedCallback: + HALSIM_GetPowerDistributionInitialized: + HALSIM_SetPowerDistributionInitialized: + HALSIM_RegisterPowerDistributionTemperatureCallback: + ignore: true + HALSIM_CancelPowerDistributionTemperatureCallback: + HALSIM_GetPowerDistributionTemperature: + HALSIM_SetPowerDistributionTemperature: + HALSIM_RegisterPowerDistributionVoltageCallback: + ignore: true + HALSIM_CancelPowerDistributionVoltageCallback: + HALSIM_GetPowerDistributionVoltage: + HALSIM_SetPowerDistributionVoltage: + HALSIM_RegisterPowerDistributionCurrentCallback: + ignore: true + HALSIM_CancelPowerDistributionCurrentCallback: + HALSIM_GetPowerDistributionCurrent: + HALSIM_SetPowerDistributionCurrent: + HALSIM_GetPowerDistributionAllCurrents: + HALSIM_SetPowerDistributionAllCurrents: + HALSIM_RegisterPowerDistributionAllNonCurrentCallbacks: + ignore: true diff --git a/hal/src/main/python/semiwrap/simulation/REVPHData.yml b/hal/src/main/python/semiwrap/simulation/REVPHData.yml new file mode 100644 index 0000000000..acaebf35e4 --- /dev/null +++ b/hal/src/main/python/semiwrap/simulation/REVPHData.yml @@ -0,0 +1,41 @@ +strip_prefixes: +- HALSIM_ + +functions: + HALSIM_ResetREVPHData: + HALSIM_RegisterREVPHInitializedCallback: + ignore: true + HALSIM_CancelREVPHInitializedCallback: + HALSIM_GetREVPHInitialized: + HALSIM_SetREVPHInitialized: + HALSIM_RegisterREVPHSolenoidOutputCallback: + ignore: true + HALSIM_CancelREVPHSolenoidOutputCallback: + HALSIM_GetREVPHSolenoidOutput: + HALSIM_SetREVPHSolenoidOutput: + HALSIM_RegisterREVPHCompressorOnCallback: + ignore: true + HALSIM_CancelREVPHCompressorOnCallback: + HALSIM_GetREVPHCompressorOn: + HALSIM_SetREVPHCompressorOn: + HALSIM_RegisterREVPHCompressorConfigTypeCallback: + ignore: true + HALSIM_CancelREVPHCompressorConfigTypeCallback: + HALSIM_GetREVPHCompressorConfigType: + HALSIM_SetREVPHCompressorConfigType: + HALSIM_RegisterREVPHPressureSwitchCallback: + ignore: true + HALSIM_CancelREVPHPressureSwitchCallback: + HALSIM_GetREVPHPressureSwitch: + HALSIM_SetREVPHPressureSwitch: + HALSIM_RegisterREVPHCompressorCurrentCallback: + ignore: true + HALSIM_CancelREVPHCompressorCurrentCallback: + HALSIM_GetREVPHCompressorCurrent: + HALSIM_SetREVPHCompressorCurrent: + HALSIM_GetREVPHAllSolenoids: + HALSIM_SetREVPHAllSolenoids: + HALSIM_RegisterREVPHAllNonSolenoidCallbacks: + ignore: true + HALSIM_RegisterREVPHAllSolenoidCallbacks: + ignore: true diff --git a/hal/src/main/python/semiwrap/simulation/Reset.yml b/hal/src/main/python/semiwrap/simulation/Reset.yml new file mode 100644 index 0000000000..ab6e273afc --- /dev/null +++ b/hal/src/main/python/semiwrap/simulation/Reset.yml @@ -0,0 +1,5 @@ +strip_prefixes: +- HALSIM_ + +functions: + HALSIM_ResetAllSimData: diff --git a/hal/src/main/python/semiwrap/simulation/RoboRioData.yml b/hal/src/main/python/semiwrap/simulation/RoboRioData.yml new file mode 100644 index 0000000000..8bf0f231b4 --- /dev/null +++ b/hal/src/main/python/semiwrap/simulation/RoboRioData.yml @@ -0,0 +1,97 @@ +strip_prefixes: +- HALSIM_ + +functions: + HALSIM_ResetRoboRioData: + HALSIM_RegisterRoboRioFPGAButtonCallback: + ignore: true + HALSIM_CancelRoboRioFPGAButtonCallback: + ignore: true + HALSIM_GetRoboRioFPGAButton: + ignore: true + HALSIM_SetRoboRioFPGAButton: + ignore: true + HALSIM_RegisterRoboRioVInVoltageCallback: + ignore: true + HALSIM_CancelRoboRioVInVoltageCallback: + HALSIM_GetRoboRioVInVoltage: + HALSIM_SetRoboRioVInVoltage: + HALSIM_RegisterRoboRioUserVoltage3V3Callback: + ignore: true + HALSIM_CancelRoboRioUserVoltage3V3Callback: + HALSIM_GetRoboRioUserVoltage3V3: + HALSIM_SetRoboRioUserVoltage3V3: + HALSIM_RegisterRoboRioUserCurrent3V3Callback: + ignore: true + HALSIM_CancelRoboRioUserCurrent3V3Callback: + HALSIM_GetRoboRioUserCurrent3V3: + HALSIM_SetRoboRioUserCurrent3V3: + HALSIM_RegisterRoboRioUserActive3V3Callback: + ignore: true + HALSIM_CancelRoboRioUserActive3V3Callback: + HALSIM_GetRoboRioUserActive3V3: + HALSIM_SetRoboRioUserActive3V3: + HALSIM_RegisterRoboRioUserFaults3V3Callback: + ignore: true + HALSIM_CancelRoboRioUserFaults3V3Callback: + HALSIM_GetRoboRioUserFaults3V3: + HALSIM_SetRoboRioUserFaults3V3: + HALSIM_RegisterRoboRioBrownoutVoltageCallback: + ignore: true + HALSIM_CancelRoboRioBrownoutVoltageCallback: + HALSIM_GetRoboRioBrownoutVoltage: + HALSIM_SetRoboRioBrownoutVoltage: + HALSIM_RegisterRoboRioTeamNumberCallback: + ignore: true + HALSIM_CancelRoboRioTeamNumberCallback: + HALSIM_GetRoboRioTeamNumber: + HALSIM_SetRoboRioTeamNumber: + HALSIM_RegisterRoboRioSerialNumberCallback: + ignore: true + HALSIM_CancelRoboRioSerialNumberCallback: + HALSIM_GetRoboRioSerialNumber: + param_override: + serialNumber: + ignore: true + cpp_code: | + []() { + WPI_String s; + HALSIM_GetRoboRioSerialNumber(&s); + std::string ss(wpi::to_string_view(&s)); + WPI_FreeString(&s); + return ss; + } + HALSIM_SetRoboRioSerialNumber: + cpp_code: | + [](std::string_view sv) { + auto s = wpi::make_string(sv); + HALSIM_SetRoboRioSerialNumber(&s); + } + HALSIM_RegisterRoboRioCommentsCallback: + ignore: true + HALSIM_CancelRoboRioCommentsCallback: + HALSIM_GetRoboRioComments: + param_override: + comments: + ignore: true + cpp_code: | + []() { + WPI_String s; + HALSIM_GetRoboRioComments(&s); + std::string ss(wpi::to_string_view(&s)); + WPI_FreeString(&s); + return ss; + } + HALSIM_SetRoboRioComments: + cpp_code: | + [](std::string_view sv) { + auto s = wpi::make_string(sv); + HALSIM_SetRoboRioComments(&s); + } + HALSIM_RegisterRoboRioAllCallbacks: + ignore: true + HALSIM_RegisterRoboRioCPUTempCallback: + ignore: true + HALSIM_CancelRoboRioCPUTempCallback: + HALSIM_GetRoboRioCPUTemp: + HALSIM_SetRoboRioCPUTemp: diff --git a/hal/src/main/python/semiwrap/simulation/SimDeviceData.yml b/hal/src/main/python/semiwrap/simulation/SimDeviceData.yml new file mode 100644 index 0000000000..de6cf5b83e --- /dev/null +++ b/hal/src/main/python/semiwrap/simulation/SimDeviceData.yml @@ -0,0 +1,92 @@ +extra_includes: +- sim_value_cb.h +- pybind11/functional.h + +strip_prefixes: +- HALSIM_ + +functions: + HALSIM_SetSimDeviceEnabled: + HALSIM_IsSimDeviceEnabled: + HALSIM_RegisterSimDeviceCreatedCallback: + ignore: true + HALSIM_CancelSimDeviceCreatedCallback: + ignore: true + HALSIM_RegisterSimDeviceFreedCallback: + ignore: true + HALSIM_CancelSimDeviceFreedCallback: + ignore: true + HALSIM_GetSimDeviceHandle: + HALSIM_GetSimDeviceName: + HALSIM_GetSimValueDeviceHandle: + HALSIM_EnumerateSimDevices: + ignore: true + HALSIM_RegisterSimValueCreatedCallback: + param_override: + param: + ignore: true + cpp_code: | + [](hal::SimDevice &simdevice, std::function fn, bool initialNotify) -> std::unique_ptr { + auto cb = std::make_unique(fn, HALSIM_CancelSimDeviceCreatedCallback); + auto uid = HALSIM_RegisterSimValueCreatedCallback(simdevice, cb.get(), + [](const char* name, void* param, + HAL_SimValueHandle handle, + int32_t direction, + const struct HAL_Value* value) { + ((SimValueCB*)param)->m_fn(name, handle, (HAL_SimValueDirection)direction, *value); + }, initialNotify); + cb->SetUID(uid); + return std::move(cb); + } + HALSIM_CancelSimValueCreatedCallback: + ignore: true + HALSIM_RegisterSimValueChangedCallback: + param_override: + handle: + name: value + param: + ignore: true + cpp_code: | + [](hal::SimValue &simvalue, std::function fn, bool initialNotify) -> std::unique_ptr { + auto cb = std::make_unique(fn, HALSIM_CancelSimValueChangedCallback); + auto uid = HALSIM_RegisterSimValueChangedCallback(simvalue, cb.get(), + [](const char* name, void* param, + HAL_SimValueHandle handle, + int32_t direction, + const struct HAL_Value* value) { + ((SimValueCB*)param)->m_fn(name, handle, (HAL_SimValueDirection)direction, *value); + }, initialNotify); + cb->SetUID(uid); + return std::move(cb); + } + HALSIM_CancelSimValueChangedCallback: + ignore: true + HALSIM_RegisterSimValueResetCallback: + param_override: + handle: + name: value + param: + ignore: true + cpp_code: | + [](hal::SimValue &simvalue, std::function fn, bool initialNotify) -> std::unique_ptr { + auto cb = std::make_unique(fn, HALSIM_CancelSimValueResetCallback); + auto uid = HALSIM_RegisterSimValueChangedCallback(simvalue, cb.get(), + [](const char* name, void* param, + HAL_SimValueHandle handle, + int32_t direction, + const struct HAL_Value* value) { + ((SimValueCB*)param)->m_fn(name, handle, (HAL_SimValueDirection)direction, *value); + }, initialNotify); + cb->SetUID(uid); + return std::move(cb); + } + HALSIM_CancelSimValueResetCallback: + ignore: true + HALSIM_GetSimValueHandle: + HALSIM_EnumerateSimValues: + ignore: true + HALSIM_GetSimValueEnumOptions: + ignore: true + HALSIM_GetSimValueEnumDoubleValues: + ignore: true + HALSIM_ResetSimDeviceData: diff --git a/hal/src/test/python/test_hal.py b/hal/src/test/python/test_hal.py new file mode 100644 index 0000000000..9970edb5dd --- /dev/null +++ b/hal/src/test/python/test_hal.py @@ -0,0 +1,47 @@ +import hal + + +def test_hal_simdevice(): + device = hal.SimDevice("deviceName") + v = device.createInt("i", 0, 1) + assert v.get() == 1 + assert v.type == hal.Type.INT + + v.set(4) + assert v.get() == 4 + + +def test_hal_simdevice_enum(): + device = hal.SimDevice("enumDevice") + names = ["one", "two", "three"] + v = device.createEnum("e", 0, names, 0) + + assert v.get() == 0 + assert v.type == hal.Type.ENUM + + for value, name in enumerate(names): + v.set(value) + assert repr(v) == f"" + + +def test_hal_simdevice_enum_double(): + device = hal.SimDevice("enumDevice") + names = ["one", "two", "three"] + values = [0.0, 1.0, 2.0] + v = device.createEnumDouble("e", 0, names, values, 0) + + assert v.get() == 0 + assert v.type == hal.Type.ENUM + + # TODO: update this test to not use repr once https://github.com/wpilibsuite/allwpilib/issues/7800 + # is resolved + + for value, (name, dvalue) in enumerate(zip(names, values)): + v.set(value) + assert repr(v) == f"" + + +def test_hal_send_error(capsys): + hal._wpiHal.__test_senderr() + cap = capsys.readouterr() + assert cap.err == "Error at location: �badmessage\ncallstack\n\n" diff --git a/hal/src/test/python/test_hal_simulation.py b/hal/src/test/python/test_hal_simulation.py new file mode 100644 index 0000000000..f9fd666b09 --- /dev/null +++ b/hal/src/test/python/test_hal_simulation.py @@ -0,0 +1,41 @@ +import hal +import hal.simulation + +import typing + + +def test_value_changed_callback(): + + recv: typing.Optional[typing.Tuple[bool, str, int]] = None + + def created_cb( + name: str, handle: int, direction: hal.SimValueDirection, value: hal.Value + ): + nonlocal recv + recv = (True, name, value.value) + + def cb(name: str, handle: int, direction: hal.SimValueDirection, value: hal.Value): + nonlocal recv + recv = (False, name, value.value) + + dev = hal.SimDevice("simd") + + # Must keep the returned value alive or the callback will be unregistered + devunused = hal.simulation.registerSimValueCreatedCallback(dev, created_cb, True) + assert recv is None + + val = dev.createInt("answer", 0, 42) + + assert recv == (True, "answer", 42) + recv = None + + # Must keep the returned value alive or the callback will be unregistered + unused = hal.simulation.registerSimValueChangedCallback(val, cb, True) + + assert recv == (False, "answer", 42) + recv = None + + val.set(84) + + assert recv == (False, "answer", 84) + recv = None diff --git a/ntcore/.styleguide b/ntcore/.styleguide index 13634e44df..7715fa6362 100644 --- a/ntcore/.styleguide +++ b/ntcore/.styleguide @@ -16,6 +16,9 @@ generatedFileExclude { ntcore/doc/ ntcore/src/generated .*\.jinja + + src/main/python/ + src/test/python/ } repoRootNameOverride { diff --git a/ntcore/src/main/python/README.md b/ntcore/src/main/python/README.md new file mode 100644 index 0000000000..6c83bf570c --- /dev/null +++ b/ntcore/src/main/python/README.md @@ -0,0 +1,4 @@ +pyntcore +======== + +Python pybind11 wrappers around the C++ ntcore library. diff --git a/ntcore/src/main/python/devtools/gen-pubsub.py b/ntcore/src/main/python/devtools/gen-pubsub.py new file mode 100755 index 0000000000..5a2ccaa62f --- /dev/null +++ b/ntcore/src/main/python/devtools/gen-pubsub.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 + +import sphinxify +from cxxheaderparser.simple import parse_string +from cxxheaderparser.tokfmt import tokfmt + +if __name__ == "__main__": + with open("ntcore/include/ntcore_cpp.h") as fp: + data = parse_string(fp.read()) + + for c in data.namespace.namespaces["nt"].classes: + if c.class_decl.typename.format() == "struct PubSubOptions": + params = [] + docs = [] + + for f in c.fields: + if f.static or f.name == "structSize": + continue + + if f.type.format() == "NT_Publisher": + params.append( + ( + "std::optional>", + f.name, + f"{f.name}.has_value() ? {f.name}.value()->GetHandle() : {f.value.format()}", + "std::nullopt", + ) + ) + else: + v = f.value.format() + if v == "kDefaultPeriodic": + v = f"nt::PubSubOptions::{v}" + params.append((f.type, f.name, f.name, v)) + + if f.doxygen: + docs.append(f"@param {f.name} {f.doxygen}") + + paramstr = ",\n ".join(f"{t.format()} {n}" for t, n, _, _ in params) + args = ",\n ".join(f'py::arg("{n}") = {v}' for _, n, _, v in params) + options = ",\n ".join(f".{fn} = {n}" for _, fn, n, _ in params) + + doc = "\n ".join( + sphinxify.process_raw("\n".join(docs)).splitlines() + ) + + print( + f""" + // autogenerated by gen-pubsub.py + .def(py::init([]( + {paramstr} + ) -> nt::PubSubOptions {{ + return nt::PubSubOptions{{ + {options} + }}; + }}), + py::kw_only(), + {args}, + R"( + {doc} + )" + ) + + """ + ) diff --git a/ntcore/src/main/python/native-pyproject.toml b/ntcore/src/main/python/native-pyproject.toml new file mode 100644 index 0000000000..ce3dd6415c --- /dev/null +++ b/ntcore/src/main/python/native-pyproject.toml @@ -0,0 +1,43 @@ +[build-system] +build-backend = "hatchling.build" +requires = [ + "hatchling", + "hatch-nativelib~=0.2.0", + "hatch-robotpy~=0.2.1", + "robotpy-native-wpiutil==2027.0.0a2", + "robotpy-native-wpinet==2027.0.0a2", + "robotpy-native-datalog==2027.0.0a2", +] + +[project] +name = "robotpy-native-ntcore" +version = "2027.0.0a2" +description = "WPILib NetworkTables Library" +license = "BSD-3-Clause" + +dependencies = [ + "robotpy-native-wpiutil==2027.0.0a2", + "robotpy-native-wpinet==2027.0.0a2", + "robotpy-native-datalog==2027.0.0a2", +] + +[tool.hatch.build.targets.wheel] +packages = ["src/native"] + +[[tool.hatch.build.hooks.robotpy.maven_lib_download]] +artifact_id = "ntcore-cpp" +group_id = "edu.wpi.first.ntcore" +repo_url = "https://frcmaven.wpi.edu/artifactory/release-2027" +version = "2027.0.0-alpha-2" + +extract_to = "src/native/ntcore" +libs = ["ntcore"] + +[[tool.hatch.build.hooks.nativelib.pcfile]] +pcfile = "src/native/ntcore/robotpy-native-ntcore.pc" +name = "ntcore" + +includedir = "src/native/ntcore/include" +libdir = "src/native/ntcore/lib" +shared_libraries = ["ntcore"] +requires = ["robotpy-native-datalog", "robotpy-native-wpinet", "robotpy-native-wpiutil"] diff --git a/ntcore/src/main/python/ntcore/__init__.py b/ntcore/src/main/python/ntcore/__init__.py new file mode 100644 index 0000000000..76a9905bf8 --- /dev/null +++ b/ntcore/src/main/python/ntcore/__init__.py @@ -0,0 +1,195 @@ +from . import _init__ntcore + +# autogenerated by 'semiwrap create-imports ntcore ntcore._ntcore' +from ._ntcore import ( + BooleanArrayEntry, + BooleanArrayPublisher, + BooleanArraySubscriber, + BooleanArrayTopic, + BooleanEntry, + BooleanPublisher, + BooleanSubscriber, + BooleanTopic, + ConnectionInfo, + DoubleArrayEntry, + DoubleArrayPublisher, + DoubleArraySubscriber, + DoubleArrayTopic, + DoubleEntry, + DoublePublisher, + DoubleSubscriber, + DoubleTopic, + Event, + EventFlags, + FloatArrayEntry, + FloatArrayPublisher, + FloatArraySubscriber, + FloatArrayTopic, + FloatEntry, + FloatPublisher, + FloatSubscriber, + FloatTopic, + GenericEntry, + GenericPublisher, + GenericSubscriber, + IntegerArrayEntry, + IntegerArrayPublisher, + IntegerArraySubscriber, + IntegerArrayTopic, + IntegerEntry, + IntegerPublisher, + IntegerSubscriber, + IntegerTopic, + LogMessage, + MultiSubscriber, + NTSendable, + NTSendableBuilder, + NetworkTable, + NetworkTableEntry, + NetworkTableInstance, + NetworkTableListener, + NetworkTableListenerPoller, + NetworkTableType, + PubSubOptions, + Publisher, + RawEntry, + RawPublisher, + RawSubscriber, + RawTopic, + StringArrayEntry, + StringArrayPublisher, + StringArraySubscriber, + StringArrayTopic, + StringEntry, + StringPublisher, + StringSubscriber, + StringTopic, + StructArrayEntry, + StructArrayPublisher, + StructArraySubscriber, + StructArrayTopic, + StructEntry, + StructPublisher, + StructSubscriber, + StructTopic, + Subscriber, + TimeSyncEventData, + TimestampedBoolean, + TimestampedBooleanArray, + TimestampedDouble, + TimestampedDoubleArray, + TimestampedFloat, + TimestampedFloatArray, + TimestampedInteger, + TimestampedIntegerArray, + TimestampedRaw, + TimestampedString, + TimestampedStringArray, + TimestampedStruct, + TimestampedStructArray, + Topic, + TopicInfo, + Value, + ValueEventData, +) + +__all__ = [ + "BooleanArrayEntry", + "BooleanArrayPublisher", + "BooleanArraySubscriber", + "BooleanArrayTopic", + "BooleanEntry", + "BooleanPublisher", + "BooleanSubscriber", + "BooleanTopic", + "ConnectionInfo", + "DoubleArrayEntry", + "DoubleArrayPublisher", + "DoubleArraySubscriber", + "DoubleArrayTopic", + "DoubleEntry", + "DoublePublisher", + "DoubleSubscriber", + "DoubleTopic", + "Event", + "EventFlags", + "FloatArrayEntry", + "FloatArrayPublisher", + "FloatArraySubscriber", + "FloatArrayTopic", + "FloatEntry", + "FloatPublisher", + "FloatSubscriber", + "FloatTopic", + "GenericEntry", + "GenericPublisher", + "GenericSubscriber", + "IntegerArrayEntry", + "IntegerArrayPublisher", + "IntegerArraySubscriber", + "IntegerArrayTopic", + "IntegerEntry", + "IntegerPublisher", + "IntegerSubscriber", + "IntegerTopic", + "LogMessage", + "MultiSubscriber", + "NTSendable", + "NTSendableBuilder", + "NetworkTable", + "NetworkTableEntry", + "NetworkTableInstance", + "NetworkTableListener", + "NetworkTableListenerPoller", + "NetworkTableType", + "PubSubOptions", + "Publisher", + "RawEntry", + "RawPublisher", + "RawSubscriber", + "RawTopic", + "StringArrayEntry", + "StringArrayPublisher", + "StringArraySubscriber", + "StringArrayTopic", + "StringEntry", + "StringPublisher", + "StringSubscriber", + "StringTopic", + "StructArrayEntry", + "StructArrayPublisher", + "StructArraySubscriber", + "StructArrayTopic", + "StructEntry", + "StructPublisher", + "StructSubscriber", + "StructTopic", + "Subscriber", + "TimeSyncEventData", + "TimestampedBoolean", + "TimestampedBooleanArray", + "TimestampedDouble", + "TimestampedDoubleArray", + "TimestampedFloat", + "TimestampedFloatArray", + "TimestampedInteger", + "TimestampedIntegerArray", + "TimestampedRaw", + "TimestampedString", + "TimestampedStringArray", + "TimestampedStruct", + "TimestampedStructArray", + "Topic", + "TopicInfo", + "Value", + "ValueEventData", +] + +from ._ntcore import _now, _setNow + +__all__ += ["_now", "_setNow"] + +try: + from .version import version as __version__ +except ImportError: + __version__ = "master" diff --git a/ntcore/src/main/python/ntcore/_logutil.py b/ntcore/src/main/python/ntcore/_logutil.py new file mode 100644 index 0000000000..db36bdf132 --- /dev/null +++ b/ntcore/src/main/python/ntcore/_logutil.py @@ -0,0 +1,123 @@ +import atexit +import logging +import threading + +from . import _ntcore + +import wpiutil.sync + + +class InstanceAlreadyStartedError(Exception): + pass + + +class NtLogForwarder: + """ + Forwards ntcore's logger to python's logging system + """ + + _instlock = threading.Lock() + _instances = {} + _instcfg = {} + + @classmethod + def config_logging( + cls, + instance: _ntcore.NetworkTableInstance, + minLevel: _ntcore.NetworkTableInstance.LogLevel, + maxLevel: _ntcore.NetworkTableInstance.LogLevel, + logName: str, + ): + handle = instance._getHandle() + with cls._instlock: + if handle in cls._instances: + raise InstanceAlreadyStartedError( + "cannot configure logging after instance has been started" + ) + + cls._instcfg[handle] = (minLevel, maxLevel, logName) + + @classmethod + def onInstanceStart(cls, instance: _ntcore.NetworkTableInstance): + handle = instance._getHandle() + with cls._instlock: + if handle in cls._instances: + return + + default_cfg = ( + _ntcore.NetworkTableInstance.LogLevel.kLogInfo, + _ntcore.NetworkTableInstance.LogLevel.kLogCritical, + "nt", + ) + minLevel, maxLevel, logName = cls._instcfg.get(handle, default_cfg) + + cls._instances[handle] = cls(instance, logName, minLevel, maxLevel) + + @classmethod + def onInstanceDestroy(cls, instance: _ntcore.NetworkTableInstance): + handle = instance._getHandle() + with cls._instlock: + lfwd = cls._instances.pop(handle, None) + if lfwd: + lfwd.destroy() + + def __init__( + self, + instance: _ntcore.NetworkTableInstance, + logName: str, + minLevel: _ntcore.NetworkTableInstance.LogLevel, + maxLevel: _ntcore.NetworkTableInstance.LogLevel, + ): + self.lock = threading.Lock() + self.poller = _ntcore.NetworkTableListenerPoller(instance) + ntLogger = self.poller.addLogger(minLevel, maxLevel) + + self.thread = threading.Thread( + target=self._logging_thread, + name=logName + "-log-thread", + daemon=True, + args=(self.poller, logName, ntLogger), + ) + self.thread.start() + + atexit.register(self.destroy) + + def _logging_thread( + self, poller: _ntcore.NetworkTableListenerPoller, logName: str, ntLogger: int + ): + logger = logging.getLogger(logName) + + _waitForObject = wpiutil.sync.waitForObject + handle = poller.getHandle() + + while True: + if not _waitForObject(handle): + break + + messages = poller.readQueue() + if not messages: + continue + + for msg in messages: + msg = msg.data + if logger.isEnabledFor(msg.level): + lr = logger.makeRecord( + logName, + msg.level, + msg.filename, + msg.line, + "%s", + (msg.message,), + None, + ) + logger.handle(lr) + + def destroy(self): + with self.lock: + if self.poller: + self.poller.close() + self.thread.join(timeout=1) + self.poller = None + + +_config_logging = NtLogForwarder.config_logging diff --git a/ntcore/src/main/python/ntcore/meta/__init__.py b/ntcore/src/main/python/ntcore/meta/__init__.py new file mode 100644 index 0000000000..73453762d5 --- /dev/null +++ b/ntcore/src/main/python/ntcore/meta/__init__.py @@ -0,0 +1,28 @@ +# autogenerated by 'semiwrap create-imports ntcore.meta ntcore._ntcore.meta' +from .._ntcore.meta import ( + Client, + ClientPublisher, + ClientSubscriber, + SubscriberOptions, + TopicPublisher, + TopicSubscriber, + decodeClientPublishers, + decodeClientSubscribers, + decodeClients, + decodeTopicPublishers, + decodeTopicSubscribers, +) + +__all__ = [ + "Client", + "ClientPublisher", + "ClientSubscriber", + "SubscriberOptions", + "TopicPublisher", + "TopicSubscriber", + "decodeClientPublishers", + "decodeClientSubscribers", + "decodeClients", + "decodeTopicPublishers", + "decodeTopicSubscribers", +] diff --git a/ntcore/src/main/python/ntcore/py.typed b/ntcore/src/main/python/ntcore/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/ntcore/src/main/python/ntcore/src/NetworkTable.cpp.inl b/ntcore/src/main/python/ntcore/src/NetworkTable.cpp.inl new file mode 100644 index 0000000000..1c127232e1 --- /dev/null +++ b/ntcore/src/main/python/ntcore/src/NetworkTable.cpp.inl @@ -0,0 +1,56 @@ +cls_NetworkTable + .def("getValue", [](const NetworkTable &self, std::string_view key, py::object defaultValue) -> py::object { + nt::NetworkTableEntry entry; + { + py::gil_scoped_release release; + entry = self.GetEntry(key); + } + return pyntcore::GetValueEntry(entry, defaultValue); + }, py::arg("key"), py::arg("value")) + + // double overload must come before boolean version + .def("putValue", [](nt::NetworkTable *self, std::string_view key, double value) { + return self->PutValue(key, nt::Value::MakeDouble(value)); + }, py::arg("key"), py::arg("value"), release_gil()) + .def("putValue", [](nt::NetworkTable *self, std::string_view key, bool value) { + return self->PutValue(key, nt::Value::MakeBoolean(value)); + }, py::arg("key"), py::arg("value"), release_gil()) + .def("putValue", [](nt::NetworkTable *self, std::string_view key, py::bytes value) { + auto v = nt::Value::MakeRaw(value.cast>()); + py::gil_scoped_release release; + return self->PutValue(key, v); + }, py::arg("key"), py::arg("value")) + .def("putValue", [](nt::NetworkTable *self, std::string_view key, std::string value) { + return self->PutValue(key, nt::Value::MakeString(std::move(value))); + }, py::arg("key"), py::arg("value"), release_gil()) + .def("putValue", [](nt::NetworkTable *self, std::string_view key, py::sequence value) { + auto v = pyntcore::py2ntvalue(value); + py::gil_scoped_release release; + return self->PutValue(key, v); + }, py::arg("key"), py::arg("value")) + + // double overload must come before boolean version + .def("setDefaultValue", [](nt::NetworkTable *self, std::string_view key, double value) { + return self->SetDefaultValue(key, nt::Value::MakeDouble(value)); + }, py::arg("key"), py::arg("value"), release_gil()) + .def("setDefaultValue", [](nt::NetworkTable *self, std::string_view key, bool value) { + return self->SetDefaultValue(key, nt::Value::MakeBoolean(value)); + }, py::arg("key"), py::arg("value"), release_gil()) + .def("setDefaultValue", [](nt::NetworkTable *self, std::string_view key, py::bytes value) { + auto v = nt::Value::MakeRaw(value.cast>()); + py::gil_scoped_release release; + return self->SetDefaultValue(key, v); + }, py::arg("key"), py::arg("value")) + .def("setDefaultValue", [](nt::NetworkTable *self, std::string_view key, std::string value) { + return self->SetDefaultValue(key, nt::Value::MakeString(std::move(value))); + }, py::arg("key"), py::arg("value"), release_gil()) + .def("setDefaultValue", [](nt::NetworkTable *self, std::string_view key, py::sequence value) { + auto v = pyntcore::py2ntvalue(value); + py::gil_scoped_release release; + return self->SetDefaultValue(key, v); + }, py::arg("key"), py::arg("value")) + + .def("__contains__", [](const nt::NetworkTable &self, std::string_view key) -> bool { + return self.ContainsKey(key); + }, release_gil()) +; diff --git a/ntcore/src/main/python/ntcore/src/NetworkTableEntry.cpp.inl b/ntcore/src/main/python/ntcore/src/NetworkTableEntry.cpp.inl new file mode 100644 index 0000000000..a0e0e60a0e --- /dev/null +++ b/ntcore/src/main/python/ntcore/src/NetworkTableEntry.cpp.inl @@ -0,0 +1,48 @@ +cls_NetworkTableEntry + .def_property_readonly("value", [](const nt::NetworkTableEntry &self) { + nt::Value v; + { + py::gil_scoped_release release; + v = self.GetValue(); + } + return pyntcore::ntvalue2py(v); + }) + + // double overload must come before boolean version + .def("setValue", [](nt::NetworkTableEntry *self, double value) { + return self->SetValue(nt::Value::MakeDouble(value)); + }, py::arg("value"), release_gil()) + .def("setValue", [](nt::NetworkTableEntry *self, bool value) { + return self->SetValue(nt::Value::MakeBoolean(value)); + }, py::arg("value"), release_gil()) + .def("setValue", [](nt::NetworkTableEntry *self, py::bytes value) { + auto v = nt::Value::MakeRaw(value.cast>()); + py::gil_scoped_release release; + return self->SetValue(v); + }, py::arg("value")) + .def("setValue", [](nt::NetworkTableEntry *self, std::string value) { + return self->SetValue(nt::Value::MakeString(value)); + }, py::arg("value"), release_gil()) + .def("setValue", [](nt::NetworkTableEntry *self, py::sequence value) { + return self->SetValue(pyntcore::py2ntvalue(value)); + }, py::arg("value")) + + // double overload must come before boolean version + .def("setDefaultValue", [](nt::NetworkTableEntry *self, double value) { + return self->SetDefaultValue(nt::Value::MakeDouble(value)); + }, py::arg("value"), release_gil()) + .def("setDefaultValue", [](nt::NetworkTableEntry *self, bool value) { + return self->SetDefaultValue(nt::Value::MakeBoolean(value)); + }, py::arg("value"), release_gil()) + .def("setDefaultValue", [](nt::NetworkTableEntry *self, py::bytes value) { + auto v = nt::Value::MakeRaw(value.cast>()); + py::gil_scoped_release release; + return self->SetDefaultValue(v); + }, py::arg("value")) + .def("setDefaultValue", [](nt::NetworkTableEntry *self, std::string value) { + return self->SetDefaultValue(nt::Value::MakeString(value)); + }, py::arg("value"), release_gil()) + .def("setDefaultValue", [](nt::NetworkTableEntry *self, py::sequence value) { + return self->SetDefaultValue(pyntcore::py2ntvalue(value)); + }, py::arg("value")) +; diff --git a/ntcore/src/main/python/ntcore/src/nt_instance.cpp b/ntcore/src/main/python/ntcore/src/nt_instance.cpp new file mode 100644 index 0000000000..9924c44da2 --- /dev/null +++ b/ntcore/src/main/python/ntcore/src/nt_instance.cpp @@ -0,0 +1,56 @@ +#include +#include "nt_instance.h" +#include "ntcore_cpp.h" + +#include + +// only accessed under GIL +static std::set g_known_instances; + +namespace pyntcore { + +void onInstanceStart(nt::NetworkTableInstance *instance) { + g_known_instances.emplace(instance->GetHandle()); + + py::module::import("ntcore._logutil") + .attr("NtLogForwarder").attr("onInstanceStart")(instance); +} + +void onInstancePreReset(nt::NetworkTableInstance *instance) { + py::module::import("ntcore._logutil") + .attr("NtLogForwarder").attr("onInstanceDestroy")(instance); +} + +void onInstancePostReset(nt::NetworkTableInstance *instance) { + py::module::import("ntcore.util") + .attr("_NtProperty").attr("onInstancePostReset")(instance); +} + +void onInstanceDestroy(nt::NetworkTableInstance *instance) { + py::module::import("ntcore._logutil") + .attr("NtLogForwarder").attr("onInstanceDestroy")(instance); + py::module::import("ntcore.util") + .attr("_NtProperty").attr("onInstanceDestroy")(instance); + + g_known_instances.erase(instance->GetHandle()); +} + +// reset all instances to clear out any potential python references that +// might be hanging around in a callback or something +void resetAllInstances() +{ + std::set known_instances; + known_instances.swap(g_known_instances); + + // always reset the default instance + known_instances.emplace(nt::GetDefaultInstance()); + + py::gil_scoped_release unlock; + + for (auto &inst: known_instances) { + nt::ResetInstance(inst); + } +} + + +}; // namespace pyntcore \ No newline at end of file diff --git a/ntcore/src/main/python/ntcore/src/nt_instance.h b/ntcore/src/main/python/ntcore/src/nt_instance.h new file mode 100644 index 0000000000..67b51da98e --- /dev/null +++ b/ntcore/src/main/python/ntcore/src/nt_instance.h @@ -0,0 +1,16 @@ + +#pragma once + +#include +#include + +namespace pyntcore { + +void onInstanceStart(nt::NetworkTableInstance *instance); +void onInstancePreReset(nt::NetworkTableInstance *instance); +void onInstancePostReset(nt::NetworkTableInstance *instance); +void onInstanceDestroy(nt::NetworkTableInstance *instance); + +void resetAllInstances(); + +}; // namespace pyntcore diff --git a/ntcore/src/main/python/ntcore/src/nt_type_caster.h b/ntcore/src/main/python/ntcore/src/nt_type_caster.h new file mode 100644 index 0000000000..de9158f29c --- /dev/null +++ b/ntcore/src/main/python/ntcore/src/nt_type_caster.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { + +// ntcore uses std::vector anytime there is a raw value, so +// add this specialization to convert to/from bytes directly + +template<> +struct type_caster> { + using vector_type = std::vector; + PYBIND11_TYPE_CASTER(vector_type, const_name("bytes")); + + bool load(handle src, bool convert) { + if (!isinstance(src)) { + return false; + } + auto buf = reinterpret_borrow(src); + auto req = buf.request(); + if (req.ndim != 1) { + return false; + } + + auto begin = (const uint8_t*)req.ptr; + auto end = begin + req.size*req.itemsize; + + value = std::vector(begin, end); + return true; + } + + static handle cast(const std::vector &src, return_value_policy policy, handle parent) { + return py::bytes((char*)src.data(), src.size()).release(); + } +}; + +} +} \ No newline at end of file diff --git a/ntcore/src/main/python/ntcore/src/ntcore.cpp b/ntcore/src/main/python/ntcore/src/ntcore.cpp new file mode 100644 index 0000000000..169563a1e2 --- /dev/null +++ b/ntcore/src/main/python/ntcore/src/ntcore.cpp @@ -0,0 +1,14 @@ + +#include +#include "nt_instance.h" + +SEMIWRAP_PYBIND11_MODULE(m) { + initWrapper(m); + + static int unused; + py::capsule cleanup(&unused, [](void *) { + pyntcore::resetAllInstances(); + }); + + m.add_object("_st_cleanup", cleanup); +} \ No newline at end of file diff --git a/ntcore/src/main/python/ntcore/src/py2value.cpp b/ntcore/src/main/python/ntcore/src/py2value.cpp new file mode 100644 index 0000000000..0634c40f6f --- /dev/null +++ b/ntcore/src/main/python/ntcore/src/py2value.cpp @@ -0,0 +1,180 @@ + +#include "py2value.h" + +#include + +// type casters +#include +#include + + + +namespace pyntcore { + +const char * nttype2str(NT_Type type) { + switch (type) { + case NT_BOOLEAN: + return "bool"; + case NT_DOUBLE: + return "double"; + case NT_STRING: + return "string"; + case NT_RAW: + return "raw"; + case NT_BOOLEAN_ARRAY: + return "bool[]"; + case NT_DOUBLE_ARRAY: + return "double[]"; + case NT_STRING_ARRAY: + return "string[]"; + case NT_INTEGER: + return "int"; + case NT_FLOAT: + return "float"; + case NT_INTEGER_ARRAY: + return "int[]"; + case NT_FLOAT_ARRAY: + return "float[]"; + default: + return "invalid"; + } +} + + +py::object ntvalue2py(const nt::Value &ntvalue) { + auto &v = ntvalue.value(); + switch (v.type) { + case NT_BOOLEAN: + return py::bool_(v.data.v_boolean); + + case NT_DOUBLE: + return py::float_(v.data.v_double); + + case NT_STRING: + return py::str(v.data.v_string.str, v.data.v_string.len); + + case NT_RAW: + return py::bytes((const char *)v.data.v_raw.data, v.data.v_raw.size); + + case NT_BOOLEAN_ARRAY: { + py::list l(v.data.arr_boolean.size); + for (size_t i = 0; i < v.data.arr_boolean.size; i++) { + auto b = py::bool_(v.data.arr_boolean.arr[i]); + PyList_SET_ITEM(l.ptr(), i, b.release().ptr()); + } + return std::move(l); + } + + case NT_DOUBLE_ARRAY: { + py::list l(v.data.arr_double.size); + for (size_t i = 0; i < v.data.arr_double.size; i++) { + auto d = py::float_(v.data.arr_double.arr[i]); + PyList_SET_ITEM(l.ptr(), i, d.release().ptr()); + } + return std::move(l); + } + + case NT_STRING_ARRAY: { + return py::cast(ntvalue.GetStringArray()); + } + + case NT_INTEGER: { + return py::int_(v.data.v_int); + } + + case NT_FLOAT: { + return py::float_(v.data.v_float); + } + + case NT_INTEGER_ARRAY: { + py::list l(v.data.arr_int.size); + for (size_t i = 0; i < v.data.arr_int.size; i++) { + auto d = py::int_(v.data.arr_int.arr[i]); + PyList_SET_ITEM(l.ptr(), i, d.release().ptr()); + } + return std::move(l); + } + + case NT_FLOAT_ARRAY: { + py::list l(v.data.arr_float.size); + for (size_t i = 0; i < v.data.arr_float.size; i++) { + auto d = py::float_(v.data.arr_float.arr[i]); + PyList_SET_ITEM(l.ptr(), i, d.release().ptr()); + } + return std::move(l); + } + + default: + return py::none(); + } +} + +nt::Value py2ntvalue(py::handle h) { + if (py::isinstance(h)) { + return nt::Value::MakeBoolean(h.cast()); + } else if (py::isinstance(h)) { + return nt::Value::MakeDouble(h.cast()); + } else if (py::isinstance(h)) { + return nt::Value::MakeInteger(h.cast()); + } else if (py::isinstance(h)) { + return nt::Value::MakeString(h.cast()); + } else if (py::isinstance(h)) { + return nt::Value::MakeRaw(h.cast>()); + } else if (py::isinstance(h)) { + throw py::value_error("Cannot put None into NetworkTable"); + } + + auto seq = h.cast(); + if (seq.size() == 0) { + throw py::type_error("If you use a list here, cannot be empty"); + } + // check the first item + auto i1 = seq[0]; + if (py::isinstance(i1)) { + auto v = h.cast>(); + return nt::Value::MakeBooleanArray(v); + } else if (py::isinstance(i1)) { + auto v = h.cast>(); + return nt::Value::MakeDoubleArray(v); + } else if (py::isinstance(i1)) { + auto v = h.cast>(); + return nt::Value::MakeIntegerArray(v); + } else if (py::isinstance(i1)) { + auto v = h.cast>(); + return nt::Value::MakeStringArray(v); + } else { + throw py::value_error("Can only put bool/int/float/str/bytes or lists/tuples of them"); + } +} + +py::function valueFactoryByType(nt::NetworkTableType type) { + py::object PyNtValue = py::module::import("ntcore").attr("Value"); + switch (type) { + case nt::NetworkTableType::kBoolean: + return PyNtValue.attr("makeBoolean"); + case nt::NetworkTableType::kDouble: + return PyNtValue.attr("makeDouble"); + case nt::NetworkTableType::kString: + return PyNtValue.attr("makeString"); + case nt::NetworkTableType::kRaw: + return PyNtValue.attr("makeRaw"); + case nt::NetworkTableType::kBooleanArray: + return PyNtValue.attr("makeBooleanArray"); + case nt::NetworkTableType::kDoubleArray: + return PyNtValue.attr("makeDoubleArray"); + case nt::NetworkTableType::kStringArray: + return PyNtValue.attr("makeStringArray"); + case nt::NetworkTableType::kInteger: + return PyNtValue.attr("makeInteger"); + case nt::NetworkTableType::kFloat: + return PyNtValue.attr("makeFloat"); + case nt::NetworkTableType::kIntegerArray: + return PyNtValue.attr("makeIntegerArray"); + case nt::NetworkTableType::kFloatArray: + return PyNtValue.attr("makeFloatArray"); + default: + throw py::type_error("empty nt value"); + } +} + +} diff --git a/ntcore/src/main/python/ntcore/src/py2value.h b/ntcore/src/main/python/ntcore/src/py2value.h new file mode 100644 index 0000000000..410ba5a7c8 --- /dev/null +++ b/ntcore/src/main/python/ntcore/src/py2value.h @@ -0,0 +1,25 @@ + +#include +#include +#include +#include + +namespace pyntcore { + +const char * nttype2str(NT_Type type); + +py::object ntvalue2py(const nt::Value &ntvalue); + +nt::Value py2ntvalue(py::handle h); + +py::function valueFactoryByType(nt::NetworkTableType type); + +inline void ensure_value_is(NT_Type expected, nt::Value *v) { + if (v->type() != expected) { + throw py::value_error(fmt::format( + "Value type is {}, not {}", nttype2str(v->type()), nttype2str(expected) + )); + } +} + +}; \ No newline at end of file diff --git a/ntcore/src/main/python/ntcore/src/pyentry.cpp b/ntcore/src/main/python/ntcore/src/pyentry.cpp new file mode 100644 index 0000000000..f9cad5b33e --- /dev/null +++ b/ntcore/src/main/python/ntcore/src/pyentry.cpp @@ -0,0 +1,141 @@ + +#include "pyentry.h" +#include "py2value.h" + +#include +#include + +namespace pyntcore { + +py::object GetBooleanEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) { + nt::Value value; + { + py::gil_scoped_release release; + value = nt::GetEntryValue(entry.GetHandle()); + } + if (!value || value.type() != NT_BOOLEAN) return defaultValue; + return py::cast(value.GetBoolean()); +} + +py::object GetDoubleEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) { + nt::Value value; + { + py::gil_scoped_release release; + value = nt::GetEntryValue(entry.GetHandle()); + } + if (!value || value.type() != NT_DOUBLE) return defaultValue; + return py::cast(value.GetDouble()); +} + +py::object GetFloatEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) { + nt::Value value; + { + py::gil_scoped_release release; + value = nt::GetEntryValue(entry.GetHandle()); + } + if (!value || value.type() != NT_FLOAT) return defaultValue; + return py::cast(value.GetFloat()); +} + +py::object GetIntegerEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) { + nt::Value value; + { + py::gil_scoped_release release; + value = nt::GetEntryValue(entry.GetHandle()); + } + if (!value || value.type() != NT_INTEGER) return defaultValue; + return py::cast(value.GetInteger()); +} + + +py::object GetStringEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) { + nt::Value value; + { + py::gil_scoped_release release; + value = nt::GetEntryValue(entry.GetHandle()); + } + if (!value || value.type() != NT_STRING) return defaultValue; + auto s = value.GetString(); + return py::str(s.data(), s.size()); +} + +py::object GetRawEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) { + nt::Value value; + { + py::gil_scoped_release release; + value = nt::GetEntryValue(entry.GetHandle()); + } + if (!value || value.type() != NT_RAW) return defaultValue; + return py::cast(value.GetRaw()); +} + +py::object GetBooleanArrayEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) { + nt::Value value; + { + py::gil_scoped_release release; + value = nt::GetEntryValue(entry.GetHandle()); + } + if (!value || value.type() != NT_BOOLEAN_ARRAY) return defaultValue; + // ntcore will return bit vector by default. Convert to List[bool] + auto v = value.value(); + py::list l(v.data.arr_boolean.size); + for (size_t i = 0; i < v.data.arr_boolean.size; i++) { + auto b = py::bool_(v.data.arr_boolean.arr[i]); + PyList_SET_ITEM(l.ptr(), i, b.release().ptr()); + } + return std::move(l); +} + +py::object GetDoubleArrayEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) { + nt::Value value; + { + py::gil_scoped_release release; + value = nt::GetEntryValue(entry.GetHandle()); + } + if (!value || value.type() != NT_DOUBLE_ARRAY) return defaultValue; + return py::cast(value.GetDoubleArray()); +} + +py::object GetFloatArrayEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) { + nt::Value value; + { + py::gil_scoped_release release; + value = nt::GetEntryValue(entry.GetHandle()); + } + if (!value || value.type() != NT_FLOAT_ARRAY) return defaultValue; + return py::cast(value.GetFloatArray()); +} + +py::object GetIntegerArrayEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) { + nt::Value value; + { + py::gil_scoped_release release; + value = nt::GetEntryValue(entry.GetHandle()); + } + if (!value || value.type() != NT_INTEGER_ARRAY) return defaultValue; + return py::cast(value.GetIntegerArray()); +} + +py::object GetStringArrayEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) { + nt::Value value; + { + py::gil_scoped_release release; + value = nt::GetEntryValue(entry.GetHandle()); + } + if (!value || value.type() != NT_STRING_ARRAY) return defaultValue; + std::span rval = value.GetStringArray(); + return py::cast(rval); +} + +py::object GetValueEntry(const nt::NetworkTableEntry &entry, py::object defaultValue) { + nt::Value value; + { + py::gil_scoped_release release; + value = nt::GetEntryValue(entry.GetHandle()); + } + if (!value) return defaultValue; + return ntvalue2py(value); +} + + +}; // pyntcore diff --git a/ntcore/src/main/python/ntcore/src/pyentry.h b/ntcore/src/main/python/ntcore/src/pyentry.h new file mode 100644 index 0000000000..8693ccfe70 --- /dev/null +++ b/ntcore/src/main/python/ntcore/src/pyentry.h @@ -0,0 +1,21 @@ + +#include +#include +#include + +namespace pyntcore { + +py::object GetBooleanEntry(const nt::NetworkTableEntry &entry, py::object defaultValue); +py::object GetDoubleEntry(const nt::NetworkTableEntry &entry, py::object defaultValue); +py::object GetFloatEntry(const nt::NetworkTableEntry &entry, py::object defaultValue); +py::object GetIntegerEntry(const nt::NetworkTableEntry &entry, py::object defaultValue); +py::object GetStringEntry(const nt::NetworkTableEntry &entry, py::object defaultValue); +py::object GetRawEntry(const nt::NetworkTableEntry &entry, py::object defaultValue); +py::object GetBooleanArrayEntry(const nt::NetworkTableEntry &entry, py::object defaultValue); +py::object GetDoubleArrayEntry(const nt::NetworkTableEntry &entry, py::object defaultValue); +py::object GetFloatArrayEntry(const nt::NetworkTableEntry &entry, py::object defaultValue); +py::object GetIntegerArrayEntry(const nt::NetworkTableEntry &entry, py::object defaultValue); +py::object GetStringArrayEntry(const nt::NetworkTableEntry &entry, py::object defaultValue); +py::object GetValueEntry(const nt::NetworkTableEntry &entry, py::object defaultValue); + +}; diff --git a/ntcore/src/main/python/ntcore/types.py b/ntcore/src/main/python/ntcore/types.py new file mode 100644 index 0000000000..8105039b4e --- /dev/null +++ b/ntcore/src/main/python/ntcore/types.py @@ -0,0 +1,13 @@ +from typing import Sequence, Union + +ValueT = Union[ + bool, + int, + float, + str, + bytes, + Sequence[bool], + Sequence[int], + Sequence[float], + Sequence[str], +] diff --git a/ntcore/src/main/python/ntcore/util.py b/ntcore/src/main/python/ntcore/util.py new file mode 100644 index 0000000000..39242b9a20 --- /dev/null +++ b/ntcore/src/main/python/ntcore/util.py @@ -0,0 +1,224 @@ +import threading +import weakref + +from typing import Callable, Dict, Optional, Sequence + +from ._ntcore import NetworkTableInstance, NetworkTableEntry, NetworkTableType, Value + +__all__ = ["ntproperty", "ChooserControl"] + + +class _NtProperty: + """ + Don't use this directly, use @ntproperty instead + """ + + entry: NetworkTableEntry + defaultValue: Value + + _instlock = threading.Lock() + _instances: Dict[int, weakref.WeakSet] = {} + + @classmethod + def attach(cls, self: "_NtProperty", inst: NetworkTableInstance): + with cls._instlock: + handle = inst._getHandle() + props = cls._instances.get(handle) + if not props: + props = weakref.WeakSet() + cls._instances[handle] = props + props.add(self) + + @classmethod + def onInstancePostReset(cls, inst: NetworkTableInstance): + with cls._instlock: + props = cls._instances.get(inst._getHandle(), []) + for prop in props: + prop.reset() + + @classmethod + def onInstanceDestroy(cls, inst: NetworkTableInstance): + with cls._instlock: + cls._instances.pop(inst._getHandle(), None) + + def __init__( + self, + key: str, + defaultValue, + writeDefault: bool, + persistent: bool, + type: Optional[NetworkTableType], + inst: NetworkTableInstance, + ) -> None: + # Autodetect the type if not provided, and store the default + # value as that specific type + if type is None: + self.defaultValue = Value.makeValue(defaultValue) + self.mkv = Value.getFactoryByType(self.defaultValue.type()) + else: + self.mkv = Value.getFactoryByType(type) + self.defaultValue = self.mkv(defaultValue) + + self.key = key + self.writeDefault = writeDefault + self.persistent = persistent + # never overwrite persistent values with defaults + if persistent: + self.writeDefault = False + self.inst = inst + _NtProperty.attach(self, inst) + + # Set it up + self.reset() + + def reset(self): + self.entry = self.inst.getEntry(self.key) + if self.writeDefault: + print("set v", self.defaultValue) + self.entry.setValue(self.defaultValue) + else: + print("set default", self.defaultValue) + self.entry.setDefaultValue(self.defaultValue) + + if self.persistent: + self.entry.setPersistent() + + def get(self, _): + return self.entry.value + + def set(self, _, value): + self.entry.setValue(self.mkv(value)) + + +def ntproperty( + key: str, + defaultValue, + *, + writeDefault: bool = True, + doc: str = None, + persistent: bool = False, + type: Optional[NetworkTableType] = None, + inst: Optional[NetworkTableInstance] = None +) -> property: + """ + A property that you can add to your classes to access NetworkTables + variables like a normal variable. + + :param key: A full NetworkTables key (eg ``/SmartDashboard/foo``) + :param defaultValue: Default value to use if not in the table + :type defaultValue: any + :param writeDefault: If True, put the default value to the table, + overwriting existing values + :param doc: If given, will be the docstring of the property. + :param persistent: If True, persist set values across restarts. + *writeDefault* is ignored if this is True. + :param type: Specify the type of this entry. If not specified, + will autodetect the type from the default value + :param inst: The NetworkTables instance to use. + + Example usage:: + + class Foo(object): + + something = ntproperty('/SmartDashboard/something', True) + + ... + + def do_thing(self): + if self.something: # reads from value + ... + + self.something = False # writes value + + .. note:: When using empty lists/tuples, you must explicitly specify + the type. + + .. warning:: + + This function assumes that the value's type + never changes. If it does, you'll get really strange + errors... so don't do that. + """ + if inst is None: + inst = NetworkTableInstance.getDefault() + + ntprop = _NtProperty(key, defaultValue, writeDefault, persistent, type, inst) + return property(fget=ntprop.get, fset=ntprop.set, doc=doc) + + +class ChooserControl: + """ + Interacts with a :class:`wpilib.SendableChooser` + object over NetworkTables. + """ + + def __init__( + self, + key: str, + on_choices: Optional[Callable[[Sequence[str]], None]] = None, + on_selected: Optional[Callable[[str], None]] = None, + *, + inst: Optional[NetworkTableInstance] = None + ) -> None: + """ + :param key: NetworkTables key + :param on_choices: A function that will be called when the + choices change. + :param on_selection: A function that will be called when the + selection changes. + :param inst: The NetworkTables instance to use. + """ + + if inst is None: + inst = NetworkTableInstance.getDefault() + + self.subtable = inst.getTable("SmartDashboard").getSubTable(key) + + self.on_choices = on_choices + self.on_selected = on_selected + + if on_choices or on_selected: + self.subtable.addTableListener(self._on_change, True) + + def close(self) -> None: + """Stops listening for changes to the ``SendableChooser``""" + if self.on_choices or self.on_selected: + self.subtable.removeTableListener(self._on_change) + + def getChoices(self) -> Sequence[str]: + """ + Returns the current choices. If the chooser doesn't exist, this + will return an empty tuple. + """ + return self.subtable.getStringArray("options", []) + + def getSelected(self) -> Optional[str]: + """ + Returns the current selection or None + """ + selected = self.subtable.getString("selected", None) + if selected is None: + selected = self.subtable.getString("default", None) + return selected + + def setSelected(self, selection: str) -> None: + """ + Sets the active selection on the chooser + + :param selection: Active selection name + """ + self.subtable.putString("selected", selection) + + def _on_change(self, table, key, value, isNew): + if key == "options": + if self.on_choices is not None: + self.on_choices(value) + elif key == "selected": + if self.on_selected is not None: + self.on_selected(value) + elif key == "default": + if ( + self.on_selected is not None + and self.subtable.getString("selected", None) is None + ): + self.on_selected(value) diff --git a/ntcore/src/main/python/pyproject.toml b/ntcore/src/main/python/pyproject.toml new file mode 100644 index 0000000000..36de800322 --- /dev/null +++ b/ntcore/src/main/python/pyproject.toml @@ -0,0 +1,96 @@ +[build-system] +build-backend = "hatchling.build" +requires = [ + "semiwrap~=0.1.7", + "hatch-meson~=0.1.0b2", + "hatch-robotpy~=0.2.1", + "hatchling", + "robotpy-native-ntcore==2027.0.0a2", + "robotpy-wpiutil==2027.0.0a2", + "robotpy-wpinet==2027.0.0a2", + "robotpy-wpilog==2027.0.0a2", +] + + +[project] +name = "pyntcore" +version = "2027.0.0a2" +description = "Binary wrappers for the FRC ntcore library" +authors = [ + {name = "RobotPy Development Team", email = "robotpy@googlegroups.com"}, +] +license = "BSD-3-Clause" +dependencies = [ + "robotpy-native-ntcore==2027.0.0a2", + "robotpy-wpiutil==2027.0.0a2", + "robotpy-wpinet==2027.0.0a2", + "robotpy-wpilog==2027.0.0a2", +] + +[project.urls] +"Source code" = "https://github.com/robotpy/mostrobotpy" + + +[tool.hatch.build.hooks.robotpy] +version_file = "ntcore/version.py" + +[tool.hatch.build.hooks.semiwrap] + +[tool.hatch.build.hooks.meson] + +[tool.hatch.build.targets.wheel] +packages = ["ntcore"] + + +[tool.semiwrap] +update_init = [ + "ntcore", + "ntcore.meta ntcore._ntcore.meta" +] +scan_headers_ignore = [ + "networktables/ProtobufTopic.h", + "networktables/UnitTopic.h", + + "ntcore.h", + "ntcore_c.h", + "ntcore_c_types.h", + "ntcore_test.h", + + "src/*", +] + +[tool.semiwrap.extension_modules."ntcore._ntcore"] +name = "ntcore" +wraps = ["robotpy-native-ntcore"] +depends = ["wpiutil", "wpinet", "wpilog"] + + +[tool.semiwrap.extension_modules."ntcore._ntcore".headers] +# networktables +BooleanArrayTopic = "networktables/BooleanArrayTopic.h" +BooleanTopic = "networktables/BooleanTopic.h" +DoubleArrayTopic = "networktables/DoubleArrayTopic.h" +DoubleTopic = "networktables/DoubleTopic.h" +FloatArrayTopic = "networktables/FloatArrayTopic.h" +FloatTopic = "networktables/FloatTopic.h" +GenericEntry = "networktables/GenericEntry.h" +IntegerArrayTopic = "networktables/IntegerArrayTopic.h" +IntegerTopic = "networktables/IntegerTopic.h" +MultiSubscriber = "networktables/MultiSubscriber.h" +NTSendable = "networktables/NTSendable.h" +NTSendableBuilder = "networktables/NTSendableBuilder.h" +NetworkTable = "networktables/NetworkTable.h" +NetworkTableEntry = "networktables/NetworkTableEntry.h" +NetworkTableInstance = "networktables/NetworkTableInstance.h" +NetworkTableListener = "networktables/NetworkTableListener.h" +NetworkTableType = "networktables/NetworkTableType.h" +NetworkTableValue = "networktables/NetworkTableValue.h" +RawTopic = "networktables/RawTopic.h" +StructTopic = "networktables/StructTopic.h" +StructArrayTopic = "networktables/StructArrayTopic.h" +StringArrayTopic = "networktables/StringArrayTopic.h" +StringTopic = "networktables/StringTopic.h" +Topic = "networktables/Topic.h" + +ntcore_cpp = "ntcore_cpp.h" +ntcore_cpp_types = "ntcore_cpp_types.h" diff --git a/ntcore/src/main/python/semiwrap/BooleanArrayTopic.yml b/ntcore/src/main/python/semiwrap/BooleanArrayTopic.yml new file mode 100644 index 0000000000..0133c4ad7d --- /dev/null +++ b/ntcore/src/main/python/semiwrap/BooleanArrayTopic.yml @@ -0,0 +1,115 @@ +classes: + nt::BooleanArraySubscriber: + methods: + BooleanArraySubscriber: + overloads: + '': + ignore: true + NT_Subscriber, ParamType: + ignore: true + Get: + overloads: + '[const]': + ParamType [const]: + wpi::SmallVectorImpl& [const]: + ignore: true + wpi::SmallVectorImpl&, ParamType [const]: + ignore: true + GetAtomic: + overloads: + '[const]': + ParamType [const]: + wpi::SmallVectorImpl& [const]: + ignore: true + wpi::SmallVectorImpl&, ParamType [const]: + ignore: true + ReadQueue: + GetTopic: + inline_code: | + .def("close", [](BooleanArraySubscriber *self) { + py::gil_scoped_release release; + *self = BooleanArraySubscriber(); + }, py::doc("Destroys the subscriber")) + .def("__enter__", [](BooleanArraySubscriber *self) { + return self; + }) + .def("__exit__", [](BooleanArraySubscriber *self, py::args args) { + py::gil_scoped_release release; + *self = BooleanArraySubscriber(); + }) + nt::BooleanArrayPublisher: + methods: + BooleanArrayPublisher: + overloads: + '': + ignore: true + NT_Publisher: + ignore: true + Set: + SetDefault: + GetTopic: + inline_code: | + .def("close", [](BooleanArrayPublisher *self) { + py::gil_scoped_release release; + *self = BooleanArrayPublisher(); + }, py::doc("Destroys the publisher")) + .def("__enter__", [](BooleanArrayPublisher *self) { + return self; + }) + .def("__exit__", [](BooleanArrayPublisher *self, py::args args) { + py::gil_scoped_release release; + *self = BooleanArrayPublisher(); + }) + nt::BooleanArrayEntry: + methods: + BooleanArrayEntry: + overloads: + '': + ignore: true + NT_Entry, ParamType: + ignore: true + GetHandle: + ignore: true + GetTopic: + Unpublish: + inline_code: | + .def("close", [](BooleanArrayEntry *self) { + py::gil_scoped_release release; + *self = BooleanArrayEntry(); + }, py::doc("Destroys the entry")) + .def("__enter__", [](BooleanArrayEntry *self) { + return self; + }) + .def("__exit__", [](BooleanArrayEntry *self, py::args args) { + py::gil_scoped_release release; + *self = BooleanArrayEntry(); + }) + nt::BooleanArrayTopic: + attributes: + kTypeString: + methods: + BooleanArrayTopic: + overloads: + '': + ignore: true + NT_Topic: + ignore: true + Topic: + Subscribe: + SubscribeEx: + Publish: + PublishEx: + GetEntry: + GetEntryEx: + inline_code: | + .def("close", [](BooleanArrayTopic *self) { + py::gil_scoped_release release; + *self = BooleanArrayTopic(); + }, py::doc("Destroys the topic")) + .def("__enter__", [](BooleanArrayTopic *self) { + return self; + }) + .def("__exit__", [](BooleanArrayTopic *self, py::args args) { + py::gil_scoped_release release; + *self = BooleanArrayTopic(); + }) diff --git a/ntcore/src/main/python/semiwrap/BooleanTopic.yml b/ntcore/src/main/python/semiwrap/BooleanTopic.yml new file mode 100644 index 0000000000..8a4d3ddef5 --- /dev/null +++ b/ntcore/src/main/python/semiwrap/BooleanTopic.yml @@ -0,0 +1,107 @@ +classes: + nt::BooleanSubscriber: + methods: + BooleanSubscriber: + overloads: + '': + ignore: true + NT_Subscriber, ParamType: + ignore: true + Get: + overloads: + '[const]': + ParamType [const]: + GetAtomic: + overloads: + '[const]': + ParamType [const]: + ReadQueue: + GetTopic: + inline_code: | + .def("close", [](BooleanSubscriber *self) { + py::gil_scoped_release release; + *self = BooleanSubscriber(); + }, py::doc("Destroys the subscriber")) + .def("__enter__", [](BooleanSubscriber *self) { + return self; + }) + .def("__exit__", [](BooleanSubscriber *self, py::args args) { + py::gil_scoped_release release; + *self = BooleanSubscriber(); + }) + nt::BooleanPublisher: + methods: + BooleanPublisher: + overloads: + '': + ignore: true + NT_Publisher: + ignore: true + Set: + SetDefault: + GetTopic: + inline_code: | + .def("close", [](BooleanPublisher *self) { + py::gil_scoped_release release; + *self = BooleanPublisher(); + }, py::doc("Destroys the publisher")) + .def("__enter__", [](BooleanPublisher *self) { + return self; + }) + .def("__exit__", [](BooleanPublisher *self, py::args args) { + py::gil_scoped_release release; + *self = BooleanPublisher(); + }) + nt::BooleanEntry: + methods: + BooleanEntry: + overloads: + '': + ignore: true + NT_Entry, ParamType: + ignore: true + GetHandle: + ignore: true + GetTopic: + Unpublish: + inline_code: | + .def("close", [](BooleanEntry *self) { + py::gil_scoped_release release; + *self = BooleanEntry(); + }, py::doc("Destroys the entry")) + .def("__enter__", [](BooleanEntry *self) { + return self; + }) + .def("__exit__", [](BooleanEntry *self, py::args args) { + py::gil_scoped_release release; + *self = BooleanEntry(); + }) + nt::BooleanTopic: + attributes: + kTypeString: + methods: + BooleanTopic: + overloads: + '': + ignore: true + NT_Topic: + ignore: true + Topic: + Subscribe: + SubscribeEx: + Publish: + PublishEx: + GetEntry: + GetEntryEx: + inline_code: |- + .def("close", [](BooleanTopic *self) { + py::gil_scoped_release release; + *self = BooleanTopic(); + }, py::doc("Destroys the topic")) + .def("__enter__", [](BooleanTopic *self) { + return self; + }) + .def("__exit__", [](BooleanTopic *self, py::args args) { + py::gil_scoped_release release; + *self = BooleanTopic(); + }) diff --git a/ntcore/src/main/python/semiwrap/DoubleArrayTopic.yml b/ntcore/src/main/python/semiwrap/DoubleArrayTopic.yml new file mode 100644 index 0000000000..52892083ff --- /dev/null +++ b/ntcore/src/main/python/semiwrap/DoubleArrayTopic.yml @@ -0,0 +1,115 @@ +classes: + nt::DoubleArraySubscriber: + methods: + DoubleArraySubscriber: + overloads: + '': + ignore: true + NT_Subscriber, ParamType: + ignore: true + Get: + overloads: + '[const]': + ParamType [const]: + wpi::SmallVectorImpl& [const]: + ignore: true + wpi::SmallVectorImpl&, ParamType [const]: + ignore: true + GetAtomic: + overloads: + '[const]': + ParamType [const]: + wpi::SmallVectorImpl& [const]: + ignore: true + wpi::SmallVectorImpl&, ParamType [const]: + ignore: true + ReadQueue: + GetTopic: + inline_code: | + .def("close", [](DoubleArraySubscriber *self) { + py::gil_scoped_release release; + *self = DoubleArraySubscriber(); + }, py::doc("Destroys the subscriber")) + .def("__enter__", [](DoubleArraySubscriber *self) { + return self; + }) + .def("__exit__", [](DoubleArraySubscriber *self, py::args args) { + py::gil_scoped_release release; + *self = DoubleArraySubscriber(); + }) + nt::DoubleArrayPublisher: + methods: + DoubleArrayPublisher: + overloads: + '': + ignore: true + NT_Publisher: + ignore: true + Set: + SetDefault: + GetTopic: + inline_code: | + .def("close", [](DoubleArrayPublisher *self) { + py::gil_scoped_release release; + *self = DoubleArrayPublisher(); + }, py::doc("Destroys the publisher")) + .def("__enter__", [](DoubleArrayPublisher *self) { + return self; + }) + .def("__exit__", [](DoubleArrayPublisher *self, py::args args) { + py::gil_scoped_release release; + *self = DoubleArrayPublisher(); + }) + nt::DoubleArrayEntry: + methods: + DoubleArrayEntry: + overloads: + '': + ignore: true + NT_Entry, ParamType: + ignore: true + GetHandle: + ignore: true + GetTopic: + Unpublish: + inline_code: | + .def("close", [](DoubleArrayEntry *self) { + py::gil_scoped_release release; + *self = DoubleArrayEntry(); + }, py::doc("Destroys the entry")) + .def("__enter__", [](DoubleArrayEntry *self) { + return self; + }) + .def("__exit__", [](DoubleArrayEntry *self, py::args args) { + py::gil_scoped_release release; + *self = DoubleArrayEntry(); + }) + nt::DoubleArrayTopic: + attributes: + kTypeString: + methods: + DoubleArrayTopic: + overloads: + '': + ignore: true + NT_Topic: + ignore: true + Topic: + Subscribe: + SubscribeEx: + Publish: + PublishEx: + GetEntry: + GetEntryEx: + inline_code: |- + .def("close", [](DoubleArrayTopic *self) { + py::gil_scoped_release release; + *self = DoubleArrayTopic(); + }, py::doc("Destroys the topic")) + .def("__enter__", [](DoubleArrayTopic *self) { + return self; + }) + .def("__exit__", [](DoubleArrayTopic *self, py::args args) { + py::gil_scoped_release release; + *self = DoubleArrayTopic(); + }) diff --git a/ntcore/src/main/python/semiwrap/DoubleTopic.yml b/ntcore/src/main/python/semiwrap/DoubleTopic.yml new file mode 100644 index 0000000000..de572d690e --- /dev/null +++ b/ntcore/src/main/python/semiwrap/DoubleTopic.yml @@ -0,0 +1,107 @@ +classes: + nt::DoubleSubscriber: + methods: + DoubleSubscriber: + overloads: + '': + ignore: true + NT_Subscriber, ParamType: + ignore: true + Get: + overloads: + '[const]': + ParamType [const]: + GetAtomic: + overloads: + '[const]': + ParamType [const]: + ReadQueue: + GetTopic: + inline_code: | + .def("close", [](DoubleSubscriber *self) { + py::gil_scoped_release release; + *self = DoubleSubscriber(); + }, py::doc("Destroys the subscriber")) + .def("__enter__", [](DoubleSubscriber *self) { + return self; + }) + .def("__exit__", [](DoubleSubscriber *self, py::args args) { + py::gil_scoped_release release; + *self = DoubleSubscriber(); + }) + nt::DoublePublisher: + methods: + DoublePublisher: + overloads: + '': + ignore: true + NT_Publisher: + ignore: true + Set: + SetDefault: + GetTopic: + inline_code: | + .def("close", [](DoublePublisher *self) { + py::gil_scoped_release release; + *self = DoublePublisher(); + }, py::doc("Destroys the publisher")) + .def("__enter__", [](DoublePublisher *self) { + return self; + }) + .def("__exit__", [](DoublePublisher *self, py::args args) { + py::gil_scoped_release release; + *self = DoublePublisher(); + }) + nt::DoubleEntry: + methods: + DoubleEntry: + overloads: + '': + ignore: true + NT_Entry, ParamType: + ignore: true + GetHandle: + ignore: true + GetTopic: + Unpublish: + inline_code: | + .def("close", [](DoubleEntry *self) { + py::gil_scoped_release release; + *self = DoubleEntry(); + }, py::doc("Destroys the entry")) + .def("__enter__", [](DoubleEntry *self) { + return self; + }) + .def("__exit__", [](DoubleEntry *self, py::args args) { + py::gil_scoped_release release; + *self = DoubleEntry(); + }) + nt::DoubleTopic: + attributes: + kTypeString: + methods: + DoubleTopic: + overloads: + '': + ignore: true + NT_Topic: + ignore: true + Topic: + Subscribe: + SubscribeEx: + Publish: + PublishEx: + GetEntry: + GetEntryEx: + inline_code: | + .def("close", [](DoubleTopic *self) { + py::gil_scoped_release release; + *self = DoubleTopic(); + }, py::doc("Destroys the topic")) + .def("__enter__", [](DoubleTopic *self) { + return self; + }) + .def("__exit__", [](DoubleTopic *self, py::args args) { + py::gil_scoped_release release; + *self = DoubleTopic(); + }) diff --git a/ntcore/src/main/python/semiwrap/FloatArrayTopic.yml b/ntcore/src/main/python/semiwrap/FloatArrayTopic.yml new file mode 100644 index 0000000000..a00b48bb94 --- /dev/null +++ b/ntcore/src/main/python/semiwrap/FloatArrayTopic.yml @@ -0,0 +1,115 @@ +classes: + nt::FloatArraySubscriber: + methods: + FloatArraySubscriber: + overloads: + '': + ignore: true + NT_Subscriber, ParamType: + ignore: true + Get: + overloads: + '[const]': + ParamType [const]: + wpi::SmallVectorImpl& [const]: + ignore: true + wpi::SmallVectorImpl&, ParamType [const]: + ignore: true + GetAtomic: + overloads: + '[const]': + ParamType [const]: + wpi::SmallVectorImpl& [const]: + ignore: true + wpi::SmallVectorImpl&, ParamType [const]: + ignore: true + ReadQueue: + GetTopic: + inline_code: | + .def("close", [](FloatArraySubscriber *self) { + py::gil_scoped_release release; + *self = FloatArraySubscriber(); + }, py::doc("Destroys the subscriber")) + .def("__enter__", [](FloatArraySubscriber *self) { + return self; + }) + .def("__exit__", [](FloatArraySubscriber *self, py::args args) { + py::gil_scoped_release release; + *self = FloatArraySubscriber(); + }) + nt::FloatArrayPublisher: + methods: + FloatArrayPublisher: + overloads: + '': + ignore: true + NT_Publisher: + ignore: true + Set: + SetDefault: + GetTopic: + inline_code: | + .def("close", [](FloatArrayPublisher *self) { + py::gil_scoped_release release; + *self = FloatArrayPublisher(); + }, py::doc("Destroys the publisher")) + .def("__enter__", [](FloatArrayPublisher *self) { + return self; + }) + .def("__exit__", [](FloatArrayPublisher *self, py::args args) { + py::gil_scoped_release release; + *self = FloatArrayPublisher(); + }) + nt::FloatArrayEntry: + methods: + FloatArrayEntry: + overloads: + '': + ignore: true + NT_Entry, ParamType: + ignore: true + GetHandle: + ignore: true + GetTopic: + Unpublish: + inline_code: | + .def("close", [](FloatArrayEntry *self) { + py::gil_scoped_release release; + *self = FloatArrayEntry(); + }, py::doc("Destroys the entry")) + .def("__enter__", [](FloatArrayEntry *self) { + return self; + }) + .def("__exit__", [](FloatArrayEntry *self, py::args args) { + py::gil_scoped_release release; + *self = FloatArrayEntry(); + }) + nt::FloatArrayTopic: + attributes: + kTypeString: + methods: + FloatArrayTopic: + overloads: + '': + ignore: true + NT_Topic: + ignore: true + Topic: + Subscribe: + SubscribeEx: + Publish: + PublishEx: + GetEntry: + GetEntryEx: + inline_code: | + .def("close", [](FloatArrayTopic *self) { + py::gil_scoped_release release; + *self = FloatArrayTopic(); + }, py::doc("Destroys the topic")) + .def("__enter__", [](FloatArrayTopic *self) { + return self; + }) + .def("__exit__", [](FloatArrayTopic *self, py::args args) { + py::gil_scoped_release release; + *self = FloatArrayTopic(); + }) diff --git a/ntcore/src/main/python/semiwrap/FloatTopic.yml b/ntcore/src/main/python/semiwrap/FloatTopic.yml new file mode 100644 index 0000000000..c0f3e192b3 --- /dev/null +++ b/ntcore/src/main/python/semiwrap/FloatTopic.yml @@ -0,0 +1,107 @@ +classes: + nt::FloatSubscriber: + methods: + FloatSubscriber: + overloads: + '': + ignore: true + NT_Subscriber, ParamType: + ignore: true + Get: + overloads: + '[const]': + ParamType [const]: + GetAtomic: + overloads: + '[const]': + ParamType [const]: + ReadQueue: + GetTopic: + inline_code: | + .def("close", [](FloatSubscriber *self) { + py::gil_scoped_release release; + *self = FloatSubscriber(); + }, py::doc("Destroys the subscriber")) + .def("__enter__", [](FloatSubscriber *self) { + return self; + }) + .def("__exit__", [](FloatSubscriber *self, py::args args) { + py::gil_scoped_release release; + *self = FloatSubscriber(); + }) + nt::FloatPublisher: + methods: + FloatPublisher: + overloads: + '': + ignore: true + NT_Publisher: + ignore: true + Set: + SetDefault: + GetTopic: + inline_code: | + .def("close", [](FloatPublisher *self) { + py::gil_scoped_release release; + *self = FloatPublisher(); + }, py::doc("Destroys the publisher")) + .def("__enter__", [](FloatPublisher *self) { + return self; + }) + .def("__exit__", [](FloatPublisher *self, py::args args) { + py::gil_scoped_release release; + *self = FloatPublisher(); + }) + nt::FloatEntry: + methods: + FloatEntry: + overloads: + '': + ignore: true + NT_Entry, ParamType: + ignore: true + GetHandle: + ignore: true + GetTopic: + Unpublish: + inline_code: | + .def("close", [](FloatEntry *self) { + py::gil_scoped_release release; + *self = FloatEntry(); + }, py::doc("Destroys the entry")) + .def("__enter__", [](FloatEntry *self) { + return self; + }) + .def("__exit__", [](FloatEntry *self, py::args args) { + py::gil_scoped_release release; + *self = FloatEntry(); + }) + nt::FloatTopic: + attributes: + kTypeString: + methods: + FloatTopic: + overloads: + '': + ignore: true + NT_Topic: + ignore: true + Topic: + Subscribe: + SubscribeEx: + Publish: + PublishEx: + GetEntry: + GetEntryEx: + inline_code: | + .def("close", [](FloatTopic *self) { + py::gil_scoped_release release; + *self = FloatTopic(); + }, py::doc("Destroys the topic")) + .def("__enter__", [](FloatTopic *self) { + return self; + }) + .def("__exit__", [](FloatTopic *self, py::args args) { + py::gil_scoped_release release; + *self = FloatTopic(); + }) diff --git a/ntcore/src/main/python/semiwrap/GenericEntry.yml b/ntcore/src/main/python/semiwrap/GenericEntry.yml new file mode 100644 index 0000000000..3b7335d5c6 --- /dev/null +++ b/ntcore/src/main/python/semiwrap/GenericEntry.yml @@ -0,0 +1,86 @@ +extra_includes: +- src/nt_type_caster.h + +classes: + nt::GenericSubscriber: + methods: + GenericSubscriber: + overloads: + '': + ignore: true + NT_Subscriber: + ignore: true + Get: + GetBoolean: + GetInteger: + GetFloat: + GetDouble: + GetString: + GetRaw: + GetBooleanArray: + GetIntegerArray: + GetFloatArray: + GetDoubleArray: + GetStringArray: + ReadQueue: + GetTopic: + nt::GenericPublisher: + methods: + GenericPublisher: + overloads: + '': + ignore: true + NT_Publisher: + ignore: true + Set: + SetBoolean: + SetInteger: + SetFloat: + SetDouble: + SetString: + SetRaw: + SetBooleanArray: + overloads: + std::span, int64_t: + std::span, int64_t: + SetIntegerArray: + SetFloatArray: + SetDoubleArray: + SetStringArray: + SetDefault: + SetDefaultBoolean: + SetDefaultInteger: + SetDefaultFloat: + SetDefaultDouble: + SetDefaultString: + SetDefaultRaw: + SetDefaultBooleanArray: + SetDefaultIntegerArray: + SetDefaultFloatArray: + SetDefaultDoubleArray: + SetDefaultStringArray: + GetTopic: + nt::GenericEntry: + methods: + GenericEntry: + overloads: + '': + ignore: true + NT_Entry: + ignore: true + GetHandle: + ignore: true + GetTopic: + Unpublish: + inline_code: | + .def("close", [](GenericEntry *self) { + py::gil_scoped_release release; + *self = GenericEntry(); + }, py::doc("Destroys the entry")) + .def("__enter__", [](GenericEntry *self) { + return self; + }) + .def("__exit__", [](GenericEntry *self, py::args args) { + py::gil_scoped_release release; + *self = GenericEntry(); + }) diff --git a/ntcore/src/main/python/semiwrap/IntegerArrayTopic.yml b/ntcore/src/main/python/semiwrap/IntegerArrayTopic.yml new file mode 100644 index 0000000000..2582c32de9 --- /dev/null +++ b/ntcore/src/main/python/semiwrap/IntegerArrayTopic.yml @@ -0,0 +1,115 @@ +classes: + nt::IntegerArraySubscriber: + methods: + IntegerArraySubscriber: + overloads: + '': + ignore: true + NT_Subscriber, ParamType: + ignore: true + Get: + overloads: + '[const]': + ParamType [const]: + wpi::SmallVectorImpl& [const]: + ignore: true + wpi::SmallVectorImpl&, ParamType [const]: + ignore: true + GetAtomic: + overloads: + '[const]': + ParamType [const]: + wpi::SmallVectorImpl& [const]: + ignore: true + wpi::SmallVectorImpl&, ParamType [const]: + ignore: true + ReadQueue: + GetTopic: + inline_code: | + .def("close", [](IntegerArraySubscriber *self) { + py::gil_scoped_release release; + *self = IntegerArraySubscriber(); + }, py::doc("Destroys the subscriber")) + .def("__enter__", [](IntegerArraySubscriber *self) { + return self; + }) + .def("__exit__", [](IntegerArraySubscriber *self, py::args args) { + py::gil_scoped_release release; + *self = IntegerArraySubscriber(); + }) + nt::IntegerArrayPublisher: + methods: + IntegerArrayPublisher: + overloads: + '': + ignore: true + NT_Publisher: + ignore: true + Set: + SetDefault: + GetTopic: + inline_code: | + .def("close", [](IntegerArrayPublisher *self) { + py::gil_scoped_release release; + *self = IntegerArrayPublisher(); + }, py::doc("Destroys the publisher")) + .def("__enter__", [](IntegerArrayPublisher *self) { + return self; + }) + .def("__exit__", [](IntegerArrayPublisher *self, py::args args) { + py::gil_scoped_release release; + *self = IntegerArrayPublisher(); + }) + nt::IntegerArrayEntry: + methods: + IntegerArrayEntry: + overloads: + '': + ignore: true + NT_Entry, ParamType: + ignore: true + GetHandle: + ignore: true + GetTopic: + Unpublish: + inline_code: | + .def("close", [](IntegerArrayEntry *self) { + py::gil_scoped_release release; + *self = IntegerArrayEntry(); + }, py::doc("Destroys the entry")) + .def("__enter__", [](IntegerArrayEntry *self) { + return self; + }) + .def("__exit__", [](IntegerArrayEntry *self, py::args args) { + py::gil_scoped_release release; + *self = IntegerArrayEntry(); + }) + nt::IntegerArrayTopic: + attributes: + kTypeString: + methods: + IntegerArrayTopic: + overloads: + '': + ignore: true + NT_Topic: + ignore: true + Topic: + Subscribe: + SubscribeEx: + Publish: + PublishEx: + GetEntry: + GetEntryEx: + inline_code: | + .def("close", [](IntegerArrayTopic *self) { + py::gil_scoped_release release; + *self = IntegerArrayTopic(); + }, py::doc("Destroys the topic")) + .def("__enter__", [](IntegerArrayTopic *self) { + return self; + }) + .def("__exit__", [](IntegerArrayTopic *self, py::args args) { + py::gil_scoped_release release; + *self = IntegerArrayTopic(); + }) diff --git a/ntcore/src/main/python/semiwrap/IntegerTopic.yml b/ntcore/src/main/python/semiwrap/IntegerTopic.yml new file mode 100644 index 0000000000..07b6859cd0 --- /dev/null +++ b/ntcore/src/main/python/semiwrap/IntegerTopic.yml @@ -0,0 +1,107 @@ +classes: + nt::IntegerSubscriber: + methods: + IntegerSubscriber: + overloads: + '': + ignore: true + NT_Subscriber, ParamType: + ignore: true + Get: + overloads: + '[const]': + ParamType [const]: + GetAtomic: + overloads: + '[const]': + ParamType [const]: + ReadQueue: + GetTopic: + inline_code: | + .def("close", [](IntegerSubscriber *self) { + py::gil_scoped_release release; + *self = IntegerSubscriber(); + }, py::doc("Destroys the subscriber")) + .def("__enter__", [](IntegerSubscriber *self) { + return self; + }) + .def("__exit__", [](IntegerSubscriber *self, py::args args) { + py::gil_scoped_release release; + *self = IntegerSubscriber(); + }) + nt::IntegerPublisher: + methods: + IntegerPublisher: + overloads: + '': + ignore: true + NT_Publisher: + ignore: true + Set: + SetDefault: + GetTopic: + inline_code: | + .def("close", [](IntegerPublisher *self) { + py::gil_scoped_release release; + *self = IntegerPublisher(); + }, py::doc("Destroys the publisher")) + .def("__enter__", [](IntegerPublisher *self) { + return self; + }) + .def("__exit__", [](IntegerPublisher *self, py::args args) { + py::gil_scoped_release release; + *self = IntegerPublisher(); + }) + nt::IntegerEntry: + methods: + IntegerEntry: + overloads: + '': + ignore: true + NT_Entry, ParamType: + ignore: true + GetHandle: + ignore: true + GetTopic: + Unpublish: + inline_code: | + .def("close", [](IntegerEntry *self) { + py::gil_scoped_release release; + *self = IntegerEntry(); + }, py::doc("Destroys the entry")) + .def("__enter__", [](IntegerEntry *self) { + return self; + }) + .def("__exit__", [](IntegerEntry *self, py::args args) { + py::gil_scoped_release release; + *self = IntegerEntry(); + }) + nt::IntegerTopic: + attributes: + kTypeString: + methods: + IntegerTopic: + overloads: + '': + ignore: true + NT_Topic: + ignore: true + Topic: + Subscribe: + SubscribeEx: + Publish: + PublishEx: + GetEntry: + GetEntryEx: + inline_code: | + .def("close", [](IntegerTopic *self) { + py::gil_scoped_release release; + *self = IntegerTopic(); + }, py::doc("Destroys the topic")) + .def("__enter__", [](IntegerTopic *self) { + return self; + }) + .def("__exit__", [](IntegerTopic *self, py::args args) { + py::gil_scoped_release release; + *self = IntegerTopic(); + }) diff --git a/ntcore/src/main/python/semiwrap/MultiSubscriber.yml b/ntcore/src/main/python/semiwrap/MultiSubscriber.yml new file mode 100644 index 0000000000..a980e9b5a5 --- /dev/null +++ b/ntcore/src/main/python/semiwrap/MultiSubscriber.yml @@ -0,0 +1,22 @@ +classes: + nt::MultiSubscriber: + methods: + MultiSubscriber: + overloads: + '': + ignore: true + NetworkTableInstance, std::span, const PubSubOptions&: + GetHandle: + ignore: true + inline_code: | + .def("close", [](MultiSubscriber *self) { + py::gil_scoped_release release; + *self = MultiSubscriber(); + }, py::doc("Destroys the subscriber")) + .def("__enter__", [](MultiSubscriber *self) { + return self; + }) + .def("__exit__", [](MultiSubscriber *self, py::args args) { + py::gil_scoped_release release; + *self = MultiSubscriber(); + }) diff --git a/ntcore/src/main/python/semiwrap/NTSendable.yml b/ntcore/src/main/python/semiwrap/NTSendable.yml new file mode 100644 index 0000000000..b105e6fa11 --- /dev/null +++ b/ntcore/src/main/python/semiwrap/NTSendable.yml @@ -0,0 +1,20 @@ +extra_includes: +- networktables/NTSendableBuilder.h + +classes: + nt::NTSendable: + methods: + InitSendable: + overloads: + NTSendableBuilder&: + virtual_xform: | + [&](py::function fn) { + auto builderHandle = py::cast(builder, py::return_value_policy::reference); + fn(builderHandle); + } + wpi::SendableBuilder&: + virtual_xform: | + [&](py::function fn) { + auto builderHandle = py::cast(builder, py::return_value_policy::reference); + fn(builderHandle); + } diff --git a/ntcore/src/main/python/semiwrap/NTSendableBuilder.yml b/ntcore/src/main/python/semiwrap/NTSendableBuilder.yml new file mode 100644 index 0000000000..afa178a30b --- /dev/null +++ b/ntcore/src/main/python/semiwrap/NTSendableBuilder.yml @@ -0,0 +1,15 @@ +classes: + nt::NTSendableBuilder: + force_type_casters: + - std::function + typealias: + - BackendKind = wpi::SendableBuilder::BackendKind + methods: + SetUpdateTable: + cpp_code: | + [](NTSendableBuilder *self, std::function func) { + self->SetUpdateTable(std::move(func)); + } + GetTopic: + GetTable: + GetBackendKind: diff --git a/ntcore/src/main/python/semiwrap/NetworkTable.yml b/ntcore/src/main/python/semiwrap/NetworkTable.yml new file mode 100644 index 0000000000..c940e475b7 --- /dev/null +++ b/ntcore/src/main/python/semiwrap/NetworkTable.yml @@ -0,0 +1,178 @@ +extra_includes: +- networktables/BooleanArrayTopic.h +- networktables/BooleanTopic.h +- networktables/DoubleArrayTopic.h +- networktables/DoubleTopic.h +- networktables/FloatArrayTopic.h +- networktables/FloatTopic.h +- networktables/IntegerArrayTopic.h +- networktables/IntegerTopic.h +- networktables/NetworkTableInstance.h +- networktables/RawTopic.h +- networktables/StringArrayTopic.h +- networktables/StringTopic.h +- networktables/StructArrayTopic.h +- networktables/StructTopic.h +- networktables/Topic.h +- src/py2value.h +- src/pyentry.h +- wpystruct.h + +classes: + nt::NetworkTable: + attributes: + PATH_SEPARATOR_CHAR: + methods: + BasenameKey: + NormalizeKey: + overloads: + std::string_view, bool: + std::string_view, wpi::SmallVectorImpl&, bool: + ignore: true + GetHierarchy: + NetworkTable: + ignore: true + GetInstance: + GetEntry: + GetTopic: + GetBooleanTopic: + GetIntegerTopic: + GetFloatTopic: + GetDoubleTopic: + GetStringTopic: + GetRawTopic: + GetBooleanArrayTopic: + GetIntegerArrayTopic: + GetFloatArrayTopic: + GetDoubleArrayTopic: + GetStringArrayTopic: + GetProtobufTopic: + ignore: true + GetStructTopic: + param_override: + info: + name: type + cpp_code: | + [](const NetworkTable &self, std::string_view name, const py::type &t) { + WPyStructInfo info(t); + return self.GetStructTopic(name, info); + } + GetStructArrayTopic: + param_override: + info: + name: type + cpp_code: | + [](const NetworkTable &self, std::string_view name, const py::type &t) { + WPyStructInfo info(t); + return self.GetStructArrayTopic(name, info); + } + GetSubTable: + ContainsKey: + ContainsSubTable: + GetTopicInfo: + GetTopics: + GetKeys: + GetSubTables: + SetPersistent: + ClearPersistent: + IsPersistent: + PutNumber: + SetDefaultNumber: + GetNumber: + cpp_code: | + [](NetworkTable * table, std::string_view key, py::object defaultValue) -> py::object { + nt::NetworkTableEntry entry; + { + py::gil_scoped_release release; + entry = table->GetEntry(key); + } + return pyntcore::GetDoubleEntry(entry, defaultValue); + } + PutString: + SetDefaultString: + GetString: + cpp_code: | + [](NetworkTable * table, std::string_view key, py::object defaultValue) -> py::object { + nt::NetworkTableEntry entry; + { + py::gil_scoped_release release; + entry = table->GetEntry(key); + } + return pyntcore::GetStringEntry(entry, defaultValue); + } + PutBoolean: + SetDefaultBoolean: + GetBoolean: + cpp_code: | + [](NetworkTable * table, std::string_view key, py::object defaultValue) -> py::object { + nt::NetworkTableEntry entry; + { + py::gil_scoped_release release; + entry = table->GetEntry(key); + } + return pyntcore::GetBooleanEntry(entry, defaultValue); + } + PutBooleanArray: + SetDefaultBooleanArray: + GetBooleanArray: + cpp_code: | + [](NetworkTable * table, std::string_view key, py::object defaultValue) -> py::object { + nt::NetworkTableEntry entry; + { + py::gil_scoped_release release; + entry = table->GetEntry(key); + } + return pyntcore::GetBooleanArrayEntry(entry, defaultValue); + } + PutNumberArray: + SetDefaultNumberArray: + GetNumberArray: + cpp_code: | + [](NetworkTable * table, std::string_view key, py::object defaultValue) -> py::object { + nt::NetworkTableEntry entry; + { + py::gil_scoped_release release; + entry = table->GetEntry(key); + } + return pyntcore::GetDoubleArrayEntry(entry, defaultValue); + } + PutStringArray: + SetDefaultStringArray: + GetStringArray: + cpp_code: | + [](NetworkTable * table, std::string_view key, py::object defaultValue) -> py::object { + nt::NetworkTableEntry entry; + { + py::gil_scoped_release release; + entry = table->GetEntry(key); + } + return pyntcore::GetStringArrayEntry(entry, defaultValue); + } + PutRaw: + SetDefaultRaw: + GetRaw: + cpp_code: | + [](NetworkTable * table, std::string_view key, py::object defaultValue) -> py::object { + nt::NetworkTableEntry entry; + { + py::gil_scoped_release release; + entry = table->GetEntry(key); + } + return pyntcore::GetRawEntry(entry, defaultValue); + } + PutValue: + ignore: true + SetDefaultValue: + ignore: true + GetValue: + ignore: true + GetPath: + AddListener: + overloads: + int, TableEventListener: + std::string_view, int, TableEventListener: + AddSubTableListener: + RemoveListener: + +inline_code: | + #include diff --git a/ntcore/src/main/python/semiwrap/NetworkTableEntry.yml b/ntcore/src/main/python/semiwrap/NetworkTableEntry.yml new file mode 100644 index 0000000000..107fa6f650 --- /dev/null +++ b/ntcore/src/main/python/semiwrap/NetworkTableEntry.yml @@ -0,0 +1,97 @@ +extra_includes: +- networktables/NetworkTableInstance.h +- networktables/Topic.h +- src/py2value.h +- src/pyentry.h + +inline_code: | + #include +classes: + nt::NetworkTableEntry: + methods: + NetworkTableEntry: + overloads: + '': + ignore: true + NT_Entry: + ignore: true + GetHandle: + ignore: true + GetInstance: + Exists: + GetName: + GetType: + GetLastChange: + GetValue: + GetBoolean: + cpp_code: | + &pyntcore::GetBooleanEntry + GetInteger: + cpp_code: | + &pyntcore::GetIntegerEntry + GetFloat: + cpp_code: | + &pyntcore::GetFloatEntry + GetDouble: + cpp_code: | + &pyntcore::GetDoubleEntry + GetString: + cpp_code: | + &pyntcore::GetStringEntry + GetRaw: + cpp_code: | + &pyntcore::GetRawEntry + GetBooleanArray: + cpp_code: | + &pyntcore::GetBooleanArrayEntry + GetIntegerArray: + cpp_code: | + &pyntcore::GetIntegerArrayEntry + GetFloatArray: + cpp_code: | + &pyntcore::GetFloatArrayEntry + GetDoubleArray: + cpp_code: | + &pyntcore::GetDoubleArrayEntry + GetStringArray: + cpp_code: | + &pyntcore::GetStringArrayEntry + ReadQueue: + SetDefaultValue: + SetDefaultBoolean: + SetDefaultInteger: + SetDefaultFloat: + SetDefaultDouble: + SetDefaultString: + SetDefaultRaw: + SetDefaultBooleanArray: + SetDefaultIntegerArray: + SetDefaultFloatArray: + SetDefaultDoubleArray: + SetDefaultStringArray: + SetValue: + SetBoolean: + SetInteger: + SetFloat: + SetDouble: + SetString: + SetRaw: + SetBooleanArray: + overloads: + std::span, int64_t: + std::span, int64_t: + ignore: true + SetIntegerArray: + SetFloatArray: + SetDoubleArray: + SetStringArray: + SetPersistent: + ClearPersistent: + IsPersistent: + Unpublish: + GetTopic: + operator==: + inline_code: | + .def("__repr__", [](NetworkTableEntry self) { + return py::str("").format(self.GetName()); + }) diff --git a/ntcore/src/main/python/semiwrap/NetworkTableInstance.yml b/ntcore/src/main/python/semiwrap/NetworkTableInstance.yml new file mode 100644 index 0000000000..82f2a9bb18 --- /dev/null +++ b/ntcore/src/main/python/semiwrap/NetworkTableInstance.yml @@ -0,0 +1,181 @@ +extra_includes: +- networktables/BooleanArrayTopic.h +- networktables/BooleanTopic.h +- networktables/DoubleArrayTopic.h +- networktables/DoubleTopic.h +- networktables/FloatArrayTopic.h +- networktables/FloatTopic.h +- networktables/IntegerArrayTopic.h +- networktables/IntegerTopic.h +- networktables/MultiSubscriber.h +- networktables/RawTopic.h +- networktables/StringArrayTopic.h +- networktables/StringTopic.h +- networktables/StructArrayTopic.h +- networktables/StructTopic.h +- networktables/Topic.h +- src/py2value.h +- src/nt_instance.h +- wpi/datalog/DataLog.h +- wpystruct.h + +classes: + nt::NetworkTableInstance: + force_type_casters: + - std::function + attributes: + kDefaultPort: + enums: + NetworkMode: + arithmetic: true + inline_code: | + .value("kNetModeStarting", (nt::NetworkTableInstance::NetworkMode)NT_NET_MODE_STARTING) + LogLevel: + methods: + NetworkTableInstance: + overloads: + '': + ignore: true + NT_Inst: + ignore: true + GetDefault: + Create: + Destroy: + cpp_code: | + [](NetworkTableInstance * self) { + pyntcore::onInstanceDestroy(self); + py::gil_scoped_release release; + NetworkTableInstance::Destroy(*self); + } + GetHandle: + internal: true + GetTopic: + GetBooleanTopic: + GetIntegerTopic: + GetFloatTopic: + GetDoubleTopic: + GetStringTopic: + GetRawTopic: + GetBooleanArrayTopic: + GetIntegerArrayTopic: + GetFloatArrayTopic: + GetDoubleArrayTopic: + GetStringArrayTopic: + GetProtobufTopic: + ignore: true + GetStructTopic: + param_override: + info: + name: type + cpp_code: | + [](const NetworkTableInstance &self, std::string_view name, const py::type &t) { + WPyStructInfo info(t); + return self.GetStructTopic(name, info); + } + GetStructArrayTopic: + param_override: + info: + name: type + cpp_code: | + [](const NetworkTableInstance &self, std::string_view name, const py::type &t) { + WPyStructInfo info(t); + return self.GetStructArrayTopic(name, info); + } + GetTopics: + overloads: + '': + std::string_view: + std::string_view, unsigned int: + std::string_view, std::span: + GetTopicInfo: + overloads: + '': + std::string_view: + std::string_view, unsigned int: + std::string_view, std::span: + GetEntry: + GetTable: + RemoveListener: + WaitForListenerQueue: + AddConnectionListener: + AddTimeSyncListener: + AddListener: + overloads: + Topic, unsigned int, ListenerCallback: + Subscriber&, unsigned int, ListenerCallback: + MultiSubscriber&, int, ListenerCallback: + const NetworkTableEntry&, int, ListenerCallback: + std::span, int, ListenerCallback: + GetNetworkMode: + StartLocal: + cpp_code: | + [](NetworkTableInstance * self) { + pyntcore::onInstanceStart(self); + py::gil_scoped_release release; + self->StartLocal(); + } + StopLocal: + StartServer: + cpp_code: | + [](NetworkTableInstance * self, std::string_view persist_filename, const char* listen_address, + unsigned int port) { + pyntcore::onInstanceStart(self); + py::gil_scoped_release release; + self->StartServer(persist_filename, listen_address, port); + } + StopServer: + StopClient: + SetServer: + overloads: + std::string_view, unsigned int: + std::span>: + std::span, unsigned int: + SetServerTeam: + Disconnect: + StartDSClient: + StopDSClient: + FlushLocal: + Flush: + GetConnections: + IsConnected: + GetServerTimeOffset: + StartEntryDataLog: + StopEntryDataLog: + StartConnectionDataLog: + StopConnectionDataLog: + AddLogger: + HasSchema: + AddSchema: + overloads: + std::string_view, std::string_view, std::span: + std::string_view, std::string_view, std::string_view: + AddProtobufSchema: + ignore: true + AddStructSchema: + ignore: true + operator==: + StartClient: + inline_code: | + .def("configPythonLogging", [](NetworkTableInstance * self, + NetworkTableInstance::LogLevel minLevel, NetworkTableInstance::LogLevel maxLevel, py::str logName) { + py::module::import("ntcore._logutil").attr("_config_logging")(self, minLevel, maxLevel, logName); + }, py::kw_only(), + py::arg("min") = NetworkTableInstance::LogLevel::kLogInfo, + py::arg("max") = NetworkTableInstance::LogLevel::kLogCritical, + py::arg("name") = "nt", + py::doc("Configure python logging for this instance.\n" + "\n" + ":param min: Minimum NT level to log\n" + ":param max: Maximum NT level to log\n" + ":param name: Name of python logger\n" + "\n" + ".. note:: This must be called before the instance is started") + ) + .def("_reset", [](NetworkTableInstance *self) { + pyntcore::onInstancePreReset(self); + { + py::gil_scoped_release release; + nt::ResetInstance(self->GetHandle()); + } + pyntcore::onInstancePostReset(self); + }) diff --git a/ntcore/src/main/python/semiwrap/NetworkTableListener.yml b/ntcore/src/main/python/semiwrap/NetworkTableListener.yml new file mode 100644 index 0000000000..e96cfabe8f --- /dev/null +++ b/ntcore/src/main/python/semiwrap/NetworkTableListener.yml @@ -0,0 +1,63 @@ +classes: + nt::NetworkTableListener: + force_type_casters: + - std::function + methods: + NetworkTableListener: + overloads: + '': + CreateListener: + overloads: + NetworkTableInstance, std::span, unsigned int, ListenerCallback: + Topic, unsigned int, ListenerCallback: + Subscriber&, unsigned int, ListenerCallback: + MultiSubscriber&, unsigned int, ListenerCallback: + NetworkTableEntry&, unsigned int, ListenerCallback: + CreateConnectionListener: + CreateTimeSyncListener: + CreateLogger: + GetHandle: + WaitForQueue: + inline_code: | + .def("close", [](NetworkTableListener *self) { + py::gil_scoped_release release; + *self = NetworkTableListener(); + }, py::doc("Destroys the listener")) + .def("__enter__", [](NetworkTableListener *self) { + return self; + }) + .def("__exit__", [](NetworkTableListener *self, py::args args) { + py::gil_scoped_release release; + *self = NetworkTableListener(); + }) + nt::NetworkTableListenerPoller: + methods: + NetworkTableListenerPoller: + overloads: + '': + NetworkTableInstance: + GetHandle: + AddListener: + overloads: + std::span, unsigned int: + Topic, unsigned int: + Subscriber&, unsigned int: + MultiSubscriber&, unsigned int: + NetworkTableEntry&, unsigned int: + AddConnectionListener: + AddTimeSyncListener: + AddLogger: + RemoveListener: + ReadQueue: + inline_code: | + .def("close", [](NetworkTableListenerPoller *self) { + py::gil_scoped_release release; + *self = NetworkTableListenerPoller(); + }, py::doc("Destroys the poller")) + .def("__enter__", [](NetworkTableListenerPoller *self) { + return self; + }) + .def("__exit__", [](NetworkTableListenerPoller *self, py::args args) { + py::gil_scoped_release release; + *self = NetworkTableListenerPoller(); + }) diff --git a/ntcore/src/main/python/semiwrap/NetworkTableType.yml b/ntcore/src/main/python/semiwrap/NetworkTableType.yml new file mode 100644 index 0000000000..d7a65e056b --- /dev/null +++ b/ntcore/src/main/python/semiwrap/NetworkTableType.yml @@ -0,0 +1,2 @@ +enums: + NetworkTableType: diff --git a/ntcore/src/main/python/semiwrap/NetworkTableValue.yml b/ntcore/src/main/python/semiwrap/NetworkTableValue.yml new file mode 100644 index 0000000000..3d5af62478 --- /dev/null +++ b/ntcore/src/main/python/semiwrap/NetworkTableValue.yml @@ -0,0 +1,184 @@ +extra_includes: +- networktables/NetworkTableType.h +- src/py2value.h + +functions: + Now: + ignore: true + +classes: + nt::Value: + methods: + Value: + overloads: + '': + ignore: true + NT_Type, size_t, int64_t, const private_init&: + ignore: true + NT_Type, size_t, int64_t, int64_t, const private_init&: + ignore: true + type: + cpp_code: | + [](Value *t) { + return (NetworkTableType)t->type(); + } + value: + cpp_code: | + [](const Value &self) -> py::object { + return pyntcore::ntvalue2py(self); + } + last_change: + time: + size: + SetTime: + server_time: + SetServerTime: + IsValid: + IsBoolean: + IsInteger: + IsFloat: + IsDouble: + IsString: + IsRaw: + IsBooleanArray: + IsIntegerArray: + IsFloatArray: + IsDoubleArray: + IsStringArray: + GetBoolean: + cpp_code: | + [](Value *self) { + pyntcore::ensure_value_is(NT_BOOLEAN, self); + return self->GetBoolean(); + } + GetInteger: + cpp_code: | + [](Value *self) { + pyntcore::ensure_value_is(NT_INTEGER, self); + return self->GetInteger(); + } + GetFloat: + cpp_code: | + [](Value *self) { + pyntcore::ensure_value_is(NT_FLOAT, self); + return self->GetFloat(); + } + GetDouble: + cpp_code: | + [](Value *self) { + pyntcore::ensure_value_is(NT_DOUBLE, self); + return self->GetDouble(); + } + GetString: + cpp_code: | + [](Value *self) { + pyntcore::ensure_value_is(NT_STRING, self); + return self->GetString(); + } + GetRaw: + cpp_code: | + [](Value *self) { + pyntcore::ensure_value_is(NT_RAW, self); + return self->GetRaw(); + } + GetBooleanArray: + cpp_code: | + [](Value *self) -> py::object { + pyntcore::ensure_value_is(NT_BOOLEAN_ARRAY, self); + auto v = self->value(); + py::list l(v.data.arr_boolean.size); + for (size_t i = 0; i < v.data.arr_boolean.size; i++) { + auto b = py::bool_(v.data.arr_boolean.arr[i]); + PyList_SET_ITEM(l.ptr(), i, b.release().ptr()); + } + return std::move(l); + } + GetIntegerArray: + cpp_code: | + [](Value *self) { + pyntcore::ensure_value_is(NT_INTEGER_ARRAY, self); + return self->GetIntegerArray(); + } + GetFloatArray: + cpp_code: | + [](Value *self) { + pyntcore::ensure_value_is(NT_FLOAT_ARRAY, self); + return self->GetFloatArray(); + } + GetDoubleArray: + cpp_code: | + [](Value *self) { + pyntcore::ensure_value_is(NT_DOUBLE_ARRAY, self); + return self->GetDoubleArray(); + } + GetStringArray: + cpp_code: | + [](Value *self) { + pyntcore::ensure_value_is(NT_STRING_ARRAY, self); + return self->GetStringArray(); + } + MakeBoolean: + MakeInteger: + MakeFloat: + MakeDouble: + MakeString: + overloads: + std::string_view, int64_t: + T&&, int64_t: + ignore: true + MakeRaw: + overloads: + std::span, int64_t: + T&&, int64_t: + ignore: true + MakeBooleanArray: + overloads: + std::span, int64_t: + std::initializer_list, int64_t: + ignore: true + std::span, int64_t: + ignore: true + std::initializer_list, int64_t: + ignore: true + std::vector&&, int64_t: + ignore: true + MakeIntegerArray: + overloads: + std::span, int64_t: + ignore: true + std::initializer_list, int64_t: + ignore: true + std::vector&&, int64_t: + MakeFloatArray: + overloads: + std::span, int64_t: + ignore: true + std::initializer_list, int64_t: + ignore: true + std::vector&&, int64_t: + MakeDoubleArray: + overloads: + std::span, int64_t: + ignore: true + std::initializer_list, int64_t: + ignore: true + std::vector&&, int64_t: + MakeStringArray: + overloads: + std::span, int64_t: + ignore: true + std::initializer_list, int64_t: + ignore: true + std::vector&&, int64_t: + inline_code: |- + .def_static("makeValue", [](py::handle value) { + return pyntcore::py2ntvalue(value); + }, py::arg("value")) + .def_static("getFactoryByType", [](nt::NetworkTableType type) { + return pyntcore::valueFactoryByType(type); + }, py::arg("type")) + .def("__repr__", [](const Value &self) -> py::str { + auto typestr = pyntcore::nttype2str(self.type()); + auto valrepr = py::repr(pyntcore::ntvalue2py(self)); + return py::str("").format(typestr, valrepr); + }) diff --git a/ntcore/src/main/python/semiwrap/RawTopic.yml b/ntcore/src/main/python/semiwrap/RawTopic.yml new file mode 100644 index 0000000000..a7a844d4c3 --- /dev/null +++ b/ntcore/src/main/python/semiwrap/RawTopic.yml @@ -0,0 +1,114 @@ +extra_includes: +- src/nt_type_caster.h + +classes: + nt::RawSubscriber: + methods: + RawSubscriber: + overloads: + '': + ignore: true + NT_Subscriber, ParamType: + ignore: true + Get: + overloads: + '[const]': + ParamType [const]: + wpi::SmallVectorImpl& [const]: + ignore: true + wpi::SmallVectorImpl&, ParamType [const]: + ignore: true + GetAtomic: + overloads: + '[const]': + ParamType [const]: + wpi::SmallVectorImpl& [const]: + ignore: true + wpi::SmallVectorImpl&, ParamType [const]: + ignore: true + ReadQueue: + GetTopic: + inline_code: | + .def("close", [](RawSubscriber *self) { + py::gil_scoped_release release; + *self = RawSubscriber(); + }, py::doc("Destroys the subscriber")) + .def("__enter__", [](RawSubscriber *self) { + return self; + }) + .def("__exit__", [](RawSubscriber *self, py::args args) { + py::gil_scoped_release release; + *self = RawSubscriber(); + }) + nt::RawPublisher: + methods: + RawPublisher: + overloads: + '': + ignore: true + NT_Publisher: + ignore: true + Set: + SetDefault: + GetTopic: + inline_code: | + .def("close", [](RawPublisher *self) { + py::gil_scoped_release release; + *self = RawPublisher(); + }, py::doc("Destroys the publisher")) + .def("__enter__", [](RawPublisher *self) { + return self; + }) + .def("__exit__", [](RawPublisher *self, py::args args) { + py::gil_scoped_release release; + *self = RawPublisher(); + }) + nt::RawEntry: + methods: + RawEntry: + overloads: + '': + ignore: true + NT_Entry, ParamType: + ignore: true + GetHandle: + ignore: true + GetTopic: + Unpublish: + inline_code: | + .def("close", [](RawEntry *self) { + py::gil_scoped_release release; + *self = RawEntry(); + }, py::doc("Destroys the entry")) + .def("__enter__", [](RawEntry *self) { + return self; + }) + .def("__exit__", [](RawEntry *self, py::args args) { + py::gil_scoped_release release; + *self = RawEntry(); + }) + nt::RawTopic: + methods: + RawTopic: + overloads: + '': + ignore: true + NT_Topic: + ignore: true + Topic: + Subscribe: + Publish: + PublishEx: + GetEntry: + inline_code: | + .def("close", [](RawTopic *self) { + py::gil_scoped_release release; + *self = RawTopic(); + }, py::doc("Destroys the topic")) + .def("__enter__", [](RawTopic *self) { + return self; + }) + .def("__exit__", [](RawTopic *self, py::args args) { + py::gil_scoped_release release; + *self = RawTopic(); + }) diff --git a/ntcore/src/main/python/semiwrap/StringArrayTopic.yml b/ntcore/src/main/python/semiwrap/StringArrayTopic.yml new file mode 100644 index 0000000000..c55444273e --- /dev/null +++ b/ntcore/src/main/python/semiwrap/StringArrayTopic.yml @@ -0,0 +1,107 @@ +classes: + nt::StringArraySubscriber: + methods: + StringArraySubscriber: + overloads: + '': + ignore: true + NT_Subscriber, ParamType: + ignore: true + Get: + overloads: + '[const]': + ParamType [const]: + GetAtomic: + overloads: + '[const]': + ParamType [const]: + ReadQueue: + GetTopic: + inline_code: | + .def("close", [](StringArraySubscriber *self) { + py::gil_scoped_release release; + *self = StringArraySubscriber(); + }, py::doc("Destroys the subscriber")) + .def("__enter__", [](StringArraySubscriber *self) { + return self; + }) + .def("__exit__", [](StringArraySubscriber *self, py::args args) { + py::gil_scoped_release release; + *self = StringArraySubscriber(); + }) + nt::StringArrayPublisher: + methods: + StringArrayPublisher: + overloads: + '': + ignore: true + NT_Publisher: + ignore: true + Set: + SetDefault: + GetTopic: + inline_code: | + .def("close", [](StringArrayPublisher *self) { + py::gil_scoped_release release; + *self = StringArrayPublisher(); + }, py::doc("Destroys the publisher")) + .def("__enter__", [](StringArrayPublisher *self) { + return self; + }) + .def("__exit__", [](StringArrayPublisher *self, py::args args) { + py::gil_scoped_release release; + *self = StringArrayPublisher(); + }) + nt::StringArrayEntry: + methods: + StringArrayEntry: + overloads: + '': + ignore: true + NT_Entry, ParamType: + ignore: true + GetHandle: + ignore: true + GetTopic: + Unpublish: + inline_code: | + .def("close", [](StringArrayEntry *self) { + py::gil_scoped_release release; + *self = StringArrayEntry(); + }, py::doc("Destroys the entry")) + .def("__enter__", [](StringArrayEntry *self) { + return self; + }) + .def("__exit__", [](StringArrayEntry *self, py::args args) { + py::gil_scoped_release release; + *self = StringArrayEntry(); + }) + nt::StringArrayTopic: + attributes: + kTypeString: + methods: + StringArrayTopic: + overloads: + '': + ignore: true + NT_Topic: + ignore: true + Topic: + Subscribe: + SubscribeEx: + Publish: + PublishEx: + GetEntry: + GetEntryEx: + inline_code: | + .def("close", [](StringArrayTopic *self) { + py::gil_scoped_release release; + *self = StringArrayTopic(); + }, py::doc("Destroys the topic")) + .def("__enter__", [](StringArrayTopic *self) { + return self; + }) + .def("__exit__", [](StringArrayTopic *self, py::args args) { + py::gil_scoped_release release; + *self = StringArrayTopic(); + }) diff --git a/ntcore/src/main/python/semiwrap/StringTopic.yml b/ntcore/src/main/python/semiwrap/StringTopic.yml new file mode 100644 index 0000000000..fc908747e5 --- /dev/null +++ b/ntcore/src/main/python/semiwrap/StringTopic.yml @@ -0,0 +1,115 @@ +classes: + nt::StringSubscriber: + methods: + StringSubscriber: + overloads: + '': + ignore: true + NT_Subscriber, ParamType: + ignore: true + Get: + overloads: + '[const]': + ParamType [const]: + wpi::SmallVectorImpl& [const]: + ignore: true + wpi::SmallVectorImpl&, ParamType [const]: + ignore: true + GetAtomic: + overloads: + '[const]': + ParamType [const]: + wpi::SmallVectorImpl& [const]: + ignore: true + wpi::SmallVectorImpl&, ParamType [const]: + ignore: true + ReadQueue: + GetTopic: + inline_code: | + .def("close", [](StringSubscriber *self) { + py::gil_scoped_release release; + *self = StringSubscriber(); + }, py::doc("Destroys the subscriber")) + .def("__enter__", [](StringSubscriber *self) { + return self; + }) + .def("__exit__", [](StringSubscriber *self, py::args args) { + py::gil_scoped_release release; + *self = StringSubscriber(); + }) + nt::StringPublisher: + methods: + StringPublisher: + overloads: + '': + ignore: true + NT_Publisher: + ignore: true + Set: + SetDefault: + GetTopic: + inline_code: | + .def("close", [](StringPublisher *self) { + py::gil_scoped_release release; + *self = StringPublisher(); + }, py::doc("Destroys the publisher")) + .def("__enter__", [](StringPublisher *self) { + return self; + }) + .def("__exit__", [](StringPublisher *self, py::args args) { + py::gil_scoped_release release; + *self = StringPublisher(); + }) + nt::StringEntry: + methods: + StringEntry: + overloads: + '': + ignore: true + NT_Entry, ParamType: + ignore: true + GetHandle: + ignore: true + GetTopic: + Unpublish: + inline_code: | + .def("close", [](StringEntry *self) { + py::gil_scoped_release release; + *self = StringEntry(); + }, py::doc("Destroys the entry")) + .def("__enter__", [](StringEntry *self) { + return self; + }) + .def("__exit__", [](StringEntry *self, py::args args) { + py::gil_scoped_release release; + *self = StringEntry(); + }) + nt::StringTopic: + attributes: + kTypeString: + methods: + StringTopic: + overloads: + '': + ignore: true + NT_Topic: + ignore: true + Topic: + Subscribe: + SubscribeEx: + Publish: + PublishEx: + GetEntry: + GetEntryEx: + inline_code: | + .def("close", [](StringTopic *self) { + py::gil_scoped_release release; + *self = StringTopic(); + }, py::doc("Destroys the topic")) + .def("__enter__", [](StringTopic *self) { + return self; + }) + .def("__exit__", [](StringTopic *self, py::args args) { + py::gil_scoped_release release; + *self = StringTopic(); + }) diff --git a/ntcore/src/main/python/semiwrap/StructArrayTopic.yml b/ntcore/src/main/python/semiwrap/StructArrayTopic.yml new file mode 100644 index 0000000000..88960b4992 --- /dev/null +++ b/ntcore/src/main/python/semiwrap/StructArrayTopic.yml @@ -0,0 +1,167 @@ +classes: + nt::StructArraySubscriber: + template_params: + - T + - I + methods: + StructArraySubscriber: + overloads: + '': + ignore: true + NT_Subscriber, U&&, I...: + ignore: true + Get: + overloads: + '[const]': + U&& [const]: + ignore: true + std::span [const]: + GetAtomic: + overloads: + '[const]': + U&& [const]: + ignore: true + std::span [const]: + ReadQueue: + GetTopic: + inline_code: | + .def("close", [](nt::StructArraySubscriber *self) { + py::gil_scoped_release release; + *self = nt::StructArraySubscriber(); + }, py::doc("Destroys the subscriber")) + .def("__enter__", [](nt::StructArraySubscriber *self) { + return self; + }) + .def("__exit__", [](nt::StructArraySubscriber *self, py::args args) { + py::gil_scoped_release release; + *self = nt::StructArraySubscriber(); + }) + nt::StructArrayPublisher: + template_params: + - T + - I + methods: + StructArrayPublisher: + overloads: + '': + ignore: true + NT_Publisher, I...: + ignore: true + Set: + overloads: + U&&, int64_t: + ignore: true + std::span, int64_t: + SetDefault: + overloads: + U&&: + ignore: true + std::span: + GetTopic: + inline_code: | + .def("close", [](nt::StructArrayPublisher *self) { + py::gil_scoped_release release; + *self = nt::StructArrayPublisher(); + }, py::doc("Destroys the publisher")) + .def("__enter__", [](nt::StructArrayPublisher *self) { + return self; + }) + .def("__exit__", [](nt::StructArrayPublisher *self, py::args args) { + py::gil_scoped_release release; + *self = nt::StructArrayPublisher(); + }) + nt::StructArrayEntry: + template_params: + - T + - I + base_qualnames: + StructArraySubscriber: nt::StructArraySubscriber + StructArrayPublisher: nt::StructArrayPublisher + methods: + StructArrayEntry: + overloads: + '': + ignore: true + NT_Entry, U&&, const I&...: + ignore: true + GetHandle: + ignore: true + GetTopic: + Unpublish: + inline_code: | + .def("close", [](nt::StructArrayEntry *self) { + py::gil_scoped_release release; + *self = nt::StructArrayEntry(); + }, py::doc("Destroys the entry")) + .def("__enter__", [](nt::StructArrayEntry *self) { + return self; + }) + .def("__exit__", [](nt::StructArrayEntry *self, py::args args) { + py::gil_scoped_release release; + *self = nt::StructArrayEntry(); + }) + nt::StructArrayTopic: + template_params: + - T + - I + methods: + StructArrayTopic: + overloads: + '': + ignore: true + NT_Topic, I...: + ignore: true + Topic, I...: + param_override: + info: + name: type + cpp_code: | + [](Topic topic, const py::type &t) { + WPyStructInfo info(t); + return nt::StructArrayTopic(topic, info); + } + Subscribe: + overloads: + U&&, const PubSubOptions&: + ignore: true + std::span, const PubSubOptions&: + Publish: + PublishEx: + GetEntry: + overloads: + U&&, const PubSubOptions&: + ignore: true + std::span, const PubSubOptions&: + inline_code: | + .def("close", [](nt::StructArrayTopic *self) { + py::gil_scoped_release release; + *self = nt::StructArrayTopic(); + }, py::doc("Destroys the topic")) + .def("__enter__", [](nt::StructArrayTopic *self) { + return self; + }) + .def("__exit__", [](nt::StructArrayTopic *self, py::args args) { + py::gil_scoped_release release; + *self = nt::StructArrayTopic(); + }) +templates: + StructArraySubscriber: + qualname: nt::StructArraySubscriber + params: + - WPyStruct + - WPyStructInfo + StructArrayPublisher: + qualname: nt::StructArrayPublisher + params: + - WPyStruct + - WPyStructInfo + StructArrayEntry: + qualname: nt::StructArrayEntry + params: + - WPyStruct + - WPyStructInfo + StructArrayTopic: + qualname: nt::StructArrayTopic + params: + - WPyStruct + - WPyStructInfo diff --git a/ntcore/src/main/python/semiwrap/StructTopic.yml b/ntcore/src/main/python/semiwrap/StructTopic.yml new file mode 100644 index 0000000000..694f0af8da --- /dev/null +++ b/ntcore/src/main/python/semiwrap/StructTopic.yml @@ -0,0 +1,149 @@ +classes: + nt::StructSubscriber: + template_params: + - T + - I + methods: + StructSubscriber: + overloads: + '': + ignore: true + NT_Subscriber, T, I...: + ignore: true + Get: + overloads: + '[const]': + const T& [const]: + GetInto: + ignore: true + GetAtomic: + overloads: + '[const]': + const T& [const]: + ReadQueue: + GetTopic: + inline_code: | + .def("close", [](nt::StructSubscriber *self) { + py::gil_scoped_release release; + *self = nt::StructSubscriber(); + }, py::doc("Destroys the subscriber")) + .def("__enter__", [](nt::StructSubscriber *self) { + return self; + }) + .def("__exit__", [](nt::StructSubscriber *self, py::args args) { + py::gil_scoped_release release; + *self = nt::StructSubscriber(); + }) + nt::StructPublisher: + template_params: + - T + - I + methods: + StructPublisher: + overloads: + '': + ignore: true + NT_Publisher, I...: + ignore: true + Set: + SetDefault: + GetTopic: + inline_code: | + .def("close", [](nt::StructPublisher *self) { + py::gil_scoped_release release; + *self = nt::StructPublisher(); + }, py::doc("Destroys the publisher")) + .def("__enter__", [](nt::StructPublisher *self) { + return self; + }) + .def("__exit__", [](nt::StructPublisher *self, py::args args) { + py::gil_scoped_release release; + *self = nt::StructPublisher(); + }) + nt::StructEntry: + template_params: + - T + - I + base_qualnames: + StructSubscriber: nt::StructSubscriber + StructPublisher: nt::StructPublisher + methods: + StructEntry: + overloads: + '': + ignore: true + NT_Entry, T, const I&...: + ignore: true + GetHandle: + ignore: true + GetTopic: + Unpublish: + inline_code: | + .def("close", [](nt::StructEntry *self) { + py::gil_scoped_release release; + *self = nt::StructEntry(); + }, py::doc("Destroys the entry")) + .def("__enter__", [](nt::StructEntry *self) { + return self; + }) + .def("__exit__", [](nt::StructEntry *self, py::args args) { + py::gil_scoped_release release; + *self = nt::StructEntry(); + }) + nt::StructTopic: + template_params: + - T + - I + methods: + StructTopic: + overloads: + '': + ignore: true + NT_Topic, I...: + ignore: true + Topic, I...: + param_override: + info: + name: type + cpp_code: | + [](Topic topic, const py::type &t) { + WPyStructInfo info(t); + return nt::StructTopic(topic, info); + } + Subscribe: + Publish: + PublishEx: + GetEntry: + inline_code: | + .def("close", [](nt::StructTopic *self) { + py::gil_scoped_release release; + *self = nt::StructTopic(); + }, py::doc("Destroys the topic")) + .def("__enter__", [](nt::StructTopic *self) { + return self; + }) + .def("__exit__", [](nt::StructTopic *self, py::args args) { + py::gil_scoped_release release; + *self = nt::StructTopic(); + }) +templates: + StructSubscriber: + qualname: nt::StructSubscriber + params: + - WPyStruct + - WPyStructInfo + StructPublisher: + qualname: nt::StructPublisher + params: + - WPyStruct + - WPyStructInfo + StructEntry: + qualname: nt::StructEntry + params: + - WPyStruct + - WPyStructInfo + StructTopic: + qualname: nt::StructTopic + params: + - WPyStruct + - WPyStructInfo diff --git a/ntcore/src/main/python/semiwrap/Topic.yml b/ntcore/src/main/python/semiwrap/Topic.yml new file mode 100644 index 0000000000..d2a951a84a --- /dev/null +++ b/ntcore/src/main/python/semiwrap/Topic.yml @@ -0,0 +1,89 @@ +extra_includes: +- networktables/GenericEntry.h +- networktables/NetworkTableInstance.h + +classes: + nt::Topic: + methods: + Topic: + overloads: + '': + ignore: true + NT_Topic: + ignore: true + GetHandle: + ignore: true + GetInstance: + GetName: + GetType: + GetTypeString: + SetPersistent: + IsPersistent: + SetRetained: + IsRetained: + SetCached: + IsCached: + Exists: + GetProperty: + SetProperty: + DeleteProperty: + GetProperties: + SetProperties: + GetInfo: + GenericSubscribe: + overloads: + const PubSubOptions&: + std::string_view, const PubSubOptions&: + GenericPublish: + GenericPublishEx: + GetGenericEntry: + overloads: + const PubSubOptions&: + std::string_view, const PubSubOptions&: + operator==: + inline_code: | + .def("__repr__", [](py::handle self) { + py::object type_name = self.get_type().attr("__qualname__"); + std::string name = self.cast().GetName(); + return py::str("<{} {!r}>").format(type_name, name); + }) + nt::Subscriber: + attributes: + m_subHandle: + methods: + GetHandle: + ignore: true + Exists: + GetLastChange: + GetTopic: + Subscriber: + overloads: + '': + ignore: true + NT_Subscriber: + ignore: true + inline_code: | + .def("__repr__", [](py::handle self) { + py::object type_name = self.get_type().attr("__qualname__"); + auto topic = self.cast().GetTopic(); + return py::str("<{} {!r}>").format(type_name, topic.GetName()); + }) + nt::Publisher: + attributes: + m_pubHandle: + methods: + GetHandle: + ignore: true + GetTopic: + Publisher: + overloads: + '': + ignore: true + NT_Publisher: + ignore: true + inline_code: | + .def("__repr__", [](py::handle self) { + py::object type_name = self.get_type().attr("__qualname__"); + auto topic = self.cast().GetTopic(); + return py::str("<{} {!r}>").format(type_name, topic.GetName()); + }) diff --git a/ntcore/src/main/python/semiwrap/ntcore_cpp.yml b/ntcore/src/main/python/semiwrap/ntcore_cpp.yml new file mode 100644 index 0000000000..6e1a237f12 --- /dev/null +++ b/ntcore/src/main/python/semiwrap/ntcore_cpp.yml @@ -0,0 +1,317 @@ +defaults: + ignore: true + report_ignored_missing: false + +extra_includes: +- pybind11/stl.h +- networktables/Topic.h + +functions: + RemoveListener: + internal: true + Now: + internal: true + SetNow: + internal: true + AddPolledLogger: + internal: true + HasSchema: + ignore: true + DecodeTopicPublishers: + subpackage: meta + DecodeTopicSubscribers: + subpackage: meta + DecodeClientPublishers: + subpackage: meta + DecodeClientSubscribers: + subpackage: meta + DecodeClients: + subpackage: meta +classes: + nt::EventFlags: + attributes: + kNone: + kImmediate: + kConnected: + kDisconnected: + kConnection: + kPublish: + kUnpublish: + kProperties: + kTopic: + kValueRemote: + kValueLocal: + kValueAll: + kLogMessage: + kTimeSync: + nt::TopicInfo: + attributes: + name: + type_str: + properties: + topic: + ignore: true + type: + ignore: true + methods: + GetProperties: + inline_code: | + .def_property_readonly("topic", [](const TopicInfo &self) { + return std::make_shared(self.topic); + }) + .def_property_readonly("type", [](const TopicInfo &self) { + return nt::NetworkTableType(self.type); + }) + .def("__repr__", [](const TopicInfo &self) -> py::str { + return py::str("") + .format(self.name, self.type_str); + }) + nt::ConnectionInfo: + attributes: + remote_id: + remote_ip: + remote_port: + last_update: + protocol_version: + inline_code: | + .def("__repr__", [](const ConnectionInfo &self) -> py::str { + return py::str("") + .format(self.remote_id, self.remote_ip, self.remote_port, + self.last_update, self.protocol_version); + }) + nt::ValueEventData: + attributes: + topic: + ignore: true + subentry: + ignore: true + value: + methods: + ValueEventData: + overloads: + '': + ignore: true + NT_Topic, NT_Handle, Value: + ignore: true + inline_code: | + .def_property_readonly("topic", [](const ValueEventData &self) { + return std::make_shared(self.topic); + }) + .def("__repr__", [](const ValueEventData &self) -> py::str { + auto topicInfo = nt::GetTopicInfo(self.topic); + return py::str("") + .format(topicInfo.name, topicInfo.type_str, self.value); + }) + nt::LogMessage: + attributes: + level: + filename: + line: + message: + methods: + LogMessage: + overloads: + '': + ignore: true + unsigned int, std::string_view, unsigned int, std::string_view: + ignore: true + nt::TimeSyncEventData: + attributes: + serverTimeOffset: + rtt2: + valid: + methods: + TimeSyncEventData: + overloads: + '': + ignore: true + int64_t, int64_t, bool: + nt::Event: + attributes: + listener: + flags: + data: + ignore: true + methods: + Event: + overloads: + '': + ignore: true + NT_Listener, unsigned int, ConnectionInfo: + ignore: true + NT_Listener, unsigned int, TopicInfo: + ignore: true + NT_Listener, unsigned int, ValueEventData: + ignore: true + NT_Listener, unsigned int, LogMessage: + ignore: true + NT_Listener, unsigned int, NT_Topic, NT_Handle, Value: + ignore: true + NT_Listener, unsigned int, unsigned int, std::string_view, unsigned int, std::string_view: + ignore: true + NT_Listener, unsigned int, int64_t, int64_t, bool: + ignore: true + Is: + rename: is_ + GetConnectionInfo: + overloads: + '[const]': + ignore: true + '': + ignore: true + GetTopicInfo: + overloads: + '[const]': + ignore: true + '': + ignore: true + GetValueEventData: + overloads: + '[const]': + ignore: true + '': + ignore: true + GetLogMessage: + overloads: + '[const]': + ignore: true + '': + ignore: true + GetTimeSyncEventData: + overloads: + '[const]': + ignore: true + '': + ignore: true + inline_code: | + .def_property_readonly("data", [](nt::Event *self){ + return self->data; + }) + .def("__repr__", [](const nt::Event &self) -> py::str { + return py::str("") + .format(self.listener, self.flags, self.data); + }) + nt::PubSubOptions: + force_no_default_constructor: true + attributes: + kDefaultPeriodic: + structSize: + ignore: true + pollStorage: + periodic: + excludePublisher: + sendAll: + topicsOnly: + keepDuplicates: + prefixMatch: + disableRemote: + disableLocal: + excludeSelf: + hidden: + inline_code: | + // autogenerated by gen-pubsub.py + .def(py::init([]( + unsigned int pollStorage, + double periodic, + std::optional> excludePublisher, + bool sendAll, + bool topicsOnly, + bool keepDuplicates, + bool prefixMatch, + bool disableRemote, + bool disableLocal, + bool excludeSelf, + bool hidden + ) -> nt::PubSubOptions { + return nt::PubSubOptions{ + .pollStorage = pollStorage, + .periodic = periodic, + .excludePublisher = excludePublisher.has_value() ? excludePublisher.value()->GetHandle() : 0, + .sendAll = sendAll, + .topicsOnly = topicsOnly, + .keepDuplicates = keepDuplicates, + .prefixMatch = prefixMatch, + .disableRemote = disableRemote, + .disableLocal = disableLocal, + .excludeSelf = excludeSelf, + .hidden = hidden + }; + }), + py::kw_only(), + py::arg("pollStorage") = 0, + py::arg("periodic") = nt::PubSubOptions::kDefaultPeriodic, + py::arg("excludePublisher") = std::nullopt, + py::arg("sendAll") = false, + py::arg("topicsOnly") = false, + py::arg("keepDuplicates") = false, + py::arg("prefixMatch") = false, + py::arg("disableRemote") = false, + py::arg("disableLocal") = false, + py::arg("excludeSelf") = false, + py::arg("hidden") = false, + R"( + + + :param pollStorage: Polling storage size for a subscription. Specifies the maximum number of + updates NetworkTables should store between calls to the subscriber's + ReadQueue() function. If zero, defaults to 1 if sendAll is false, 20 if + sendAll is true. + :param periodic: How frequently changes will be sent over the network, in seconds. + NetworkTables may send more frequently than this (e.g. use a combined + minimum period for all values) or apply a restricted range to this value. + The default is 100 ms. + :param excludePublisher: For subscriptions, if non-zero, value updates for ReadQueue() are not + queued for this publisher. + :param sendAll: Send all value changes over the network. + :param topicsOnly: For subscriptions, don't ask for value changes (only topic announcements). + :param keepDuplicates: Preserve duplicate value changes (rather than ignoring them). + :param prefixMatch: Perform prefix match on subscriber topic names. Is ignored/overridden by + Subscribe() functions; only present in struct for the purposes of getting + information about subscriptions. + :param disableRemote: For subscriptions, if remote value updates should not be queued for + ReadQueue(). See also disableLocal. + :param disableLocal: For subscriptions, if local value updates should not be queued for + ReadQueue(). See also disableRemote. + :param excludeSelf: For entries, don't queue (for ReadQueue) value updates for the entry's + internal publisher. + :param hidden: For subscriptions, don't share the existence of the subscription with the + network. Note this means updates will not be received from the network + unless another subscription overlaps with this one, and the subscription + will not appear in metatopics. + )" + ) + nt::meta::SubscriberOptions: + subpackage: meta + attributes: + periodic: + topicsOnly: + sendAll: + prefixMatch: + nt::meta::TopicPublisher: + subpackage: meta + attributes: + client: + pubuid: + nt::meta::TopicSubscriber: + subpackage: meta + attributes: + client: + subuid: + options: + nt::meta::ClientPublisher: + subpackage: meta + attributes: + uid: + topic: + nt::meta::ClientSubscriber: + subpackage: meta + attributes: + uid: + topics: + options: + nt::meta::Client: + subpackage: meta + attributes: + id: + conn: + version: diff --git a/ntcore/src/main/python/semiwrap/ntcore_cpp_types.yml b/ntcore/src/main/python/semiwrap/ntcore_cpp_types.yml new file mode 100644 index 0000000000..0fddb11e91 --- /dev/null +++ b/ntcore/src/main/python/semiwrap/ntcore_cpp_types.yml @@ -0,0 +1,82 @@ +extra_includes: +- src/nt_type_caster.h + +defaults: + ignore: true + report_ignored_missing: false + +classes: + nt::Timestamped: + template_params: + - T + attributes: + time: + serverTime: + value: + methods: + Timestamped: + overloads: + '': + int64_t, int64_t, T: + inline_code: | + ; + // capture class name by value + auto clsname = clsName; + cls_Timestamped.def("__repr__", [clsname](const Timestamped& self) -> py::str { + return py::str("{}(time={}, serverTime={}, value={!r})") + .format(clsname, self.time, self.serverTime, self.value); + }) + +templates: + TimestampedBoolean: + qualname: nt::Timestamped + params: + - bool + TimestampedInteger: + qualname: nt::Timestamped + params: + - int64_t + TimestampedFloat: + qualname: nt::Timestamped + params: + - float + TimestampedDouble: + qualname: nt::Timestamped + params: + - double + TimestampedString: + qualname: nt::Timestamped + params: + - std::string + TimestampedRaw: + qualname: nt::Timestamped + params: + - std::vector + TimestampedBooleanArray: + qualname: nt::Timestamped + params: + - std::vector + TimestampedIntegerArray: + qualname: nt::Timestamped + params: + - std::vector + TimestampedFloatArray: + qualname: nt::Timestamped + params: + - std::vector + TimestampedDoubleArray: + qualname: nt::Timestamped + params: + - std::vector + TimestampedStringArray: + qualname: nt::Timestamped + params: + - std::vector + TimestampedStruct: + qualname: nt::Timestamped + params: + - WPyStruct + TimestampedStructArray: + qualname: nt::Timestamped + params: + - std::vector diff --git a/ntcore/src/test/python/conftest.py b/ntcore/src/test/python/conftest.py new file mode 100644 index 0000000000..d7136a7f8c --- /dev/null +++ b/ntcore/src/test/python/conftest.py @@ -0,0 +1,165 @@ +# +# Useful fixtures +# + +from contextlib import contextmanager +from threading import Condition + +import logging + +logger = logging.getLogger("conftest") + +import pytest + +from ntcore import NetworkTableInstance, MultiSubscriber, Event, EventFlags + +# +# Fixtures for a usable in-memory version of networktables +# + + +@pytest.fixture +def cfg_logging(caplog): + caplog.set_level(logging.INFO) + + +@pytest.fixture(scope="function") +def nt(cfg_logging): + instance = NetworkTableInstance.create() + instance.startLocal() + + try: + yield instance + finally: + NetworkTableInstance.destroy(instance) + + +# +# Live NT instance fixtures +# + + +class NtTestBase: + """ + Object for managing a live pair of NT server/client + """ + + _wait_lock = None + + def __init__(self): + self.reset() + + def reset(self): + self._impl = NetworkTableInstance.create() + self.getTable = self._impl.getTable + self.isConnected = self._impl.isConnected + + def shutdown(self): + logger.info("shutting down %s", self.__class__.__name__) + if self._impl: + NetworkTableInstance.destroy(self._impl) + self._impl = None + + # def disconnect(self): + # self._api.dispatcher.stop() + + def _init_common(self): + # This resets the instance to be independent + self.shutdown() + self.reset() + + # self._wait_init() + + def _init_server(self, port=23232): + self._init_common() + + self.port = port + + def _init_client(self): + self._init_common() + + def _wait_init(self): + logger.info("wait-init %s", self.__class__.__name__) + self._wait_lock = Condition() + self._wait = 0 + + self.msub = MultiSubscriber(self._impl, [""]) + self.vl = self._impl.addListener( + self.msub, EventFlags.kValueRemote, self._wait_cb + ) + + def _wait_cb(self, evt: Event): + logger.info("wait-callback %s: %s", self.__class__.__name__, evt) + with self._wait_lock: + self._wait += 1 + self._wait_lock.notify() + + @contextmanager + def expect_changes(self, count): + """Use this on the *other* instance that you're making + changes on, to wait for the changes to propagate to the + other instance""" + + if self._wait_lock is None: + self._wait_init() + + with self._wait_lock: + self._wait = 0 + + logger.info("Begin actions") + yield + logger.info("Waiting for %s changes", count) + + with self._wait_lock: + self._wait_lock.wait_for(lambda: self._wait >= count, 4) + logger.info("expect_changes: %s == %s", self._wait, count) + + msg = "Failed waiting for exactly %s changes (got %s)" % (count, self._wait) + assert self._wait == count, msg + + +@pytest.fixture() +def nt_server(request, cfg_logging): + class NtServer(NtTestBase): + _test_saved_port = None + + def start_test(self): + logger.info("NtServer::start_test") + + # Restore server port on restart + if self._test_saved_port is not None: + self.port = self._test_saved_port + + self._impl.startServer(listen_address="127.0.0.1", port=self.port) + + self._test_saved_port = self.port + + server = NtServer() + server._init_server() + try: + yield server + finally: + server.shutdown() + + +@pytest.fixture() +def nt_client(request, nt_server): + class NtClient(NtTestBase): + def start_test(self): + self._impl.startClient("C4") + self._impl.setServer("127.0.0.1", nt_server.port) + + client = NtClient() + client._init_client() + yield client + client.shutdown() + + +@pytest.fixture +def nt_live(nt_server, nt_client): + """This fixture automatically starts the client and server""" + + nt_server.start_test() + nt_client.start_test() + + return nt_server, nt_client diff --git a/ntcore/src/test/python/test_api.py b/ntcore/src/test/python/test_api.py new file mode 100644 index 0000000000..2375a2e253 --- /dev/null +++ b/ntcore/src/test/python/test_api.py @@ -0,0 +1,155 @@ +# +# These tests stand up a separate client and server instance of +# networktables and tests the 'real' user API to ensure that it +# works correctly +# + +from __future__ import print_function + +import pytest + +import logging + +logger = logging.getLogger("test") + + +# test defaults +def doc(nt): + t = nt.getTable("nope") + + assert t.getBoolean("b", None) is None + assert t.getNumber("n", None) is None + assert t.getString("s", None) is None + assert t.getBooleanArray("ba", None) is None + assert t.getNumberArray("na", None) is None + assert t.getStringArray("sa", None) is None + assert t.getValue("v", None) is None + + assert t.getBoolean("b", True) is True + assert t.getNumber("n", 1) == 1 + assert t.getString("s", "sss") == "sss" + assert t.getBooleanArray("ba", (True,)) == (True,) + assert t.getNumberArray("na", (1,)) == (1,) + assert t.getStringArray("sa", ("ss",)) == ("ss",) + assert t.getValue("v", "vvv") == "vvv" + + +def do(nt1, nt2, t): + t1 = nt1.getTable(t) + with nt2.expect_changes(8): + t1.putBoolean("bool", True) + t1.putNumber("number1", 1) + t1.putNumber("number2", 1.5) + t1.putString("string", "string") + t1.putString("unicode", "\xa9") # copyright symbol + t1.putBooleanArray("ba", (True, False)) + t1.putNumberArray("na", (1, 2)) + t1.putStringArray("sa", ("s", "t")) + logger.info("put is done") + + t2 = nt2.getTable(t) + assert t2.getBoolean("bool", None) is True + assert t2.getNumber("number1", None) == 1 + assert t2.getNumber("number2", None) == 1.5 + assert t2.getString("string", None) == "string" + assert t2.getString("unicode", None) == "\xa9" # copyright symbol + assert t2.getBooleanArray("ba", None) == [True, False] + assert t2.getNumberArray("na", None) == [1, 2] + assert t2.getStringArray("sa", None) == ["s", "t"] + + # Value testing + with nt2.expect_changes(6): + t1.putValue("v_b", False) + t1.putValue("v_n1", 2) + t1.putValue("v_n2", 2.5) + t1.putValue("v_s", "ssss") + t1.putValue("v_s2", "\xa9") + + t1.putValue("v_v", 0) + + print(t2.getKeys()) + assert t2.getBoolean("v_b", None) is False + assert t2.getNumber("v_n1", None) == 2 + assert t2.getNumber("v_n2", None) == 2.5 + assert t2.getString("v_s", None) == "ssss" + assert t2.getString("v_s2", None) == "\xa9" + assert t2.getValue("v_v", None) == 0 + + # Ensure that updating values work! + with nt2.expect_changes(8): + t1.putBoolean("bool", False) + t1.putNumber("number1", 2) + t1.putNumber("number2", 2.5) + t1.putString("string", "sss") + t1.putString("unicode", "\u2122") # (tm) + t1.putBooleanArray("ba", (False, True, False)) + t1.putNumberArray("na", (2, 1)) + t1.putStringArray("sa", ("t", "s")) + + t2 = nt2.getTable(t) + assert t2.getBoolean("bool", None) is False + assert t2.getNumber("number1", None) == 2 + assert t2.getNumber("number2", None) == 2.5 + assert t2.getString("string", None) == "sss" + assert t2.getString("unicode", None) == "\u2122" + assert t2.getBooleanArray("ba", None) == [False, True, False] + assert t2.getNumberArray("na", None) == [2, 1] + assert t2.getStringArray("sa", None) == ["t", "s"] + + +@pytest.mark.xfail(reason="ntcore is broken") +def test_basic(nt_live): + nt_server, nt_client = nt_live + + # assert nt_server.isServer() + # assert not nt_client.isServer() + + doc(nt_client) + doc(nt_server) + + # server -> client + do(nt_server, nt_client, "server2client") + + # client -> server + do(nt_client, nt_server, "client2server") + + assert nt_client.isConnected() + assert nt_server.isConnected() + + +# def test_reconnect(nt_live): + +# nt_server, nt_client = nt_live + +# with nt_server.expect_changes(1): +# ct = nt_client.getTable("t") +# ct.putBoolean("foo", True) + +# st = nt_server.getTable("t") +# assert st.getBoolean("foo", None) == True + +# # Client disconnect testing +# nt_client.shutdown() + +# logger.info("Shutdown the client") + +# with nt_client.expect_changes(1): +# nt_client.start_test() +# ct = nt_client.getTable("t") + +# assert ct.getBoolean("foo", None) == True + +# # Server disconnect testing +# nt_server.shutdown() +# logger.info("Shutdown the server") + +# # synchronization change: if the client doesn't touch the entry locally, +# # then it won't get transferred back to the server on reconnect. Touch +# # it here to ensure that it comes back +# ct.putBoolean("foo", True) + +# with nt_server.expect_changes(1): +# nt_server.start_test() + +# st = nt_server.getTable("t") +# assert st.getBoolean("foo", None) == True diff --git a/ntcore/src/test/python/test_entry.py b/ntcore/src/test/python/test_entry.py new file mode 100644 index 0000000000..514227519b --- /dev/null +++ b/ntcore/src/test/python/test_entry.py @@ -0,0 +1,71 @@ +# +# Ensure that the NetworkTableEntry objects work +# + + +def test_entry_repr(nt): + e = nt.getEntry("/k1") + assert repr(e) == "" + + +def test_topic_repr(nt): + topic = nt.getIntegerTopic("/int") + assert repr(topic) == "" + + pub = topic.publish() + assert repr(pub) == "" + + entry = topic.getEntry(0) + assert repr(entry) == "" + + generic_entry = topic.getGenericEntry() + assert repr(generic_entry) == "" + + +def test_entry_string(nt): + e = nt.getEntry("/k1") + assert e.getString(None) is None + e.setString("value") + assert e.getString(None) == "value" + assert e.getValue().value() == "value" + assert e.value == "value" + e.unpublish() + assert e.getString(None) is None + e.setString("value") + assert e.getString(None) == "value" + + +def test_entry_string_array(nt): + e = nt.getEntry("/k1") + assert e.getStringArray(None) is None + e.setStringArray(["value"]) + assert e.getStringArray(None) == ["value"] + assert e.getValue().value() == ["value"] + assert e.value == ["value"] + e.unpublish() + assert e.getStringArray(None) is None + e.setStringArray(["value"]) + assert e.getStringArray(None) == ["value"] + + +def test_entry_persistence(nt): + e = nt.getEntry("/k2") + + for _ in range(2): + assert not e.isPersistent() + # persistent flag cannot be set unless the entry has a value + e.setString("value") + + assert not e.isPersistent() + e.setPersistent() + assert e.isPersistent() + e.clearPersistent() + assert not e.isPersistent() + + e.unpublish() + + +def test_entry_publish_empty_double_array(nt): + topic = nt.getDoubleArrayTopic("/Topic") + pub = topic.publish() + pub.set([]) diff --git a/ntcore/src/test/python/test_network_table.py b/ntcore/src/test/python/test_network_table.py new file mode 100644 index 0000000000..e8bd0d4930 --- /dev/null +++ b/ntcore/src/test/python/test_network_table.py @@ -0,0 +1,176 @@ +# +# These tests are leftover from the original pynetworktables tests +# + +import pytest + + +@pytest.fixture(scope="function") +def table1(nt): + return nt.getTable("/test1") + + +@pytest.fixture(scope="function") +def table2(nt): + return nt.getTable("/test2") + + +def test_put_double(table1): + table1.putNumber("double", 42.42) + assert table1.getNumber("double", None) == 42.42 + + assert table1.getNumber("Non-Existant", 44.44) == 44.44 + + +def test_put_boolean(table1): + table1.putBoolean("boolean", True) + assert table1.getBoolean("boolean", None) == True + + assert table1.getBoolean("Non-Existant", False) == False + + +def test_put_string(table1): + table1.putString("String", "Test 1") + assert table1.getString("String", None) == "Test 1" + + assert table1.getString("Non-Existant", "Test 3") == "Test 3" + + +def test_getvalue_overloads(table1): + table1.putValue("float", 35.5) + assert table1.getValue("float", None) == pytest.approx(35.5) + + table1.putValue("integer", 950) + assert table1.getValue("integer", None) == pytest.approx(950) + + table1.putValue("boolean", True) + assert table1.getValue("boolean", None) is True + + +def test_multi_data_type(table1): + table1.putNumber("double1", 1) + table1.putNumber("double2", 2) + table1.putNumber("double3", 3) + table1.putBoolean("bool1", False) + table1.putBoolean("bool2", True) + table1.putString("string1", "String 1") + table1.putString("string2", "String 2") + table1.putString("string3", "String 3") + + assert table1.getNumber("double1", None) == 1 + assert table1.getNumber("double2", None) == 2 + assert table1.getNumber("double3", None) == 3 + assert table1.getBoolean("bool1", None) == False + assert table1.getBoolean("bool2", None) == True + assert table1.getString("string1", None) == "String 1" + assert table1.getString("string2", None) == "String 2" + assert table1.getString("string3", None) == "String 3" + + table1.putNumber("double1", 4) + table1.putNumber("double2", 5) + table1.putNumber("double3", 6) + table1.putBoolean("bool1", True) + table1.putBoolean("bool2", False) + table1.putString("string1", "String 4") + table1.putString("string2", "String 5") + table1.putString("string3", "String 6") + + assert table1.getNumber("double1", None) == 4 + assert table1.getNumber("double2", None) == 5 + assert table1.getNumber("double3", None) == 6 + assert table1.getBoolean("bool1", None) == True + assert table1.getBoolean("bool2", None) == False + assert table1.getString("string1", None) == "String 4" + assert table1.getString("string2", None) == "String 5" + assert table1.getString("string3", None) == "String 6" + + +def test_multi_table(table1, table2): + table1.putNumber("table1double", 1) + table1.putBoolean("table1boolean", True) + table1.putString("table1string", "Table 1") + + assert table2.getNumber("table1double", None) == None + assert table2.getBoolean("table1boolean", None) == None + assert table2.getString("table1string", None) == None + + table2.putNumber("table2double", 2) + table2.putBoolean("table2boolean", False) + table2.putString("table2string", "Table 2") + + assert table1.getNumber("table2double", None) == None + assert table1.getBoolean("table2boolean", None) == None + assert table1.getString("table2string", None) == None + + +# def test_get_table(nt, table1, table2): +# assert nt.getTable("test1") is table1 +# assert nt.getTable("test2") is table2 + +# assert nt.getTable("/test1") is table1 +# assert nt.getTable("/test2") is table2 + +# assert nt.getTable("/test1/") is table1 +# assert nt.getTable("/test1/").path == "/test1" + +# assert table1 is not table2 + +# table3 = nt.getTable("/test3") +# assert table1 is not table3 +# assert table2 is not table3 + + +# def test_get_subtable(nt, table1): +# assert not table1.containsSubTable("test1") + +# st1 = table1.getSubTable("test1") + +# assert nt.getTable("/test1/test1") is st1 +# assert table1.getSubTable("test1") is st1 + +# # weird, but true -- subtable only exists when key exists +# assert not table1.containsSubTable("test1") +# st1.putBoolean("hi", True) +# assert table1.containsSubTable("test1") + +# assert table1.getSubTables() == ["test1"] +# assert st1.getSubTables() == [] + + +def test_getkeys(table1): + assert table1.getKeys() == [] + assert not table1.containsKey("hi") + assert "hi" not in table1 + + table1.putBoolean("hi", True) + assert table1.getKeys() == ["hi"] + + assert table1.containsKey("hi") + assert "hi" in table1 + + +def test_flags(table1): + table1.putBoolean("foo", True) + assert not table1.isPersistent("foo") + + table1.setPersistent("foo") + assert table1.isPersistent("foo") + + table1.clearPersistent("foo") + assert not table1.isPersistent("foo") + + +# def test_delete(table1): +# table1.putBoolean("foo", True) +# assert table1.getBoolean("foo", None) == True + +# table1.delete("foo") +# assert table1.getBoolean("foo", None) == None + + +def test_different_type(table1): + assert table1.putBoolean("foo", True) + assert table1.getBoolean("foo", None) == True + + assert not table1.putNumber("foo", 1) + assert table1.getBoolean("foo", None) == True diff --git a/ntcore/src/test/python/test_struct_topic.py b/ntcore/src/test/python/test_struct_topic.py new file mode 100644 index 0000000000..b45cd3f327 --- /dev/null +++ b/ntcore/src/test/python/test_struct_topic.py @@ -0,0 +1,55 @@ +import dataclasses + +import ntcore +from wpiutil import wpistruct + + +@wpistruct.make_wpistruct +@dataclasses.dataclass +class MyStruct: + x: int + y: bool + z: wpistruct.double + + +def test_mystruct(nt: ntcore.NetworkTableInstance): + topic = nt.getStructTopic("mystruct", MyStruct) + pub = topic.publish() + sub = topic.subscribe(MyStruct(1, True, 2.0)) + + assert topic.getTypeString() == "struct:MyStruct" + + default = MyStruct(6, False, 7.0) + pub.setDefault(default) + val = sub.get() + assert val == default + + # val2 = MyStruct(0, False, 0) + # sub.getInto(val2) + # assert val2 == default + + vals = sub.readQueue() + assert len(vals) == 1 + assert vals[0].value == default + + +def test_mystruct_array(nt: ntcore.NetworkTableInstance): + topic = nt.getStructArrayTopic("mystruct", MyStruct) + pub = topic.publish() + sub = topic.subscribe([]) + + assert topic.getTypeString() == "struct:MyStruct[]" + + default = MyStruct(8, True, 9.0) + pub.setDefault([default]) + val = sub.get() + assert val == [default] + + val2 = MyStruct(0, False, 0) + pub.set([val2]) + atomicVal = sub.getAtomic() + assert atomicVal.value == [val2] + + vals = sub.readQueue() + assert len(vals) == 1 + assert vals[0].value == [val2] diff --git a/ntcore/src/test/python/test_util.py b/ntcore/src/test/python/test_util.py new file mode 100644 index 0000000000..9f5d08034c --- /dev/null +++ b/ntcore/src/test/python/test_util.py @@ -0,0 +1,153 @@ +import pytest + +from ntcore import NetworkTableInstance, NetworkTableType +from ntcore.util import ntproperty, ChooserControl + + +# def test_autoupdatevalue(nt): + +# # tricksy: make sure that this works *before* initialization +# # of network tables happens! +# nt.shutdown() + +# foo = nt.getGlobalAutoUpdateValue("/SmartDashboard/foo", True, True) +# assert foo.value == True +# assert foo.get() == True + +# nt.startTestMode() + +# assert foo.value == True +# assert foo.get() == True + +# t = nt.getTable("/SmartDashboard") +# assert t.getBoolean("foo", None) == True +# t.putBoolean("foo", False) + +# assert foo.value == False + + +def test_ntproperty(nt: NetworkTableInstance): + class Foo(object): + robotTime = ntproperty( + "/SmartDashboard/robotTime", 0.0, writeDefault=False, inst=nt + ) + dsTime = ntproperty("/SmartDashboard/dsTime", 0.0, writeDefault=True, inst=nt) + testIntArray = ntproperty( + "/SmartDashboard/testIntArray", [1, 2, 3], writeDefault=True, inst=nt + ) + + testBoolArray = ntproperty( + "/SmartDashboard/testBoolArray", [True, False], writeDefault=True, inst=nt + ) + + testFloatArray = ntproperty( + "/SmartDashboard/testFloatArray", + [1.1, 1.2, 1.3], + writeDefault=True, + type=NetworkTableType.kFloatArray, + inst=nt, + ) + + f = Foo() + + t = nt.getTable("/SmartDashboard") + + assert f.robotTime == 0 + assert t.getNumber("robotTime", None) == 0 + + f.robotTime = 2 + assert t.getNumber("robotTime", None) == 2 + + t.putNumber("robotTime", 4) + assert f.robotTime == 4 + + assert f.testIntArray == [1, 2, 3] + f.testIntArray = [4, 5, 6] + assert f.testIntArray == [4, 5, 6] + + assert f.testBoolArray == [True, False] + f.testBoolArray = [False, True] + assert f.testBoolArray == [False, True] + + assert f.testFloatArray == [ + pytest.approx(1.1), + pytest.approx(1.2), + pytest.approx(1.3), + ] + f.testFloatArray = [4.1, 5.1, 6.1] + assert f.testFloatArray == [ + pytest.approx(4.1), + pytest.approx(5.1), + pytest.approx(6.1), + ] + + +def test_ntproperty_emptyarray(nt: NetworkTableInstance): + with pytest.raises(TypeError): + + class Foo1(object): + testArray = ntproperty( + "/SmartDashboard/testArray", [], writeDefault=True, inst=nt + ) + + with pytest.raises(TypeError): + + class Foo2(object): + testArray = ntproperty( + "/SmartDashboard/testArray", [], writeDefault=False, inst=nt + ) + + +def test_ntproperty_multitest(nt: NetworkTableInstance): + """ + Checks to see that ntproperties still work between NT restarts + + This is needed to ensure that ntproperty gets reset between + pyfrc tests + """ + + class Foo(object): + robotTime = ntproperty( + "/SmartDashboard/robotTime", 0.0, writeDefault=False, inst=nt + ) + dsTime = ntproperty("/SmartDashboard/dsTime", 0.0, writeDefault=True, inst=nt) + + for i in range(3): + print("Iteration", i) + + f = Foo() + + t = nt.getTable("/SmartDashboard") + + assert f.robotTime == 0 + assert f.dsTime == 0 + + assert t.getNumber("robotTime", None) == 0 + assert t.getNumber("dsTime", None) == 0 + + f.robotTime = 2 + assert t.getNumber("robotTime", None) == 2 + assert t.getNumber("dsTime", None) == 0 + + t.putNumber("robotTime", 4) + assert f.robotTime == 4 + assert f.dsTime == 0 + nt.stopLocal() + nt._reset() + nt.startLocal() + + +def test_chooser_control(nt: NetworkTableInstance): + c = ChooserControl("Autonomous Mode", inst=nt) + + assert c.getChoices() == [] + assert c.getSelected() is None + + c.setSelected("foo") + assert c.getSelected() == "foo" + + t = nt.getTable("/SmartDashboard/Autonomous Mode") + assert t.getString("selected", None) == "foo" + + t.putStringArray("options", ("option1", "option2")) + assert c.getChoices() == ["option1", "option2"] diff --git a/ntcore/src/test/python/test_value.py b/ntcore/src/test/python/test_value.py new file mode 100644 index 0000000000..278080f2fb --- /dev/null +++ b/ntcore/src/test/python/test_value.py @@ -0,0 +1,196 @@ +import ntcore +import pytest + + +def test_value_bool(): + v = ntcore.Value.makeBoolean(True) + assert v.getBoolean() == True + assert v.value() == True + + +def test_mkvalue_bool(): + v = ntcore.Value.makeValue(True) + assert v.getBoolean() == True + assert v.value() == True + + +def test_bool_factory(): + f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kBoolean) + v = f(True) + assert v.getBoolean() == True + + +def test_value_int(): + v = ntcore.Value.makeInteger(2) + assert v.getInteger() == 2 + assert v.value() == 2 + + +def test_mkvalue_int(): + v = ntcore.Value.makeValue(2) + assert v.getInteger() == 2 + assert v.value() == 2 + + +def test_int_factory(): + f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kInteger) + v = f(2) + assert v.getInteger() == 2 + + +def test_value_double(): + v = ntcore.Value.makeDouble(2.5) + assert v.getDouble() == pytest.approx(2.5) + assert v.value() == pytest.approx(2.5) + + +def test_mkvalue_double(): + v = ntcore.Value.makeValue(2.5) + assert v.getDouble() == pytest.approx(2.5) + assert v.value() == pytest.approx(2.5) + + +def test_double_factory(): + f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kDouble) + v = f(2.2) + assert v.getDouble() == pytest.approx(2.2) + + +def test_value_float(): + v = ntcore.Value.makeFloat(2.5) + assert v.getFloat() == pytest.approx(2.5) + assert v.value() == pytest.approx(2.5) + + +def test_mkvalue_float(): + pass # makeValue cannot create a float value + + +def test_float_factory(): + f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kFloat) + v = f(2.2) + assert v.getFloat() == pytest.approx(2.2) + + +def test_value_str(): + v = ntcore.Value.makeString("s") + assert v.getString() == "s" + assert v.value() == "s" + + +def test_mkvalue_str(): + v = ntcore.Value.makeValue("s") + assert v.getString() == "s" + assert v.value() == "s" + + +def test_str_factory(): + f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kString) + v = f("abc") + assert v.getString() == "abc" + + +def test_value_raw(): + v = ntcore.Value.makeRaw(b"raw") + assert v.getRaw() == b"raw" + assert v.value() == b"raw" + + +def test_mkvalue_raw(): + v = ntcore.Value.makeValue(b"raw") + assert v.getRaw() == b"raw" + assert v.value() == b"raw" + + +def test_raw_factory(): + f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kRaw) + v = f(b"raw") + assert v.getRaw() == b"raw" + + +def test_value_bool_list(): + v = ntcore.Value.makeBooleanArray([True, False]) + assert v.getBooleanArray() == [True, False] + assert v.value() == [True, False] + + +def test_mkvalue_bool_list(): + v = ntcore.Value.makeValue([True, False]) + assert v.getBooleanArray() == [True, False] + assert v.value() == [True, False] + + +def test_bool_list_factory(): + f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kBooleanArray) + v = f([True, False]) + assert v.getBooleanArray() == [True, False] + + +def test_value_int_list(): + v = ntcore.Value.makeIntegerArray([1, 2]) + assert v.getIntegerArray() == [1, 2] + assert v.value() == [1, 2] + + +def test_mkvalue_int_list(): + v = ntcore.Value.makeValue([1, 2]) + assert v.getIntegerArray() == [1, 2] + assert v.value() == [1, 2] + + +def test_int_list_factory(): + f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kIntegerArray) + v = f([1, 2]) + assert v.getIntegerArray() == [1, 2] + + +def test_value_double_list(): + v = ntcore.Value.makeDoubleArray([1.1, 2.2]) + assert v.getDoubleArray() == [pytest.approx(1.1), pytest.approx(2.2)] + assert v.value() == [pytest.approx(1.1), pytest.approx(2.2)] + + +def test_mkvalue_double_list(): + v = ntcore.Value.makeValue([1.1, 2.2]) + assert v.getDoubleArray() == [pytest.approx(1.1), pytest.approx(2.2)] + assert v.value() == [pytest.approx(1.1), pytest.approx(2.2)] + + +def test_double_list_factory(): + f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kDoubleArray) + v = f([1.1, 2.2]) + assert v.getDoubleArray() == [pytest.approx(1.1), pytest.approx(2.2)] + + +def test_value_float_list(): + v = ntcore.Value.makeFloatArray([1.1, 2.2]) + assert v.getFloatArray() == [pytest.approx(1.1), pytest.approx(2.2)] + assert v.value() == [pytest.approx(1.1), pytest.approx(2.2)] + + +def test_mkvalue_float_list(): + pass # not possible to use makeValue to make a float list + + +def test_float_list_factory(): + f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kFloatArray) + v = f([1.1, 2.2]) + assert v.getFloatArray() == [pytest.approx(1.1), pytest.approx(2.2)] + + +def test_value_str_list(): + v = ntcore.Value.makeStringArray(["a", "b"]) + assert v.getStringArray() == ["a", "b"] + assert v.value() == ["a", "b"] + + +def test_mkvalue_str_list(): + v = ntcore.Value.makeValue(["a", "b"]) + assert v.getStringArray() == ["a", "b"] + assert v.value() == ["a", "b"] + + +def test_str_list_factory(): + f = ntcore.Value.getFactoryByType(ntcore.NetworkTableType.kStringArray) + v = f(["a", "b"]) + assert v.getStringArray() == ["a", "b"] diff --git a/romiVendordep/.styleguide b/romiVendordep/.styleguide index 7c8d7a70fb..b9403ec6a1 100644 --- a/romiVendordep/.styleguide +++ b/romiVendordep/.styleguide @@ -8,6 +8,11 @@ cppSrcFileInclude { \.cpp$ } +generatedFileExclude { + src/main/python/ + src/test/python/ +} + repoRootNameOverride { wpilib } diff --git a/romiVendordep/src/main/python/README.md b/romiVendordep/src/main/python/README.md new file mode 100644 index 0000000000..943cbfbd78 --- /dev/null +++ b/romiVendordep/src/main/python/README.md @@ -0,0 +1,4 @@ +robotpy-romi +============ + +RobotPy support for the WPILib ROMI vendor library. diff --git a/romiVendordep/src/main/python/native-pyproject.toml b/romiVendordep/src/main/python/native-pyproject.toml new file mode 100644 index 0000000000..6da998c155 --- /dev/null +++ b/romiVendordep/src/main/python/native-pyproject.toml @@ -0,0 +1,39 @@ +[build-system] +build-backend = "hatchling.build" +requires = [ + "hatchling", + "hatch-nativelib~=0.2.0", + "hatch-robotpy~=0.2.1", + "robotpy-native-wpilib==2027.0.0a2", +] + +[project] +name = "robotpy-native-romi" +version = "2027.0.0a2" +description = "WPILib Romi support library" +license = "BSD-3-Clause" + +dependencies = [ + "robotpy-native-wpilib==2027.0.0a2", +] + +[tool.hatch.build.targets.wheel] +packages = ["src/native"] + +[[tool.hatch.build.hooks.robotpy.maven_lib_download]] +artifact_id = "romiVendordep-cpp" +group_id = "edu.wpi.first.romiVendordep" +repo_url = "https://frcmaven.wpi.edu/artifactory/release-2027" +version = "2027.0.0-alpha-2" + +extract_to = "src/native/romi" +libs = ["romiVendordep"] + +[[tool.hatch.build.hooks.nativelib.pcfile]] +pcfile = "src/native/romi/robotpy-native-romi.pc" +name = "romi" + +includedir = "src/native/romi/include" +libdir = "src/native/romi/lib" +shared_libraries = ["romiVendordep"] +requires = ["robotpy-native-wpilib"] diff --git a/romiVendordep/src/main/python/pyproject.toml b/romiVendordep/src/main/python/pyproject.toml new file mode 100644 index 0000000000..f35effbaad --- /dev/null +++ b/romiVendordep/src/main/python/pyproject.toml @@ -0,0 +1,55 @@ +[build-system] +build-backend = "hatchling.build" +requires = [ + "semiwrap~=0.1.7", + "hatch-meson~=0.1.0b2", + "hatch-robotpy~=0.2.1", + "hatchling", + "robotpy-native-romi==2027.0.0a2", + "wpilib==2027.0.0a2", +] + + +[project] +name = "robotpy-romi" +version = "2027.0.0a2" +description = "Binary wrapper for WPILib Romi Vendor library" +authors = [ + {name = "RobotPy Development Team", email = "robotpy@googlegroups.com"}, +] +license = "BSD-3-Clause" +dependencies = [ + "robotpy-native-romi==2027.0.0a2", + "wpilib==2027.0.0a2" +] + +[project.urls] +"Source code" = "https://github.com/robotpy/mostrobotpy" + + +[tool.hatch.build.hooks.robotpy] +version_file = "romi/version.py" + +[tool.hatch.build.hooks.semiwrap] + +[tool.hatch.build.hooks.meson] + +[tool.hatch.build.targets.wheel] +packages = ["romi"] + + +[tool.semiwrap] +update_init = ["romi"] + +[tool.semiwrap.extension_modules."romi._romi"] +name = "romi" +wraps = ["robotpy-native-romi"] +depends = [ + "wpilib", "wpimath_geometry" +] + +[tool.semiwrap.extension_modules."romi._romi".headers] +# frc/romi +OnBoardIO = "frc/romi/OnBoardIO.h" +RomiGyro = "frc/romi/RomiGyro.h" +RomiMotor = "frc/romi/RomiMotor.h" diff --git a/romiVendordep/src/main/python/romi/__init__.py b/romiVendordep/src/main/python/romi/__init__.py new file mode 100644 index 0000000000..be1981eb5f --- /dev/null +++ b/romiVendordep/src/main/python/romi/__init__.py @@ -0,0 +1,8 @@ +from . import _init__romi + +# autogenerated by 'semiwrap create-imports romi romi._romi' +from ._romi import OnBoardIO, RomiGyro, RomiMotor + +__all__ = ["OnBoardIO", "RomiGyro", "RomiMotor"] + +del _init__romi diff --git a/romiVendordep/src/main/python/romi/py.typed b/romiVendordep/src/main/python/romi/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/romiVendordep/src/main/python/romi/src/main.cpp b/romiVendordep/src/main/python/romi/src/main.cpp new file mode 100644 index 0000000000..9d713e6bc2 --- /dev/null +++ b/romiVendordep/src/main/python/romi/src/main.cpp @@ -0,0 +1,6 @@ + +#include + +SEMIWRAP_PYBIND11_MODULE(m) { + initWrapper(m); +} \ No newline at end of file diff --git a/romiVendordep/src/main/python/semiwrap/OnBoardIO.yml b/romiVendordep/src/main/python/semiwrap/OnBoardIO.yml new file mode 100644 index 0000000000..53fea27ab8 --- /dev/null +++ b/romiVendordep/src/main/python/semiwrap/OnBoardIO.yml @@ -0,0 +1,15 @@ +classes: + frc::OnBoardIO: + attributes: + kMessageInterval: + m_nextMessageTime: + enums: + ChannelMode: + methods: + OnBoardIO: + GetButtonAPressed: + GetButtonBPressed: + GetButtonCPressed: + SetGreenLed: + SetRedLed: + SetYellowLed: diff --git a/romiVendordep/src/main/python/semiwrap/RomiGyro.yml b/romiVendordep/src/main/python/semiwrap/RomiGyro.yml new file mode 100644 index 0000000000..4544bd5fd0 --- /dev/null +++ b/romiVendordep/src/main/python/semiwrap/RomiGyro.yml @@ -0,0 +1,13 @@ +classes: + frc::RomiGyro: + methods: + RomiGyro: + GetAngle: + GetRate: + GetRateX: + GetRateY: + GetRateZ: + GetAngleX: + GetAngleY: + GetAngleZ: + Reset: diff --git a/romiVendordep/src/main/python/semiwrap/RomiMotor.yml b/romiVendordep/src/main/python/semiwrap/RomiMotor.yml new file mode 100644 index 0000000000..b75fa2c5ad --- /dev/null +++ b/romiVendordep/src/main/python/semiwrap/RomiMotor.yml @@ -0,0 +1,4 @@ +classes: + frc::RomiMotor: + methods: + RomiMotor: diff --git a/romiVendordep/src/test/python/test_romi.py b/romiVendordep/src/test/python/test_romi.py new file mode 100644 index 0000000000..02b78f2a70 --- /dev/null +++ b/romiVendordep/src/test/python/test_romi.py @@ -0,0 +1,5 @@ +import romi + + +def test_romi(): + pass diff --git a/wpilibc/src/main/python/LICENSE.txt b/wpilibc/src/main/python/LICENSE.txt new file mode 100644 index 0000000000..82323225e9 --- /dev/null +++ b/wpilibc/src/main/python/LICENSE.txt @@ -0,0 +1,56 @@ + +RobotPy code license + +Copyright (c) 2014-2015 Peter Johnson +Copyright (c) 2014-2017 Dustin Spicuzza +Copyright (c) 2014-2017 Other contributors + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of RobotPy nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY ROBOTPY AND CONTRIBUTORS``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY NONINFRINGEMENT AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ROBOTPY OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Code for WPILib was derived from code using the following license: + +* Copyright (c) 2008-2015 FIRST +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the FIRST nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY FIRST AND CONTRIBUTORS``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY NONINFRINGEMENT AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FIRST OR CONTRIBUTORS BE LIABLE FOR +* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/wpilibc/src/main/python/README.md b/wpilibc/src/main/python/README.md new file mode 100644 index 0000000000..22a8387ea3 --- /dev/null +++ b/wpilibc/src/main/python/README.md @@ -0,0 +1,33 @@ +RobotPy WPILib +============== + +This repository contain a python implementation of wrappers for WPILib, +the library used to interface with hardware for the FIRST Robotics Competition. +Teams can use this library to write their robot code in Python, a powerful dynamic +programming language. + +**Note**: RobotPy is officially supported by FIRST. Please see the +[FAQ](https://robotpy.github.io/faq/) for more information. + +Documentation +============= + +All RobotPy documentation can be found at https://robotpy.readthedocs.io + +Installation +============ + +Installation instructions can be found in the [RobotPy documentation](https://robotpy.readthedocs.io/en/latest/getting_started.html) + + +License +======= + +See [LICENSE.txt](LICENSE.txt) + +Contributors +============ + +RobotPy is a community project, and many people have contributed over the years +to make it what it is today. RobotPy's history can be found on our +[documentation site](https://robotpy.readthedocs.io/en/stable/faq.html#who-created-robotpy). diff --git a/wpilibc/src/main/python/README.rst b/wpilibc/src/main/python/README.rst new file mode 100644 index 0000000000..76c5d84917 --- /dev/null +++ b/wpilibc/src/main/python/README.rst @@ -0,0 +1,12 @@ +RobotPy WPILib +============== + +Python version of WPILib - the standard library used for programming FRC +robots. + +Installation +============ + +Typically, you don't install this directly, but use the RobotPy installer +to install it on your roboRIO, or it is installed by pip as part of the +pyfrc setup process. diff --git a/wpilibc/src/main/python/native-pyproject.toml b/wpilibc/src/main/python/native-pyproject.toml new file mode 100644 index 0000000000..6bce95d48e --- /dev/null +++ b/wpilibc/src/main/python/native-pyproject.toml @@ -0,0 +1,47 @@ +[build-system] +build-backend = "hatchling.build" +requires = [ + "hatchling", + "hatch-nativelib~=0.2.0", + "hatch-robotpy~=0.2.1", + "robotpy-native-wpiutil==2027.0.0a2", + "robotpy-native-wpinet==2027.0.0a2", + "robotpy-native-ntcore==2027.0.0a2", + "robotpy-native-wpimath==2027.0.0a2", + "robotpy-native-wpihal==2027.0.0a2", +] + +[project] +name = "robotpy-native-wpilib" +version = "2027.0.0a2" +description = "WPILib Robotics Library" +license = "BSD-3-Clause" + +dependencies = [ + "robotpy-native-wpiutil==2027.0.0a2", + "robotpy-native-wpinet==2027.0.0a2", + "robotpy-native-ntcore==2027.0.0a2", + "robotpy-native-wpimath==2027.0.0a2", + "robotpy-native-wpihal==2027.0.0a2", +] + +[tool.hatch.build.targets.wheel] +packages = ["src/native"] + +[[tool.hatch.build.hooks.robotpy.maven_lib_download]] +artifact_id = "wpilibc-cpp" +group_id = "edu.wpi.first.wpilibc" +repo_url = "https://frcmaven.wpi.edu/artifactory/release-2027" +version = "2027.0.0-alpha-2" + +extract_to = "src/native/wpilib" +libs = ["wpilibc"] + +[[tool.hatch.build.hooks.nativelib.pcfile]] +pcfile = "src/native/wpilib/robotpy-native-wpilib.pc" +name = "wpilib" + +includedir = "src/native/wpilib/include" +libdir = "src/native/wpilib/lib" +shared_libraries = ["wpilibc"] +requires = ["robotpy-native-wpiutil", "robotpy-native-wpinet", "robotpy-native-ntcore", "robotpy-native-wpimath", "robotpy-native-wpihal"] diff --git a/wpilibc/src/main/python/pyproject.toml b/wpilibc/src/main/python/pyproject.toml new file mode 100644 index 0000000000..8942678be3 --- /dev/null +++ b/wpilibc/src/main/python/pyproject.toml @@ -0,0 +1,284 @@ +[build-system] +build-backend = "hatchling.build" +requires = [ + "semiwrap~=0.1.7", + "hatch-meson~=0.1.0b2", + "hatch-robotpy~=0.2.1", + "hatchling", + "robotpy-native-wpilib==2027.0.0a2", + "robotpy-wpiutil==2027.0.0a2", + "robotpy-wpimath==2027.0.0a2", + "robotpy-hal==2027.0.0a2", + "pyntcore==2027.0.0a2", +] + +[project] +name = "wpilib" +version = "2027.0.0a2" +description = "Binary wrapper for FRC WPILib" +authors = [ + {name = "RobotPy Development Team", email = "robotpy@googlegroups.com"}, +] +license = "BSD-3-Clause" +dependencies = [ + "robotpy-native-wpilib==2027.0.0a2", + "robotpy-wpiutil==2027.0.0a2", + "robotpy-wpimath==2027.0.0a2", + "robotpy-hal==2027.0.0a2", + "pyntcore==2027.0.0a2", + "robotpy-cli~=2024.0b" +] + +[project.urls] +"Source code" = "https://github.com/robotpy/mostrobotpy" + +[project.entry-points.robotpy] +run = "wpilib._impl.start:Main" + + +[tool.hatch.build.hooks.robotpy] +version_file = "wpilib/version.py" + +[tool.hatch.build.hooks.semiwrap] + +[tool.hatch.build.hooks.meson] + +[tool.hatch.build.targets.wheel] +packages = ["wpilib"] + + +[tool.semiwrap] +update_init = [ + "wpilib", + "wpilib.counter", + "wpilib.drive", + "wpilib.event", + "wpilib.interfaces wpilib._wpilib.interfaces", + "wpilib.simulation", + "wpilib.sysid wpilib._wpilib.sysid", +] +scan_headers_ignore = [ + "WPILibVersion.h", + + # needs a python wrapper + "frc/AsynchronousInterrupt.h", + + "frc/Filesystem.h", + "frc/Notifier.h", # wrapped separately + "frc/Resource.h", + "frc/ScopedTracer.h", # not useful for python + + "frc/motorcontrol/MotorControllerGroup.h", # wrapped separately + + "frc/smartdashboard/ListenerExecutor.h", # internal detail + + # Internals + "rpy/ControlWord.h", + "rpy/SmartDashboardData.h", +] + +[tool.semiwrap.extension_modules."wpilib._wpilib"] +name = "wpilib" +wraps = ["robotpy-native-wpilib"] +depends = [ + "wpihal", "wpiutil", "ntcore", + "wpimath", "wpimath_geometry", "wpimath_controls", + "wpilib_event" +] +includes = ["wpilib/src"] + +[tool.semiwrap.extension_modules."wpilib._wpilib".defines] +DYNAMIC_CAMERA_SERVER = 1 + +[tool.semiwrap.extension_modules."wpilib._wpilib".headers] +# frc +ADXL345_I2C = "frc/ADXL345_I2C.h" +AddressableLED = "frc/AddressableLED.h" +Alert = "frc/Alert.h" +AnalogAccelerometer = "frc/AnalogAccelerometer.h" +AnalogEncoder = "frc/AnalogEncoder.h" +AnalogGyro = "frc/AnalogGyro.h" +AnalogInput = "frc/AnalogInput.h" +AnalogPotentiometer = "frc/AnalogPotentiometer.h" +CAN = "frc/CAN.h" +Compressor = "frc/Compressor.h" +CompressorConfigType = "frc/CompressorConfigType.h" +DataLogManager = "frc/DataLogManager.h" +DSControlWord = "frc/DSControlWord.h" +DigitalInput = "frc/DigitalInput.h" +DigitalOutput = "frc/DigitalOutput.h" +DoubleSolenoid = "frc/DoubleSolenoid.h" +DriverStation = "frc/DriverStation.h" +DutyCycle = "frc/DutyCycle.h" +DutyCycleEncoder = "frc/DutyCycleEncoder.h" +Encoder = "frc/Encoder.h" +Errors = "frc/Errors.h" +Filesystem = "rpy/Filesystem.h" +I2C = "frc/I2C.h" +IterativeRobotBase = "frc/IterativeRobotBase.h" +Joystick = "frc/Joystick.h" +LEDPattern = "frc/LEDPattern.h" +MotorSafety = "frc/MotorSafety.h" +Notifier = "rpy/Notifier.h" +OnboardIMU = "frc/OnboardIMU.h" +PS4Controller = "frc/PS4Controller.h" +PS5Controller = "frc/PS5Controller.h" +PWM = "frc/PWM.h" +PneumaticHub = "frc/PneumaticHub.h" +PneumaticsBase = "frc/PneumaticsBase.h" +PneumaticsControlModule = "frc/PneumaticsControlModule.h" +PneumaticsModuleType = "frc/PneumaticsModuleType.h" +PowerDistribution = "frc/PowerDistribution.h" +Preferences = "frc/Preferences.h" +RobotBase = "frc/RobotBase.h" +RobotController = "frc/RobotController.h" +RobotState = "frc/RobotState.h" +RuntimeType = "frc/RuntimeType.h" +SensorUtil = "frc/SensorUtil.h" +SerialPort = "frc/SerialPort.h" +Servo = "frc/Servo.h" +SharpIR = "frc/SharpIR.h" +Solenoid = "frc/Solenoid.h" +StadiaController = "frc/StadiaController.h" +SystemServer = "frc/SystemServer.h" +Threads = "frc/Threads.h" +TimedRobot = "frc/TimedRobot.h" +Timer = "frc/Timer.h" +TimesliceRobot = "frc/TimesliceRobot.h" +Tracer = "frc/Tracer.h" +Watchdog = "frc/Watchdog.h" +XboxController = "frc/XboxController.h" + +# frc (interfaces) +CounterBase = "frc/CounterBase.h" +GenericHID = "frc/GenericHID.h" +MotorController = "frc/motorcontrol/MotorController.h" + +# frc/internal +DriverStationModeThread = "frc/internal/DriverStationModeThread.h" + +# frc/motorcontrol +DMC60 = "frc/motorcontrol/DMC60.h" +Jaguar = "frc/motorcontrol/Jaguar.h" +Koors40 = "frc/motorcontrol/Koors40.h" +MotorControllerGroup = "rpy/MotorControllerGroup.h" +PWMMotorController = "frc/motorcontrol/PWMMotorController.h" +PWMSparkFlex = "frc/motorcontrol/PWMSparkFlex.h" +PWMSparkMax = "frc/motorcontrol/PWMSparkMax.h" +PWMTalonFX = "frc/motorcontrol/PWMTalonFX.h" +PWMTalonSRX = "frc/motorcontrol/PWMTalonSRX.h" +PWMVenom = "frc/motorcontrol/PWMVenom.h" +PWMVictorSPX = "frc/motorcontrol/PWMVictorSPX.h" +SD540 = "frc/motorcontrol/SD540.h" +Spark = "frc/motorcontrol/Spark.h" +SparkMini = "frc/motorcontrol/SparkMini.h" +Talon = "frc/motorcontrol/Talon.h" +Victor = "frc/motorcontrol/Victor.h" +VictorSP = "frc/motorcontrol/VictorSP.h" + +# frc/smartdashboard +Field2d = "frc/smartdashboard/Field2d.h" +FieldObject2d = "frc/smartdashboard/FieldObject2d.h" +Mechanism2d = "frc/smartdashboard/Mechanism2d.h" +MechanismLigament2d = "frc/smartdashboard/MechanismLigament2d.h" +MechanismObject2d = "frc/smartdashboard/MechanismObject2d.h" +MechanismRoot2d = "frc/smartdashboard/MechanismRoot2d.h" +SendableBuilderImpl = "frc/smartdashboard/SendableBuilderImpl.h" +SendableChooser = "frc/smartdashboard/SendableChooser.h" +SendableChooserBase = "frc/smartdashboard/SendableChooserBase.h" +SmartDashboard = "frc/smartdashboard/SmartDashboard.h" + +# frc/sysid +SysIdRoutineLog = "frc/sysid/SysIdRoutineLog.h" + +# frc/util +Color = "frc/util/Color.h" +Color8Bit = "frc/util/Color8Bit.h" + + +[tool.semiwrap.extension_modules."wpilib.counter._counter"] +name = "wpilib_counter" +wraps = ["robotpy-native-wpilib"] +depends = ["wpilib"] +yaml_path = "semiwrap/counter" + +[tool.semiwrap.extension_modules."wpilib.counter._counter".headers] +# frc/counter +EdgeConfiguration = "frc/counter/EdgeConfiguration.h" +Tachometer = "frc/counter/Tachometer.h" +UpDownCounter = "frc/counter/UpDownCounter.h" + + +[tool.semiwrap.extension_modules."wpilib.drive._drive"] +name = "wpilib_drive" +wraps = ["robotpy-native-wpilib"] +depends = ["wpilib"] +yaml_path = "semiwrap/drive" + +[tool.semiwrap.extension_modules."wpilib.drive._drive".headers] +# frc/drive +DifferentialDrive = "frc/drive/DifferentialDrive.h" +MecanumDrive = "frc/drive/MecanumDrive.h" +RobotDriveBase = "frc/drive/RobotDriveBase.h" + + +[tool.semiwrap.extension_modules."wpilib.event._event"] +name = "wpilib_event" +wraps = ["robotpy-native-wpilib"] +# depends = ["wpilib", "wpimath_filter"] +depends = ["wpimath_filter"] +yaml_path = "semiwrap/event" + +[tool.semiwrap.extension_modules."wpilib.event._event".headers] +# frc/event +BooleanEvent = "frc/event/BooleanEvent.h" +EventLoop = "frc/event/EventLoop.h" +NetworkBooleanEvent = "frc/event/NetworkBooleanEvent.h" + + +[tool.semiwrap.extension_modules."wpilib.simulation._simulation"] +name = "wpilib_simulation" +wraps = ["robotpy-native-wpilib"] +depends = ["wpilib", "wpimath_controls", "wpimath_geometry", "wpimath_kinematics"] +yaml_path = "semiwrap/simulation" + +[tool.semiwrap.extension_modules."wpilib.simulation._simulation".headers] +# frc/simulation +ADXL345Sim = "frc/simulation/ADXL345Sim.h" +AddressableLEDSim = "frc/simulation/AddressableLEDSim.h" +AnalogEncoderSim = "frc/simulation/AnalogEncoderSim.h" +AnalogInputSim = "frc/simulation/AnalogInputSim.h" +BatterySim = "frc/simulation/BatterySim.h" +CTREPCMSim = "frc/simulation/CTREPCMSim.h" +CallbackStore = "frc/simulation/CallbackStore.h" +DCMotorSim = "frc/simulation/DCMotorSim.h" +DIOSim = "frc/simulation/DIOSim.h" +DifferentialDrivetrainSim = "frc/simulation/DifferentialDrivetrainSim.h" +DigitalPWMSim = "frc/simulation/DigitalPWMSim.h" +DoubleSolenoidSim = "frc/simulation/DoubleSolenoidSim.h" +DriverStationSim = "frc/simulation/DriverStationSim.h" +DutyCycleEncoderSim = "frc/simulation/DutyCycleEncoderSim.h" +DutyCycleSim = "frc/simulation/DutyCycleSim.h" +ElevatorSim = "frc/simulation/ElevatorSim.h" +EncoderSim = "frc/simulation/EncoderSim.h" +FlywheelSim = "frc/simulation/FlywheelSim.h" +GenericHIDSim = "frc/simulation/GenericHIDSim.h" +JoystickSim = "frc/simulation/JoystickSim.h" +LinearSystemSim = "frc/simulation/LinearSystemSim.h" +PS4ControllerSim = "frc/simulation/PS4ControllerSim.h" +PS5ControllerSim = "frc/simulation/PS5ControllerSim.h" +PWMSim = "frc/simulation/PWMSim.h" +PneumaticsBaseSim = "frc/simulation/PneumaticsBaseSim.h" +PowerDistributionSim = "frc/simulation/PowerDistributionSim.h" +PWMMotorControllerSim = "frc/simulation/PWMMotorControllerSim.h" +REVPHSim = "frc/simulation/REVPHSim.h" +RoboRioSim = "frc/simulation/RoboRioSim.h" +SendableChooserSim = "frc/simulation/SendableChooserSim.h" +ServoSim = "frc/simulation/ServoSim.h" +SharpIRSim = "frc/simulation/SharpIRSim.h" +SimDeviceSim = "frc/simulation/SimDeviceSim.h" +SimHooks = "frc/simulation/SimHooks.h" +SingleJointedArmSim = "frc/simulation/SingleJointedArmSim.h" +SolenoidSim = "frc/simulation/SolenoidSim.h" +StadiaControllerSim = "frc/simulation/StadiaControllerSim.h" +XboxControllerSim = "frc/simulation/XboxControllerSim.h" diff --git a/wpilibc/src/main/python/semiwrap/ADXL345_I2C.yml b/wpilibc/src/main/python/semiwrap/ADXL345_I2C.yml new file mode 100644 index 0000000000..b15ef94b4a --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/ADXL345_I2C.yml @@ -0,0 +1,30 @@ +extra_includes: +- networktables/NTSendableBuilder.h + +classes: + frc::ADXL345_I2C: + constants: + - frc::ADXL345_I2C::Range::kRange_2G + ignored_bases: + - wpi::SendableHelper + enums: + Axes: + Range: + attributes: + kAddress: + methods: + ADXL345_I2C: + GetI2CPort: + GetI2CDeviceAddress: + SetRange: + GetX: + GetY: + GetZ: + GetAcceleration: + GetAccelerations: + InitSendable: + frc::ADXL345_I2C::AllAxes: + attributes: + XAxis: + YAxis: + ZAxis: diff --git a/wpilibc/src/main/python/semiwrap/AddressableLED.yml b/wpilibc/src/main/python/semiwrap/AddressableLED.yml new file mode 100644 index 0000000000..9e9d798094 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/AddressableLED.yml @@ -0,0 +1,44 @@ +functions: + format_as: + ignore: true + +classes: + frc::AddressableLED: + methods: + AddressableLED: + SetLength: + SetData: + overloads: + std::span: + std::initializer_list: + ignore: true + SetColorOrder: + GetChannel: + SetStart: + GetStart: + SetGlobalData: + enums: + ColorOrder: + frc::AddressableLED::LEDData: + force_no_trampoline: true + base_qualnames: + HAL_AddressableLEDData: ::HAL_AddressableLEDData + methods: + LEDData: + overloads: + '': + int, int, int: + param_override: + _r: + name: r + _g: + name: g + _b: + name: b + SetRGB: + SetHSV: + SetLED: + overloads: + const Color&: + const Color8Bit&: + diff --git a/wpilibc/src/main/python/semiwrap/Alert.yml b/wpilibc/src/main/python/semiwrap/Alert.yml new file mode 100644 index 0000000000..c37f3ca12c --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Alert.yml @@ -0,0 +1,30 @@ +functions: + format_as: + ignore: true + +classes: + frc::Alert: + enums: + AlertType: + methods: + Alert: + overloads: + std::string_view, AlertType: + std::string_view, std::string_view, AlertType: + Set: + Get: + SetText: + GetText: + GetType: + inline_code: | + .def("close", [](frc::Alert &self) { + py::gil_scoped_release release; + self.Set(false); + }, py::doc("Disables the alert")) + .def("__enter__", [](frc::Alert &self) -> frc::Alert& { + return self; + }) + .def("__exit__", [](frc::Alert &self, py::args args) { + py::gil_scoped_release release; + self.Set(false); + }) diff --git a/wpilibc/src/main/python/semiwrap/AnalogAccelerometer.yml b/wpilibc/src/main/python/semiwrap/AnalogAccelerometer.yml new file mode 100644 index 0000000000..880c3d9818 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/AnalogAccelerometer.yml @@ -0,0 +1,18 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::AnalogAccelerometer: + ignored_bases: + - wpi::SendableHelper + methods: + AnalogAccelerometer: + overloads: + int: + AnalogInput*: + ignore: true + std::shared_ptr: + GetAcceleration: + SetSensitivity: + SetZero: + InitSendable: diff --git a/wpilibc/src/main/python/semiwrap/AnalogEncoder.yml b/wpilibc/src/main/python/semiwrap/AnalogEncoder.yml new file mode 100644 index 0000000000..c232227c4c --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/AnalogEncoder.yml @@ -0,0 +1,28 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h +- frc/AnalogInput.h + +classes: + frc::AnalogEncoder: + ignored_bases: + - wpi::SendableHelper + methods: + AnalogEncoder: + overloads: + int: + AnalogInput&: + ignore: true + AnalogInput*: + ignore: true + std::shared_ptr: + int, double, double: + AnalogInput&, double, double: + ignore: true + AnalogInput*, double, double: + ignore: true + std::shared_ptr, double, double: + Get: + GetChannel: + SetVoltagePercentageRange: + SetInverted: + InitSendable: diff --git a/wpilibc/src/main/python/semiwrap/AnalogGyro.yml b/wpilibc/src/main/python/semiwrap/AnalogGyro.yml new file mode 100644 index 0000000000..cee36dfc8f --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/AnalogGyro.yml @@ -0,0 +1,29 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h +- frc/AnalogInput.h + +classes: + frc::AnalogGyro: + ignored_bases: + - wpi::SendableHelper + methods: + AnalogGyro: + overloads: + int: + AnalogInput*: + ignore: true + std::shared_ptr: + int, int, double: + std::shared_ptr, int, double: + GetAngle: + GetRate: + GetCenter: + GetOffset: + SetSensitivity: + SetDeadband: + Reset: + InitGyro: + Calibrate: + GetRotation2d: + GetAnalogInput: + InitSendable: diff --git a/wpilibc/src/main/python/semiwrap/AnalogInput.yml b/wpilibc/src/main/python/semiwrap/AnalogInput.yml new file mode 100644 index 0000000000..fe5a4112a5 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/AnalogInput.yml @@ -0,0 +1,30 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::AnalogInput: + ignored_bases: + - wpi::SendableHelper + methods: + AnalogInput: + GetValue: + GetAverageValue: + GetVoltage: + GetAverageVoltage: + GetChannel: + SetAverageBits: + GetAverageBits: + SetOversampleBits: + GetOversampleBits: + GetLSBWeight: + GetOffset: + SetSampleRate: + GetSampleRate: + SetSimDevice: + InitSendable: + +inline_code: | + cls_AnalogInput + .def("__repr__", [](const AnalogInput &self) { + return py::str("").format(self.GetChannel()); + }); diff --git a/wpilibc/src/main/python/semiwrap/AnalogPotentiometer.yml b/wpilibc/src/main/python/semiwrap/AnalogPotentiometer.yml new file mode 100644 index 0000000000..620d21047e --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/AnalogPotentiometer.yml @@ -0,0 +1,16 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::AnalogPotentiometer: + ignored_bases: + - wpi::SendableHelper + methods: + AnalogPotentiometer: + overloads: + int, double, double: + AnalogInput*, double, double: + ignore: true + std::shared_ptr, double, double: + Get: + InitSendable: diff --git a/wpilibc/src/main/python/semiwrap/CAN.yml b/wpilibc/src/main/python/semiwrap/CAN.yml new file mode 100644 index 0000000000..da8247de31 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/CAN.yml @@ -0,0 +1,20 @@ +classes: + frc::CAN: + attributes: + kTeamManufacturer: + kTeamDeviceType: + methods: + CAN: + overloads: + int, int: + int, int, int, int: + WritePacket: + WritePacketNoError: + WritePacketRepeating: + WritePacketRepeatingNoError: + WriteRTRFrame: + WriteRTRFrameNoError: + StopPacketRepeating: + ReadPacketNew: + ReadPacketLatest: + ReadPacketTimeout: diff --git a/wpilibc/src/main/python/semiwrap/Color.yml b/wpilibc/src/main/python/semiwrap/Color.yml new file mode 100644 index 0000000000..57317be742 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Color.yml @@ -0,0 +1,191 @@ +extra_includes: +- pybind11/operators.h + +classes: + frc::Color: + force_type_casters: + - wpi::ct_string + attributes: + kDenim: + kFirstBlue: + kFirstRed: + kAliceBlue: + kAntiqueWhite: + kAqua: + kAquamarine: + kAzure: + kBeige: + kBisque: + kBlack: + kBlanchedAlmond: + kBlue: + kBlueViolet: + kBrown: + kBurlywood: + kCadetBlue: + kChartreuse: + kChocolate: + kCoral: + kCornflowerBlue: + kCornsilk: + kCrimson: + kCyan: + kDarkBlue: + kDarkCyan: + kDarkGoldenrod: + kDarkGray: + kDarkGreen: + kDarkKhaki: + kDarkMagenta: + kDarkOliveGreen: + kDarkOrange: + kDarkOrchid: + kDarkRed: + kDarkSalmon: + kDarkSeaGreen: + kDarkSlateBlue: + kDarkSlateGray: + kDarkTurquoise: + kDarkViolet: + kDeepPink: + kDeepSkyBlue: + kDimGray: + kDodgerBlue: + kFirebrick: + kFloralWhite: + kForestGreen: + kFuchsia: + kGainsboro: + kGhostWhite: + kGold: + kGoldenrod: + kGray: + kGreen: + kGreenYellow: + kHoneydew: + kHotPink: + kIndianRed: + kIndigo: + kIvory: + kKhaki: + kLavender: + kLavenderBlush: + kLawnGreen: + kLemonChiffon: + kLightBlue: + kLightCoral: + kLightCyan: + kLightGoldenrodYellow: + kLightGray: + kLightGreen: + kLightPink: + kLightSalmon: + kLightSeaGreen: + kLightSkyBlue: + kLightSlateGray: + kLightSteelBlue: + kLightYellow: + kLime: + kLimeGreen: + kLinen: + kMagenta: + kMaroon: + kMediumAquamarine: + kMediumBlue: + kMediumOrchid: + kMediumPurple: + kMediumSeaGreen: + kMediumSlateBlue: + kMediumSpringGreen: + kMediumTurquoise: + kMediumVioletRed: + kMidnightBlue: + kMintcream: + kMistyRose: + kMoccasin: + kNavajoWhite: + kNavy: + kOldLace: + kOlive: + kOliveDrab: + kOrange: + kOrangeRed: + kOrchid: + kPaleGoldenrod: + kPaleGreen: + kPaleTurquoise: + kPaleVioletRed: + kPapayaWhip: + kPeachPuff: + kPeru: + kPink: + kPlum: + kPowderBlue: + kPurple: + kRed: + kRosyBrown: + kRoyalBlue: + kSaddleBrown: + kSalmon: + kSandyBrown: + kSeaGreen: + kSeashell: + kSienna: + kSilver: + kSkyBlue: + kSlateBlue: + kSlateGray: + kSnow: + kSpringGreen: + kSteelBlue: + kTan: + kTeal: + kThistle: + kTomato: + kTurquoise: + kViolet: + kWheat: + kWhite: + kWhiteSmoke: + kYellow: + kYellowGreen: + red: + access: readonly + green: + access: readonly + blue: + access: readonly + methods: + Color: + overloads: + '': + double, double, double: + param_override: + r: + name: red + g: + name: green + b: + name: blue + int, int, int: + std::string_view: + FromHSV: + HexString: + operator==: + +inline_code: | + cls_Color + .def("__hash__", [](Color *self) -> size_t { + size_t h = (size_t)( + std::hash{}(self->red) + ^ (std::hash{}(self->green) << 1) + ^ (std::hash{}(self->blue) << 2) + ); + return h != -1 ? h : -2; + }) + .def("__repr__", [](Color *self) { + return "Color(" + "red=" + std::to_string(self->red) + ", " + "green=" + std::to_string(self->green) + ", " + "blue=" + std::to_string(self->blue) + ")"; + }); diff --git a/wpilibc/src/main/python/semiwrap/Color8Bit.yml b/wpilibc/src/main/python/semiwrap/Color8Bit.yml new file mode 100644 index 0000000000..1c29743cb6 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Color8Bit.yml @@ -0,0 +1,46 @@ +extra_includes: +- pybind11/operators.h + +classes: + frc::Color8Bit: + force_type_casters: + - wpi::ct_string + attributes: + red: + access: readonly + green: + access: readonly + blue: + access: readonly + methods: + Color8Bit: + overloads: + '': + int, int, int: + param_override: + r: + name: red + g: + name: green + b: + name: blue + const Color&: + std::string_view: + FromHexString: + HexString: + operator==: + +inline_code: | + cls_Color8Bit + .def("toColor", [](const Color8Bit &self) -> frc::Color { + return self; + }) + .def("__hash__", [](Color8Bit *self) -> size_t { + return (self->red) | (self->green << 8) | (self->blue << 16); + }) + .def("__repr__", [](Color8Bit *self) { + return "Color8Bit(" + "red=" + std::to_string(self->red) + ", " + "green=" + std::to_string(self->green) + ", " + "blue=" + std::to_string(self->blue) + ")"; + }); diff --git a/wpilibc/src/main/python/semiwrap/Compressor.yml b/wpilibc/src/main/python/semiwrap/Compressor.yml new file mode 100644 index 0000000000..5a2fab0e66 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Compressor.yml @@ -0,0 +1,26 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h +- frc/Compressor.h +- frc/Solenoid.h +- frc/DoubleSolenoid.h + +classes: + frc::Compressor: + ignored_bases: + - wpi::SendableHelper + methods: + Compressor: + overloads: + int, PneumaticsModuleType: + int, int, PneumaticsModuleType: + IsEnabled: + GetPressureSwitchValue: + GetCurrent: + GetAnalogVoltage: + GetPressure: + Disable: + EnableDigital: + EnableAnalog: + EnableHybrid: + GetConfigType: + InitSendable: diff --git a/wpilibc/src/main/python/semiwrap/CompressorConfigType.yml b/wpilibc/src/main/python/semiwrap/CompressorConfigType.yml new file mode 100644 index 0000000000..d55fc808b8 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/CompressorConfigType.yml @@ -0,0 +1,2 @@ +enums: + CompressorConfigType: diff --git a/wpilibc/src/main/python/semiwrap/CounterBase.yml b/wpilibc/src/main/python/semiwrap/CounterBase.yml new file mode 100644 index 0000000000..94929e183c --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/CounterBase.yml @@ -0,0 +1,15 @@ +defaults: + subpackage: interfaces + +classes: + frc::CounterBase: + enums: + EncodingType: + methods: + CounterBase: + Get: + Reset: + GetPeriod: + SetMaxPeriod: + GetStopped: + GetDirection: diff --git a/wpilibc/src/main/python/semiwrap/DMC60.yml b/wpilibc/src/main/python/semiwrap/DMC60.yml new file mode 100644 index 0000000000..4adbfcadc6 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/DMC60.yml @@ -0,0 +1,7 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::DMC60: + methods: + DMC60: diff --git a/wpilibc/src/main/python/semiwrap/DSControlWord.yml b/wpilibc/src/main/python/semiwrap/DSControlWord.yml new file mode 100644 index 0000000000..2b34b2abee --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/DSControlWord.yml @@ -0,0 +1,24 @@ +classes: + frc::DSControlWord: + methods: + DSControlWord: + IsEnabled: + no_release_gil: true + IsDisabled: + no_release_gil: true + IsEStopped: + no_release_gil: true + IsAutonomous: + no_release_gil: true + IsAutonomousEnabled: + no_release_gil: true + IsTeleop: + no_release_gil: true + IsTeleopEnabled: + no_release_gil: true + IsTest: + no_release_gil: true + IsDSAttached: + no_release_gil: true + IsFMSAttached: + no_release_gil: true diff --git a/wpilibc/src/main/python/semiwrap/DataLogManager.yml b/wpilibc/src/main/python/semiwrap/DataLogManager.yml new file mode 100644 index 0000000000..3987d7fc8f --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/DataLogManager.yml @@ -0,0 +1,14 @@ +extra_includes: +- wpi/datalog/DataLog.h + +classes: + frc::DataLogManager: + methods: + Start: + Stop: + Log: + GetLog: + return_value_policy: reference + GetLogDir: + LogNetworkTables: + LogConsoleOutput: diff --git a/wpilibc/src/main/python/semiwrap/DigitalInput.yml b/wpilibc/src/main/python/semiwrap/DigitalInput.yml new file mode 100644 index 0000000000..9d789fc405 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/DigitalInput.yml @@ -0,0 +1,13 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::DigitalInput: + ignored_bases: + - wpi::SendableHelper + methods: + DigitalInput: + Get: + GetChannel: + SetSimDevice: + InitSendable: diff --git a/wpilibc/src/main/python/semiwrap/DigitalOutput.yml b/wpilibc/src/main/python/semiwrap/DigitalOutput.yml new file mode 100644 index 0000000000..4a69d8ff5c --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/DigitalOutput.yml @@ -0,0 +1,21 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::DigitalOutput: + ignored_bases: + - wpi::SendableHelper + methods: + DigitalOutput: + Set: + Get: + GetChannel: + Pulse: + IsPulsing: + SetPWMRate: + EnablePPS: + EnablePWM: + DisablePWM: + UpdateDutyCycle: + SetSimDevice: + InitSendable: diff --git a/wpilibc/src/main/python/semiwrap/DoubleSolenoid.yml b/wpilibc/src/main/python/semiwrap/DoubleSolenoid.yml new file mode 100644 index 0000000000..07a2b608a9 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/DoubleSolenoid.yml @@ -0,0 +1,22 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::DoubleSolenoid: + ignored_bases: + - wpi::SendableHelper + enums: + Value: + methods: + DoubleSolenoid: + overloads: + int, PneumaticsModuleType, int, int: + int, int, PneumaticsModuleType, int, int: + Set: + Get: + Toggle: + GetFwdChannel: + GetRevChannel: + IsFwdSolenoidDisabled: + IsRevSolenoidDisabled: + InitSendable: diff --git a/wpilibc/src/main/python/semiwrap/DriverStation.yml b/wpilibc/src/main/python/semiwrap/DriverStation.yml new file mode 100644 index 0000000000..4dd5ee295d --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/DriverStation.yml @@ -0,0 +1,68 @@ +extra_includes: +- rpy/ControlWord.h +- wpi/datalog/DataLog.h + +classes: + frc::DriverStation: + attributes: + kJoystickPorts: + enums: + Alliance: + MatchType: + POVDirection: + methods: + GetStickButton: + GetStickButtonPressed: + GetStickButtonReleased: + GetStickAxis: + GetStickPOV: + GetStickButtons: + GetStickAxisCount: + GetStickPOVCount: + GetStickButtonCount: + GetJoystickType: + GetJoystickName: + GetJoystickAxisType: + IsJoystickConnected: + IsEnabled: + IsDisabled: + IsEStopped: + IsAutonomous: + IsAutonomousEnabled: + IsTeleop: + IsTeleopEnabled: + IsTest: + IsTestEnabled: + IsDSAttached: + IsFMSAttached: + GetGameSpecificMessage: + GetEventName: + GetMatchType: + GetMatchNumber: + GetReplayNumber: + GetAlliance: + GetLocation: + WaitForDsConnection: + GetMatchTime: + GetBatteryVoltage: + RefreshData: + ProvideRefreshedDataEventHandle: + RemoveRefreshedDataEventHandle: + SilenceJoystickConnectionWarning: + IsJoystickConnectionWarningSilenced: + StartDataLog: + GetAngle: + GetJoystickIsGamepad: + inline_code: | + .def("getControlState", + [](DriverStation *self) -> std::tuple { + py::gil_scoped_release release; + return rpy::GetControlState(); + }, + py::doc("More efficient way to determine what state the robot is in.\n" + "\n" + ":returns: booleans representing enabled, isautonomous, istest\n" + "\n" + ".. versionadded:: 2019.2.1\n" + "\n" + ".. note:: This function only exists in RobotPy\n")) diff --git a/wpilibc/src/main/python/semiwrap/DriverStationModeThread.yml b/wpilibc/src/main/python/semiwrap/DriverStationModeThread.yml new file mode 100644 index 0000000000..3fc8e68230 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/DriverStationModeThread.yml @@ -0,0 +1,9 @@ +classes: + frc::internal::DriverStationModeThread: + rename: _DriverStationModeThread + methods: + DriverStationModeThread: + InAutonomous: + InDisabled: + InTeleop: + InTest: diff --git a/wpilibc/src/main/python/semiwrap/DutyCycle.yml b/wpilibc/src/main/python/semiwrap/DutyCycle.yml new file mode 100644 index 0000000000..7150d4fdcb --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/DutyCycle.yml @@ -0,0 +1,26 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::DutyCycle: + ignored_bases: + - wpi::SendableHelper + methods: + DutyCycle: + overloads: + DigitalSource&: + ignore: true + DigitalSource*: + ignore: true + std::shared_ptr: + GetFrequency: + GetOutput: + GetHighTime: + GetSourceChannel: + InitSendable: + +inline_code: | + cls_DutyCycle + .def("__repr__", [](const DutyCycle &self) { + return py::str("").format(self.GetSourceChannel()); + }); diff --git a/wpilibc/src/main/python/semiwrap/DutyCycleEncoder.yml b/wpilibc/src/main/python/semiwrap/DutyCycleEncoder.yml new file mode 100644 index 0000000000..149985d8d3 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/DutyCycleEncoder.yml @@ -0,0 +1,38 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h +- frc/DutyCycle.h + +classes: + frc::DutyCycleEncoder: + ignored_bases: + - wpi::SendableHelper + methods: + DutyCycleEncoder: + overloads: + int: + DutyCycle&: + ignore: true + DutyCycle*: + ignore: true + std::shared_ptr: + int, double, double: + DutyCycle&, double, double: + ignore: true + DutyCycle*, double, double: + ignore: true + std::shared_ptr, double, double: + GetFrequency: + IsConnected: + SetConnectedFrequencyThreshold: + Get: + SetDutyCycleRange: + GetSourceChannel: + SetAssumedFrequency: + SetInverted: + InitSendable: + +inline_code: | + cls_DutyCycleEncoder + .def("__repr__", [](const DutyCycleEncoder &self) { + return py::str("").format(self.GetSourceChannel()); + }); diff --git a/wpilibc/src/main/python/semiwrap/Encoder.yml b/wpilibc/src/main/python/semiwrap/Encoder.yml new file mode 100644 index 0000000000..efce99290b --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Encoder.yml @@ -0,0 +1,43 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::Encoder: + ignored_bases: + - wpi::SendableHelper + typealias: + - frc::Encoder::EncodingType + methods: + Encoder: + overloads: + int, int, bool, EncodingType: + param_override: + encodingType: + default: frc::Encoder::EncodingType::k4X + DigitalSource*, DigitalSource*, bool, EncodingType: + ignore: true + DigitalSource&, DigitalSource&, bool, EncodingType: + ignore: true + std::shared_ptr, std::shared_ptr, bool, EncodingType: + param_override: + encodingType: + default: frc::Encoder::EncodingType::k4X + Get: + Reset: + GetPeriod: + SetMaxPeriod: + GetStopped: + GetDirection: + GetRaw: + GetEncodingScale: + GetDistance: + GetRate: + SetMinRate: + SetDistancePerPulse: + GetDistancePerPulse: + SetReverseDirection: + SetSamplesToAverage: + GetSamplesToAverage: + SetSimDevice: + GetFPGAIndex: + InitSendable: diff --git a/wpilibc/src/main/python/semiwrap/Errors.yml b/wpilibc/src/main/python/semiwrap/Errors.yml new file mode 100644 index 0000000000..d5750d5379 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Errors.yml @@ -0,0 +1,13 @@ +functions: + GetErrorMessage: + ReportErrorV: + ignore: true + ReportError: + ignore: true + MakeErrorV: + ignore: true + MakeError: + ignore: true +classes: + frc::RuntimeError: + ignore: true diff --git a/wpilibc/src/main/python/semiwrap/Field2d.yml b/wpilibc/src/main/python/semiwrap/Field2d.yml new file mode 100644 index 0000000000..51ef19b862 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Field2d.yml @@ -0,0 +1,19 @@ +extra_includes: +- networktables/NTSendableBuilder.h + +classes: + frc::Field2d: + ignored_bases: + - wpi::SendableHelper + methods: + Field2d: + SetRobotPose: + overloads: + const Pose2d&: + units::meter_t, units::meter_t, Rotation2d: + GetRobotPose: + GetObject: + return_value_policy: reference_internal + GetRobotObject: + return_value_policy: reference_internal + InitSendable: diff --git a/wpilibc/src/main/python/semiwrap/FieldObject2d.yml b/wpilibc/src/main/python/semiwrap/FieldObject2d.yml new file mode 100644 index 0000000000..1bf734ef9f --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/FieldObject2d.yml @@ -0,0 +1,25 @@ +extra_includes: +- frc/trajectory/Trajectory.h + +classes: + frc::FieldObject2d: + nodelete: true + methods: + FieldObject2d: + ignore: true + SetPose: + overloads: + const Pose2d&: + units::meter_t, units::meter_t, Rotation2d: + GetPose: + SetPoses: + overloads: + std::span: + std::initializer_list: + ignore: true + SetTrajectory: + GetPoses: + overloads: + '[const]': + wpi::SmallVectorImpl& [const]: + ignore: true diff --git a/wpilibc/src/main/python/semiwrap/Filesystem.yml b/wpilibc/src/main/python/semiwrap/Filesystem.yml new file mode 100644 index 0000000000..25a1e04761 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Filesystem.yml @@ -0,0 +1,7 @@ +functions: + GetOperatingDirectory: + GetDeployDirectory: + GetOperatingDirectoryFs: + ignore: true + GetDeployDirectoryFs: + ignore: true diff --git a/wpilibc/src/main/python/semiwrap/GenericHID.yml b/wpilibc/src/main/python/semiwrap/GenericHID.yml new file mode 100644 index 0000000000..a5dd696697 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/GenericHID.yml @@ -0,0 +1,54 @@ +defaults: + subpackage: interfaces + +extra_includes: +- frc/DriverStation.h +- frc/event/BooleanEvent.h + +classes: + frc::GenericHID: + enums: + RumbleType: + HIDType: + methods: + GenericHID: + GetRawButton: + GetRawButtonPressed: + GetRawButtonReleased: + Button: + GetRawAxis: + GetPOV: + POV: + overloads: + DriverStation::POVDirection, EventLoop* [const]: + int, DriverStation::POVDirection, EventLoop* [const]: + POVUp: + POVUpRight: + POVRight: + POVDownRight: + POVDown: + POVDownLeft: + POVLeft: + POVUpLeft: + POVCenter: + AxisLessThan: + AxisGreaterThan: + GetAxisCount: + GetPOVCount: + GetButtonCount: + IsConnected: + GetType: + GetName: + GetAxisType: + GetPort: + SetOutput: + SetOutputs: + SetRumble: + +inline_code: | + cls_GenericHID + .def("__repr__", [](py::handle self) { + py::object type_name = self.get_type().attr("__qualname__"); + int port = self.cast().GetPort(); + return py::str("<{} {}>").format(type_name, port); + }); diff --git a/wpilibc/src/main/python/semiwrap/I2C.yml b/wpilibc/src/main/python/semiwrap/I2C.yml new file mode 100644 index 0000000000..5ac39cbf54 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/I2C.yml @@ -0,0 +1,26 @@ +classes: + frc::I2C: + enums: + Port: + methods: + I2C: + GetPort: + GetDeviceAddress: + Transaction: + buffers: + - {type: IN, src: dataToSend, len: sendSize} + - {type: OUT, src: dataReceived, len: receiveSize} + AddressOnly: + Write: + WriteBulk: + buffers: + - {type: IN, src: data, len: count} + Read: + buffers: + - {type: OUT, src: data, len: count} + ReadOnly: + buffers: + - {type: OUT, src: buffer, len: count} + VerifySensor: + buffers: + - {type: IN, src: expected, len: count} diff --git a/wpilibc/src/main/python/semiwrap/IterativeRobotBase.yml b/wpilibc/src/main/python/semiwrap/IterativeRobotBase.yml new file mode 100644 index 0000000000..a1c5587c4f --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/IterativeRobotBase.yml @@ -0,0 +1,66 @@ +classes: + frc::IterativeRobotBase: + methods: + RobotInit: + DriverStationConnected: + DisabledInit: + AutonomousInit: + TeleopInit: + TestInit: + RobotPeriodic: + DisabledPeriodic: + AutonomousPeriodic: + TeleopPeriodic: + TestPeriodic: + IterativeRobotBase: + overloads: + double: + ignore: true + units::second_t: + LoopFunc: + SimulationInit: + internal: true + SimulationPeriodic: + internal: true + DisabledExit: + AutonomousExit: + TeleopExit: + TestExit: + SetNetworkTablesFlushEnabled: + GetPeriod: + PrintWatchdogEpochs: + doc: | + IterativeRobotBase implements a specific type of robot program framework, + extending the RobotBase class. + + The IterativeRobotBase class does not implement StartCompetition(), so it + should not be used by teams directly. + + This class provides the following functions which are called by the main + loop, StartCompetition(), at the appropriate times: + + RobotInit() -- provide for initialization at robot power-on + + Init() functions -- each of the following functions is called once when the + appropriate mode is entered: + + - DisabledInit() -- called each and every time disabled is entered from another mode + - AutonomousInit() -- called each and every time autonomous is entered from another mode + - TeleopInit() -- called each and every time teleop is entered from another mode + - TestInit() -- called each and every time test is entered from another mode + + Periodic() functions -- each of these functions is called on an interval: + + - RobotPeriodic() + - DisabledPeriodic() + - AutonomousPeriodic() + - TeleopPeriodic() + - TestPeriodic() + + Exit() functions -- each of the following functions is called once when the + appropriate mode is exited: + + - DisabledExit() -- called each and every time disabled is exited + - AutonomousExit() -- called each and every time autonomous is exited + - TeleopExit() -- called each and every time teleop is exited + - TestExit() -- called each and every time test is exited diff --git a/wpilibc/src/main/python/semiwrap/Jaguar.yml b/wpilibc/src/main/python/semiwrap/Jaguar.yml new file mode 100644 index 0000000000..20503e66c4 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Jaguar.yml @@ -0,0 +1,7 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::Jaguar: + methods: + Jaguar: diff --git a/wpilibc/src/main/python/semiwrap/Joystick.yml b/wpilibc/src/main/python/semiwrap/Joystick.yml new file mode 100644 index 0000000000..f4d586d49f --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Joystick.yml @@ -0,0 +1,46 @@ +extra_includes: +- frc/DriverStation.h + +classes: + frc::Joystick: + attributes: + kDefaultXChannel: + kDefaultYChannel: + kDefaultZChannel: + kDefaultTwistChannel: + kDefaultThrottleChannel: + enums: + AxisType: + ButtonType: + methods: + Joystick: + SetXChannel: + SetYChannel: + SetZChannel: + SetTwistChannel: + SetThrottleChannel: + GetXChannel: + GetYChannel: + GetZChannel: + GetTwistChannel: + GetThrottleChannel: + GetX: + GetY: + GetZ: + GetTwist: + GetThrottle: + GetTrigger: + GetTriggerPressed: + GetTriggerReleased: + Trigger: + GetTop: + GetTopPressed: + GetTopReleased: + Top: + GetMagnitude: + GetDirection: + rename: getDirectionRadians + inline_code: | + .def("getDirectionDegrees", [](const Joystick &self) -> units::degree_t { + return self.GetDirection(); + }) diff --git a/wpilibc/src/main/python/semiwrap/Koors40.yml b/wpilibc/src/main/python/semiwrap/Koors40.yml new file mode 100644 index 0000000000..edfff8c78c --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Koors40.yml @@ -0,0 +1,4 @@ +classes: + frc::Koors40: + methods: + Koors40: diff --git a/wpilibc/src/main/python/semiwrap/LEDPattern.yml b/wpilibc/src/main/python/semiwrap/LEDPattern.yml new file mode 100644 index 0000000000..0cff3e7d6a --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/LEDPattern.yml @@ -0,0 +1,44 @@ +classes: + frc::LEDPattern: + enums: + GradientType: + methods: + "Off": + LEDPattern: + ApplyTo: + overloads: + std::span [const]: + LEDReader, std::function [const]: + std::span, std::function [const]: + Reversed: + OffsetBy: + ScrollAtRelativeSpeed: + ScrollAtAbsoluteSpeed: + Blink: + overloads: + units::second_t, units::second_t: + units::second_t: + SynchronizedBlink: + Breathe: + OverlayOn: + Blend: + Mask: + AtBrightness: + Solid: + ProgressMaskLayer: + Steps: + overloads: + std::span>: + std::initializer_list>: + ignore: true + Gradient: + overloads: + GradientType, std::span: + GradientType, std::initializer_list: + ignore: true + Rainbow: + MapIndex: + frc::LEDPattern::LEDReader: + methods: + LEDReader: + size: diff --git a/wpilibc/src/main/python/semiwrap/Mechanism2d.yml b/wpilibc/src/main/python/semiwrap/Mechanism2d.yml new file mode 100644 index 0000000000..fc6268e177 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Mechanism2d.yml @@ -0,0 +1,13 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::Mechanism2d: + ignored_bases: + - wpi::SendableHelper + methods: + Mechanism2d: + GetRoot: + return_value_policy: reference_internal + SetBackgroundColor: + InitSendable: diff --git a/wpilibc/src/main/python/semiwrap/MechanismLigament2d.yml b/wpilibc/src/main/python/semiwrap/MechanismLigament2d.yml new file mode 100644 index 0000000000..6b63a58864 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/MechanismLigament2d.yml @@ -0,0 +1,14 @@ +classes: + frc::MechanismLigament2d: + methods: + MechanismLigament2d: + ignore: true + SetColor: + GetColor: + SetLength: + GetLength: + SetAngle: + GetAngle: + SetLineWeight: + GetLineWeight: + UpdateEntries: diff --git a/wpilibc/src/main/python/semiwrap/MechanismObject2d.yml b/wpilibc/src/main/python/semiwrap/MechanismObject2d.yml new file mode 100644 index 0000000000..a6b01a0ee3 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/MechanismObject2d.yml @@ -0,0 +1,31 @@ +extra_includes: +- frc/smartdashboard/MechanismLigament2d.h + +classes: + frc::MechanismObject2d: + force_type_casters: + - units::degree_t + attributes: + m_mutex: + ignore: true + methods: + GetName: + Append: + ignore: true + MechanismObject2d: + ignore: true + UpdateEntries: + +# keep this in sync with MechanismRoot2d.yml +inline_code: |- + cls_MechanismObject2d + .def("appendLigament", [](MechanismObject2d *self, + std::string_view name, double length, units::degree_t angle, + double lineWidth, const frc::Color8Bit& color) { + return self->Append(name, length, angle, lineWidth, color); + }, + py::arg("name"), py::arg("length"), py::arg("angle"), + py::arg("lineWidth") = 6, py::arg("color") = frc::Color8Bit{235, 137, 52}, + "Append a ligament node", + py::return_value_policy::reference_internal) + ; diff --git a/wpilibc/src/main/python/semiwrap/MechanismRoot2d.yml b/wpilibc/src/main/python/semiwrap/MechanismRoot2d.yml new file mode 100644 index 0000000000..29c5f68996 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/MechanismRoot2d.yml @@ -0,0 +1,30 @@ +extra_includes: +- frc/smartdashboard/MechanismLigament2d.h + +classes: + frc::MechanismRoot2d: + force_type_casters: + - units::degree_t + methods: + MechanismRoot2d: + ignore: true + SetPosition: + GetName: + ignore: true + Append: + ignore: true + +# keep this in sync with MechanismRoot2d.h +inline_code: |- + cls_MechanismRoot2d + .def("getName", [](MechanismRoot2d *self) { return self->GetName(); }, release_gil()) + .def("appendLigament", [](MechanismRoot2d *self, + std::string_view name, double length, units::degree_t angle, + double lineWidth, const frc::Color8Bit& color) { + return self->Append(name, length, angle, lineWidth, color); + }, + py::arg("name"), py::arg("length"), py::arg("angle"), + py::arg("lineWidth") = 6, py::arg("color") = frc::Color8Bit{235, 137, 52}, + release_gil(), "Append a ligament node", + py::return_value_policy::reference_internal) + ; diff --git a/wpilibc/src/main/python/semiwrap/MotorController.yml b/wpilibc/src/main/python/semiwrap/MotorController.yml new file mode 100644 index 0000000000..8249a2b37b --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/MotorController.yml @@ -0,0 +1,13 @@ +defaults: + subpackage: interfaces + +classes: + frc::MotorController: + methods: + Set: + SetVoltage: + Get: + SetInverted: + GetInverted: + Disable: + StopMotor: diff --git a/wpilibc/src/main/python/semiwrap/MotorControllerGroup.yml b/wpilibc/src/main/python/semiwrap/MotorControllerGroup.yml new file mode 100644 index 0000000000..5bf1971c58 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/MotorControllerGroup.yml @@ -0,0 +1,43 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h +- pybind11/stl.h + +classes: + frc::PyMotorControllerGroup: + rename: MotorControllerGroup + ignored_bases: + - wpi::SendableHelper + methods: + PyMotorControllerGroup: + cpp_code: | + [](py::args args) { + if (args.size() < 1) { + throw py::type_error("requires 1+ arguments"); + } + std::vector> v; + v.reserve(args.size()); + int i = 1; + for (auto &arg: args) { + try { + auto mc = arg.cast>(); + if (!mc) throw py::cast_error(); + v.push_back(mc); + } catch (py::cast_error &e) { + throw py::type_error(py::str("Argument {} must be a MotorController (got '{}')").format(i, py::repr(arg))); + } + i++; + } + return std::make_shared(std::move(v)); + } + keepalive: [] + param_override: + args: + ignore: true + Set: + SetVoltage: + Get: + SetInverted: + GetInverted: + Disable: + StopMotor: + InitSendable: diff --git a/wpilibc/src/main/python/semiwrap/MotorSafety.yml b/wpilibc/src/main/python/semiwrap/MotorSafety.yml new file mode 100644 index 0000000000..4b4ec84d50 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/MotorSafety.yml @@ -0,0 +1,17 @@ +extra_includes: +- wpi/SmallString.h + +classes: + frc::MotorSafety: + methods: + MotorSafety: + Feed: + SetExpiration: + GetExpiration: + IsAlive: + SetSafetyEnabled: + IsSafetyEnabled: + Check: + CheckMotors: + StopMotor: + GetDescription: diff --git a/wpilibc/src/main/python/semiwrap/Notifier.yml b/wpilibc/src/main/python/semiwrap/Notifier.yml new file mode 100644 index 0000000000..cfa1876b01 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Notifier.yml @@ -0,0 +1,21 @@ +classes: + frc::PyNotifier: + rename: Notifier + methods: + PyNotifier: + overloads: + std::function: + SetName: + SetCallback: + StartSingle: + overloads: + double: + ignore: true + units::second_t: + StartPeriodic: + overloads: + double: + ignore: true + units::second_t: + Stop: + SetHALThreadPriority: diff --git a/wpilibc/src/main/python/semiwrap/OnboardIMU.yml b/wpilibc/src/main/python/semiwrap/OnboardIMU.yml new file mode 100644 index 0000000000..97f70ec98c --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/OnboardIMU.yml @@ -0,0 +1,20 @@ +classes: + frc::OnboardIMU: + enums: + MountOrientation: + methods: + OnboardIMU: + GetYaw: + ResetYaw: + GetRotation2d: + GetRotation3d: + GetQuaternion: + GetAngleX: + GetAngleY: + GetAngleZ: + GetGyroRateX: + GetGyroRateY: + GetGyroRateZ: + GetAccelX: + GetAccelY: + GetAccelZ: diff --git a/wpilibc/src/main/python/semiwrap/PS4Controller.yml b/wpilibc/src/main/python/semiwrap/PS4Controller.yml new file mode 100644 index 0000000000..c116a26af3 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/PS4Controller.yml @@ -0,0 +1,100 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h +- frc/event/BooleanEvent.h + +classes: + frc::PS4Controller: + ignored_bases: + - wpi::SendableHelper + methods: + PS4Controller: + GetLeftX: + GetRightX: + GetLeftY: + GetRightY: + GetL2Axis: + GetR2Axis: + GetSquareButton: + GetSquareButtonPressed: + GetSquareButtonReleased: + Square: + GetCrossButton: + GetCrossButtonPressed: + GetCrossButtonReleased: + Cross: + GetCircleButton: + GetCircleButtonPressed: + GetCircleButtonReleased: + Circle: + GetTriangleButton: + GetTriangleButtonPressed: + GetTriangleButtonReleased: + Triangle: + GetL1Button: + GetL1ButtonPressed: + GetL1ButtonReleased: + L1: + GetR1Button: + GetR1ButtonPressed: + GetR1ButtonReleased: + R1: + GetL2Button: + GetL2ButtonPressed: + GetL2ButtonReleased: + L2: + GetR2Button: + GetR2ButtonPressed: + GetR2ButtonReleased: + R2: + GetShareButton: + GetShareButtonPressed: + GetShareButtonReleased: + Share: + GetOptionsButton: + GetOptionsButtonPressed: + GetOptionsButtonReleased: + Options: + GetL3Button: + GetL3ButtonPressed: + GetL3ButtonReleased: + L3: + GetR3Button: + GetR3ButtonPressed: + GetR3ButtonReleased: + R3: + GetPSButton: + GetPSButtonPressed: + GetPSButtonReleased: + PS: + GetTouchpad: + GetTouchpadPressed: + GetTouchpadReleased: + Touchpad: + GetTouchpadButton: + GetTouchpadButtonPressed: + GetTouchpadButtonReleased: + InitSendable: + frc::PS4Controller::Button: + attributes: + kSquare: + kCross: + kCircle: + kTriangle: + kL1: + kR1: + kL2: + kR2: + kShare: + kOptions: + kL3: + kR3: + kPS: + kTouchpad: + frc::PS4Controller::Axis: + attributes: + kLeftX: + kLeftY: + kRightX: + kRightY: + kL2: + kR2: diff --git a/wpilibc/src/main/python/semiwrap/PS5Controller.yml b/wpilibc/src/main/python/semiwrap/PS5Controller.yml new file mode 100644 index 0000000000..1ae505b01a --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/PS5Controller.yml @@ -0,0 +1,99 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::PS5Controller: + ignored_bases: + - wpi::SendableHelper + methods: + PS5Controller: + GetLeftX: + GetRightX: + GetLeftY: + GetRightY: + GetL2Axis: + GetR2Axis: + GetSquareButton: + GetSquareButtonPressed: + GetSquareButtonReleased: + Square: + GetCrossButton: + GetCrossButtonPressed: + GetCrossButtonReleased: + Cross: + GetCircleButton: + GetCircleButtonPressed: + GetCircleButtonReleased: + Circle: + GetTriangleButton: + GetTriangleButtonPressed: + GetTriangleButtonReleased: + Triangle: + GetL1Button: + GetL1ButtonPressed: + GetL1ButtonReleased: + L1: + GetR1Button: + GetR1ButtonPressed: + GetR1ButtonReleased: + R1: + GetL2Button: + GetL2ButtonPressed: + GetL2ButtonReleased: + L2: + GetR2Button: + GetR2ButtonPressed: + GetR2ButtonReleased: + R2: + GetCreateButton: + GetCreateButtonPressed: + GetCreateButtonReleased: + Create: + GetOptionsButton: + GetOptionsButtonPressed: + GetOptionsButtonReleased: + Options: + GetL3Button: + GetL3ButtonPressed: + GetL3ButtonReleased: + L3: + GetR3Button: + GetR3ButtonPressed: + GetR3ButtonReleased: + R3: + GetPSButton: + GetPSButtonPressed: + GetPSButtonReleased: + PS: + GetTouchpad: + GetTouchpadPressed: + GetTouchpadReleased: + Touchpad: + GetTouchpadButton: + GetTouchpadButtonPressed: + GetTouchpadButtonReleased: + InitSendable: + frc::PS5Controller::Button: + attributes: + kSquare: + kCross: + kCircle: + kTriangle: + kL1: + kR1: + kL2: + kR2: + kCreate: + kOptions: + kL3: + kR3: + kPS: + kTouchpad: + frc::PS5Controller::Axis: + attributes: + kLeftX: + kLeftY: + kRightX: + kRightY: + kL2: + kR2: diff --git a/wpilibc/src/main/python/semiwrap/PWM.yml b/wpilibc/src/main/python/semiwrap/PWM.yml new file mode 100644 index 0000000000..f176b66779 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/PWM.yml @@ -0,0 +1,28 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h +- frc/AddressableLED.h +- wpi/SmallString.h + +classes: + frc::PWM: + ignored_bases: + - wpi::SendableHelper + enums: + OutputPeriod: + methods: + PWM: + SetPulseTime: + GetPulseTime: + SetDisabled: + GetChannel: + InitSendable: + SetOutputPeriod: + SetSimDevice: + +inline_code: | + cls_PWM + .def("__repr__", [](py::handle self) { + py::object type_name = self.get_type().attr("__qualname__"); + int channel = self.cast().GetChannel(); + return py::str("<{} {}>").format(type_name, channel); + }); diff --git a/wpilibc/src/main/python/semiwrap/PWMMotorController.yml b/wpilibc/src/main/python/semiwrap/PWMMotorController.yml new file mode 100644 index 0000000000..47b0f9e6fd --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/PWMMotorController.yml @@ -0,0 +1,33 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::PWMMotorController: + ignored_bases: + - wpi::SendableHelper + attributes: + m_pwm: + methods: + Set: + SetVoltage: + Get: + GetVoltage: + SetInverted: + GetInverted: + Disable: + StopMotor: + GetDescription: + GetChannel: + EnableDeadbandElimination: + AddFollower: + overloads: + PWMMotorController&: + keepalive: + - [1, 2] + T&&: + ignore: true + PWMMotorController: + InitSendable: + SetSpeed: + GetSpeed: + SetBounds: diff --git a/wpilibc/src/main/python/semiwrap/PWMSparkFlex.yml b/wpilibc/src/main/python/semiwrap/PWMSparkFlex.yml new file mode 100644 index 0000000000..631385f360 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/PWMSparkFlex.yml @@ -0,0 +1,4 @@ +classes: + frc::PWMSparkFlex: + methods: + PWMSparkFlex: diff --git a/wpilibc/src/main/python/semiwrap/PWMSparkMax.yml b/wpilibc/src/main/python/semiwrap/PWMSparkMax.yml new file mode 100644 index 0000000000..3c13a9d36a --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/PWMSparkMax.yml @@ -0,0 +1,7 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::PWMSparkMax: + methods: + PWMSparkMax: diff --git a/wpilibc/src/main/python/semiwrap/PWMTalonFX.yml b/wpilibc/src/main/python/semiwrap/PWMTalonFX.yml new file mode 100644 index 0000000000..3c05e86f81 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/PWMTalonFX.yml @@ -0,0 +1,7 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::PWMTalonFX: + methods: + PWMTalonFX: diff --git a/wpilibc/src/main/python/semiwrap/PWMTalonSRX.yml b/wpilibc/src/main/python/semiwrap/PWMTalonSRX.yml new file mode 100644 index 0000000000..6e979fd273 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/PWMTalonSRX.yml @@ -0,0 +1,7 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::PWMTalonSRX: + methods: + PWMTalonSRX: diff --git a/wpilibc/src/main/python/semiwrap/PWMVenom.yml b/wpilibc/src/main/python/semiwrap/PWMVenom.yml new file mode 100644 index 0000000000..522f244565 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/PWMVenom.yml @@ -0,0 +1,7 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::PWMVenom: + methods: + PWMVenom: diff --git a/wpilibc/src/main/python/semiwrap/PWMVictorSPX.yml b/wpilibc/src/main/python/semiwrap/PWMVictorSPX.yml new file mode 100644 index 0000000000..f7bcedd1dc --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/PWMVictorSPX.yml @@ -0,0 +1,7 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::PWMVictorSPX: + methods: + PWMVictorSPX: diff --git a/wpilibc/src/main/python/semiwrap/PneumaticHub.yml b/wpilibc/src/main/python/semiwrap/PneumaticHub.yml new file mode 100644 index 0000000000..00c7848f75 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/PneumaticHub.yml @@ -0,0 +1,91 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h +- frc/Compressor.h +- frc/Solenoid.h +- frc/DoubleSolenoid.h + +classes: + frc::PneumaticHub: + methods: + PneumaticHub: + overloads: + int: + int, int: + GetCompressor: + DisableCompressor: + EnableCompressorDigital: + EnableCompressorAnalog: + EnableCompressorHybrid: + GetCompressorConfigType: + GetPressureSwitch: + GetCompressorCurrent: + SetSolenoids: + GetSolenoids: + GetModuleNumber: + GetSolenoidDisabledList: + FireOneShot: + SetOneShotDuration: + CheckSolenoidChannel: + CheckAndReserveSolenoids: + UnreserveSolenoids: + ReserveCompressor: + UnreserveCompressor: + MakeSolenoid: + MakeDoubleSolenoid: + MakeCompressor: + GetVersion: + GetFaults: + GetStickyFaults: + ClearStickyFaults: + GetInputVoltage: + Get5VRegulatedVoltage: + GetSolenoidsTotalCurrent: + GetSolenoidsVoltage: + GetAnalogVoltage: + GetPressure: + ReportUsage: + frc::PneumaticHub::Version: + attributes: + FirmwareMajor: + FirmwareMinor: + FirmwareFix: + HardwareMinor: + HardwareMajor: + UniqueId: + frc::PneumaticHub::Faults: + attributes: + Channel0Fault: + Channel1Fault: + Channel2Fault: + Channel3Fault: + Channel4Fault: + Channel5Fault: + Channel6Fault: + Channel7Fault: + Channel8Fault: + Channel9Fault: + Channel10Fault: + Channel11Fault: + Channel12Fault: + Channel13Fault: + Channel14Fault: + Channel15Fault: + CompressorOverCurrent: + CompressorOpen: + SolenoidOverCurrent: + Brownout: + CanWarning: + HardwareFault: + methods: + GetChannelFault: + frc::PneumaticHub::StickyFaults: + attributes: + CompressorOverCurrent: + CompressorOpen: + SolenoidOverCurrent: + Brownout: + CanWarning: + CanBusOff: + HasReset: + HardwareFault: + FirmwareFault: diff --git a/wpilibc/src/main/python/semiwrap/PneumaticsBase.yml b/wpilibc/src/main/python/semiwrap/PneumaticsBase.yml new file mode 100644 index 0000000000..747ab959f2 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/PneumaticsBase.yml @@ -0,0 +1,37 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h +- frc/Compressor.h +- frc/Solenoid.h +- frc/DoubleSolenoid.h + + +classes: + frc::PneumaticsBase: + methods: + GetCompressor: + GetPressureSwitch: + GetCompressorCurrent: + DisableCompressor: + EnableCompressorDigital: + EnableCompressorAnalog: + EnableCompressorHybrid: + GetCompressorConfigType: + SetSolenoids: + GetSolenoids: + GetModuleNumber: + GetSolenoidDisabledList: + FireOneShot: + SetOneShotDuration: + CheckSolenoidChannel: + CheckAndReserveSolenoids: + UnreserveSolenoids: + ReserveCompressor: + UnreserveCompressor: + GetAnalogVoltage: + GetPressure: + MakeSolenoid: + MakeDoubleSolenoid: + MakeCompressor: + GetForType: + GetDefaultForType: + ReportUsage: diff --git a/wpilibc/src/main/python/semiwrap/PneumaticsControlModule.yml b/wpilibc/src/main/python/semiwrap/PneumaticsControlModule.yml new file mode 100644 index 0000000000..1d4edc1d30 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/PneumaticsControlModule.yml @@ -0,0 +1,47 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h +- frc/Compressor.h +- frc/Solenoid.h +- frc/DoubleSolenoid.h + +classes: + frc::PneumaticsControlModule: + methods: + PneumaticsControlModule: + overloads: + int: + int, int: + GetCompressor: + DisableCompressor: + EnableCompressorDigital: + EnableCompressorAnalog: + EnableCompressorHybrid: + GetCompressorConfigType: + GetPressureSwitch: + GetCompressorCurrent: + GetCompressorCurrentTooHighFault: + GetCompressorCurrentTooHighStickyFault: + GetCompressorShortedFault: + GetCompressorShortedStickyFault: + GetCompressorNotConnectedFault: + GetCompressorNotConnectedStickyFault: + GetSolenoidVoltageFault: + GetSolenoidVoltageStickyFault: + ClearAllStickyFaults: + SetSolenoids: + GetSolenoids: + GetModuleNumber: + GetSolenoidDisabledList: + FireOneShot: + SetOneShotDuration: + CheckSolenoidChannel: + CheckAndReserveSolenoids: + UnreserveSolenoids: + ReserveCompressor: + UnreserveCompressor: + GetAnalogVoltage: + GetPressure: + MakeSolenoid: + MakeDoubleSolenoid: + MakeCompressor: + ReportUsage: diff --git a/wpilibc/src/main/python/semiwrap/PneumaticsModuleType.yml b/wpilibc/src/main/python/semiwrap/PneumaticsModuleType.yml new file mode 100644 index 0000000000..2903eeeaa4 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/PneumaticsModuleType.yml @@ -0,0 +1,2 @@ +enums: + PneumaticsModuleType: diff --git a/wpilibc/src/main/python/semiwrap/PowerDistribution.yml b/wpilibc/src/main/python/semiwrap/PowerDistribution.yml new file mode 100644 index 0000000000..e4773df8ed --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/PowerDistribution.yml @@ -0,0 +1,107 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::PowerDistribution: + ignored_bases: + - wpi::SendableHelper + attributes: + kDefaultModule: + enums: + ModuleType: + methods: + PowerDistribution: + overloads: + int: + int, int, ModuleType: + GetVoltage: + GetTemperature: + GetCurrent: + GetTotalCurrent: + GetTotalPower: + GetTotalEnergy: + ResetTotalEnergy: + ClearStickyFaults: + GetModule: + GetType: + GetSwitchableChannel: + SetSwitchableChannel: + GetVersion: + GetFaults: + GetStickyFaults: + GetNumChannels: + GetAllCurrents: + InitSendable: + frc::PowerDistribution::Version: + attributes: + FirmwareMajor: + FirmwareMinor: + FirmwareFix: + HardwareMinor: + HardwareMajor: + UniqueId: + frc::PowerDistribution::Faults: + attributes: + Channel0BreakerFault: + Channel1BreakerFault: + Channel2BreakerFault: + Channel3BreakerFault: + Channel4BreakerFault: + Channel5BreakerFault: + Channel6BreakerFault: + Channel7BreakerFault: + Channel8BreakerFault: + Channel9BreakerFault: + Channel10BreakerFault: + Channel11BreakerFault: + Channel12BreakerFault: + Channel13BreakerFault: + Channel14BreakerFault: + Channel15BreakerFault: + Channel16BreakerFault: + Channel17BreakerFault: + Channel18BreakerFault: + Channel19BreakerFault: + Channel20BreakerFault: + Channel21BreakerFault: + Channel22BreakerFault: + Channel23BreakerFault: + Brownout: + CanWarning: + HardwareFault: + methods: + GetBreakerFault: + frc::PowerDistribution::StickyFaults: + attributes: + Channel0BreakerFault: + Channel1BreakerFault: + Channel2BreakerFault: + Channel3BreakerFault: + Channel4BreakerFault: + Channel5BreakerFault: + Channel6BreakerFault: + Channel7BreakerFault: + Channel8BreakerFault: + Channel9BreakerFault: + Channel10BreakerFault: + Channel11BreakerFault: + Channel12BreakerFault: + Channel13BreakerFault: + Channel14BreakerFault: + Channel15BreakerFault: + Channel16BreakerFault: + Channel17BreakerFault: + Channel18BreakerFault: + Channel19BreakerFault: + Channel20BreakerFault: + Channel21BreakerFault: + Channel22BreakerFault: + Channel23BreakerFault: + Brownout: + CanWarning: + CanBusOff: + HasReset: + HardwareFault: + FirmwareFault: + methods: + GetBreakerFault: diff --git a/wpilibc/src/main/python/semiwrap/Preferences.yml b/wpilibc/src/main/python/semiwrap/Preferences.yml new file mode 100644 index 0000000000..7d8bd60574 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Preferences.yml @@ -0,0 +1,25 @@ +classes: + frc::Preferences: + methods: + GetKeys: + GetString: + GetInt: + GetDouble: + GetFloat: + GetBoolean: + GetLong: + SetString: + InitString: + SetInt: + InitInt: + SetDouble: + InitDouble: + SetFloat: + InitFloat: + SetBoolean: + InitBoolean: + SetLong: + InitLong: + ContainsKey: + Remove: + RemoveAll: diff --git a/wpilibc/src/main/python/semiwrap/RobotBase.yml b/wpilibc/src/main/python/semiwrap/RobotBase.yml new file mode 100644 index 0000000000..07c5470541 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/RobotBase.yml @@ -0,0 +1,62 @@ +extra_includes: +- frc/DriverStation.h +- rpy/ControlWord.h + +functions: + # TODO + RunHALInitialization: + ignore: true + RunRobot: + ignore: true + StartRobot: + ignore: true + ResetMotorSafety: + ignore: true +classes: + frc::RobotBase: + attributes: + m_threadId: + ignore: true + connListenerHandle: + ignore: true + methods: + IsEnabled: + IsDisabled: + IsAutonomous: + IsAutonomousEnabled: + IsTeleop: + IsTeleopEnabled: + IsTest: + IsTestEnabled: + GetThreadId: + ignore: true + StartCompetition: + EndCompetition: + GetRuntimeType: + IsReal: + IsSimulation: + RobotBase: + inline_code: | + .def_static("main", + [](py::object robot_cls) -> py::object { + auto start = py::module::import("wpilib._impl.start"); + auto starter = start.attr("RobotStarter")(); + return starter.attr("run")(robot_cls); + }, + py::arg("robot_cls"), py::doc("Starting point for the application")) + .def( + "getControlState", + [](RobotBase *self) -> std::tuple { + py::gil_scoped_release release; + return rpy::GetControlState(); + }, + py::doc("More efficient way to determine what state the robot is in.\n" + "\n" + ":returns: booleans representing enabled, isautonomous, istest\n" + "\n" + ".. versionadded:: 2019.2.1\n" + "\n" + ".. note:: This function only exists in RobotPy\n")); + + auto logger = py::module::import("logging").attr("getLogger")("robot"); + cls_RobotBase.attr("logger") = logger; diff --git a/wpilibc/src/main/python/semiwrap/RobotController.yml b/wpilibc/src/main/python/semiwrap/RobotController.yml new file mode 100644 index 0000000000..22ce23a2bd --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/RobotController.yml @@ -0,0 +1,36 @@ +classes: + frc::CANStatus: + attributes: + percentBusUtilization: + busOffCount: + txFullCount: + receiveErrorCount: + transmitErrorCount: + frc::RobotController: + nodelete: true + methods: + GetFPGAVersion: + GetFPGARevision: + GetSerialNumber: + GetComments: + GetTeamNumber: + SetTimeSource: + GetTime: + GetFPGATime: + IsSysActive: + IsBrownedOut: + GetRSLState: + IsSystemTimeValid: + GetInputVoltage: + GetVoltage3V3: + GetCurrent3V3: + SetEnabled3V3: + GetEnabled3V3: + GetFaultCount3V3: + GetBatteryVoltage: + GetBrownoutVoltage: + SetBrownoutVoltage: + GetCPUTemp: + GetCANStatus: + GetCommsDisableCount: + ResetRailFaultCounts: diff --git a/wpilibc/src/main/python/semiwrap/RobotState.yml b/wpilibc/src/main/python/semiwrap/RobotState.yml new file mode 100644 index 0000000000..63c29f64e8 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/RobotState.yml @@ -0,0 +1,10 @@ +classes: + frc::RobotState: + nodelete: true + methods: + IsDisabled: + IsEnabled: + IsEStopped: + IsTeleop: + IsAutonomous: + IsTest: diff --git a/wpilibc/src/main/python/semiwrap/RuntimeType.yml b/wpilibc/src/main/python/semiwrap/RuntimeType.yml new file mode 100644 index 0000000000..f80ffbe3fe --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/RuntimeType.yml @@ -0,0 +1,2 @@ +enums: + RuntimeType: diff --git a/wpilibc/src/main/python/semiwrap/SD540.yml b/wpilibc/src/main/python/semiwrap/SD540.yml new file mode 100644 index 0000000000..756428a73d --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/SD540.yml @@ -0,0 +1,7 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::SD540: + methods: + SD540: diff --git a/wpilibc/src/main/python/semiwrap/SendableBuilderImpl.yml b/wpilibc/src/main/python/semiwrap/SendableBuilderImpl.yml new file mode 100644 index 0000000000..c4cee26d95 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/SendableBuilderImpl.yml @@ -0,0 +1,49 @@ +classes: + frc::SendableBuilderImpl: + methods: + SendableBuilderImpl: + SetTable: + GetTable: + IsPublished: + IsActuator: + Update: + StartListeners: + StopListeners: + ClearProperties: + SetSmartDashboardType: + SetActuator: + SetUpdateTable: + cpp_code: | + [](SendableBuilderImpl *self, std::function func) { + self->SetUpdateTable(std::move(func)); + } + GetTopic: + AddBooleanProperty: + PublishConstBoolean: + AddIntegerProperty: + PublishConstInteger: + AddFloatProperty: + PublishConstFloat: + AddDoubleProperty: + PublishConstDouble: + AddStringProperty: + PublishConstString: + AddBooleanArrayProperty: + PublishConstBooleanArray: + AddIntegerArrayProperty: + PublishConstIntegerArray: + AddFloatArrayProperty: + PublishConstFloatArray: + AddDoubleArrayProperty: + PublishConstDoubleArray: + AddStringArrayProperty: + PublishConstStringArray: + AddRawProperty: + PublishConstRaw: + AddSmallStringProperty: + AddSmallBooleanArrayProperty: + AddSmallIntegerArrayProperty: + AddSmallFloatArrayProperty: + AddSmallDoubleArrayProperty: + AddSmallStringArrayProperty: + AddSmallRawProperty: diff --git a/wpilibc/src/main/python/semiwrap/SendableChooser.yml b/wpilibc/src/main/python/semiwrap/SendableChooser.yml new file mode 100644 index 0000000000..68947a3c31 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/SendableChooser.yml @@ -0,0 +1,42 @@ +extra_includes: +- gilsafe_object.h + +classes: + frc::SendableChooser: + template_params: + - T + methods: + SendableChooser: + AddOption: + SetDefaultOption: + GetSelected: + # weirdness because return type + cpp_code: | + [](frc::SendableChooser * __that) -> py::object { + auto v = __that->GetSelected(); + if (!v) { + return py::none(); + } + return v; + } + OnChange: + # more weirdness + cpp_code: | + [](frc::SendableChooser *self, std::function fn) { + self->OnChange([fn](T v) { + py::gil_scoped_acquire lock; + if (v) { + fn(v); + } else { + fn(py::none()); + } + }); + } + + InitSendable: + +templates: + SendableChooser: + qualname: frc::SendableChooser + params: + - semiwrap::gilsafe_object diff --git a/wpilibc/src/main/python/semiwrap/SendableChooserBase.yml b/wpilibc/src/main/python/semiwrap/SendableChooserBase.yml new file mode 100644 index 0000000000..e02ba1659d --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/SendableChooserBase.yml @@ -0,0 +1,25 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::SendableChooserBase: + ignored_bases: + - wpi::SendableHelper + attributes: + kDefault: + kOptions: + kSelected: + kActive: + kInstance: + m_defaultChoice: + m_selected: + m_haveSelected: + m_mutex: + ignore: true + m_instance: + m_previousVal: + s_instances: + ignore: true + methods: + SendableChooserBase: + ignore: true diff --git a/wpilibc/src/main/python/semiwrap/SensorUtil.yml b/wpilibc/src/main/python/semiwrap/SensorUtil.yml new file mode 100644 index 0000000000..1de1a6eb02 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/SensorUtil.yml @@ -0,0 +1,12 @@ +classes: + frc::SensorUtil: + nodelete: true + methods: + CheckDigitalChannel: + CheckPWMChannel: + CheckAnalogInputChannel: + GetDefaultCTREPCMModule: + GetDefaultREVPHModule: + GetNumDigitalChannels: + GetNumAnalogInputs: + GetNumPwmChannels: diff --git a/wpilibc/src/main/python/semiwrap/SerialPort.yml b/wpilibc/src/main/python/semiwrap/SerialPort.yml new file mode 100644 index 0000000000..0bb859adb0 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/SerialPort.yml @@ -0,0 +1,50 @@ +classes: + frc::SerialPort: + enums: + Parity: + StopBits: + FlowControl: + WriteBufferMode: + Port: + methods: + SerialPort: + overloads: + int, Port, int, Parity, StopBits: + param_override: + port: + default: frc::SerialPort::Port::kOnboard + parity: + default: frc::SerialPort::Parity::kParity_None + stopBits: + default: frc::SerialPort::StopBits::kStopBits_One + int, std::string_view, Port, int, Parity, StopBits: + param_override: + port: + default: frc::SerialPort::Port::kOnboard + parity: + default: frc::SerialPort::Parity::kParity_None + stopBits: + default: frc::SerialPort::StopBits::kStopBits_One + SetFlowControl: + EnableTermination: + param_override: + terminator: + default: "'\\n'" + DisableTermination: + GetBytesReceived: + Read: + buffers: + - {type: OUT, src: buffer, len: count} + Write: + overloads: + const char*, int: + buffers: + - {type: IN, src: buffer, len: count} + std::string_view: + ignore: true + SetTimeout: + SetReadBufferSize: + SetWriteBufferSize: + SetWriteBufferMode: + Flush: + Reset: diff --git a/wpilibc/src/main/python/semiwrap/Servo.yml b/wpilibc/src/main/python/semiwrap/Servo.yml new file mode 100644 index 0000000000..93b46aac50 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Servo.yml @@ -0,0 +1,15 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::Servo: + ignored_bases: + - wpi::SendableHelper + methods: + Servo: + Set: + Get: + SetAngle: + GetAngle: + InitSendable: + GetChannel: diff --git a/wpilibc/src/main/python/semiwrap/SharpIR.yml b/wpilibc/src/main/python/semiwrap/SharpIR.yml new file mode 100644 index 0000000000..68c7990772 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/SharpIR.yml @@ -0,0 +1,16 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::SharpIR: + ignored_bases: + - wpi::SendableHelper + methods: + GP2Y0A02YK0F: + GP2Y0A21YK0F: + GP2Y0A41SK0F: + GP2Y0A51SK0F: + SharpIR: + GetChannel: + GetRange: + InitSendable: diff --git a/wpilibc/src/main/python/semiwrap/SmartDashboard.yml b/wpilibc/src/main/python/semiwrap/SmartDashboard.yml new file mode 100644 index 0000000000..568d4df1a3 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/SmartDashboard.yml @@ -0,0 +1,171 @@ +extra_includes: +- src/rpy/SmartDashboardData.h +- frc/Errors.h +- wpi/sendable/SendableRegistry.h + +classes: + frc::SmartDashboard: + methods: + init: + ContainsKey: + GetKeys: + SetPersistent: + ClearPersistent: + IsPersistent: + GetEntry: + PutData: + # overrides ensure data doesn't die if this is the only reference + overloads: + std::string_view, wpi::Sendable*: + cpp_code: | + [](py::str &key, std::shared_ptr data) { + if (!data) { + throw FRC_MakeError(err::NullParameter, "{}", "value"); + } + + // convert key to a raw string so that we can create a StringRef + Py_ssize_t raw_size; + const char *raw_str = PyUnicode_AsUTF8AndSize(key.ptr(), &raw_size); + if (raw_str == NULL) { + throw py::error_already_set(); + } + + std::string_view keyRef(raw_str, raw_size); + frc::SmartDashboard::PutData(keyRef, data.get()); + + // this comes after the PutData to ensure that the original object doesn't die + // while PutData is called + rpy::addSmartDashboardData(key, data); + } + wpi::Sendable*: + cpp_code: | + [](std::shared_ptr value) { + frc::SmartDashboard::PutData(value.get()); + // this comes after the PutData to ensure that the original object doesn't die + // while PutData is called + auto name = wpi::SendableRegistry::GetName(value.get()); + if (!name.empty()) { + py::str key(name); + rpy::addSmartDashboardData(key, value); + } + } + GetData: + PutBoolean: + SetDefaultBoolean: + GetBoolean: + cpp_code: | + [](std::string_view key, py::object defaultValue) -> py::object { + nt::Value value; + { + py::gil_scoped_release release; + auto entry = frc::SmartDashboard::GetEntry(key); + value = nt::GetEntryValue(entry.GetHandle()); + } + if (!value || value.type() != NT_BOOLEAN) return defaultValue; + return py::cast(value.GetBoolean()); + } + PutNumber: + SetDefaultNumber: + GetNumber: + cpp_code: | + [](std::string_view key, py::object defaultValue) -> py::object { + nt::Value value; + { + py::gil_scoped_release release; + auto entry = frc::SmartDashboard::GetEntry(key); + value = nt::GetEntryValue(entry.GetHandle()); + } + if (!value || value.type() != NT_DOUBLE) return defaultValue; + return py::cast(value.GetDouble()); + } + PutString: + SetDefaultString: + GetString: + cpp_code: | + [](std::string_view key, py::object defaultValue) -> py::object { + nt::Value value; + { + py::gil_scoped_release release; + auto entry = frc::SmartDashboard::GetEntry(key); + value = nt::GetEntryValue(entry.GetHandle()); + } + if (!value || value.type() != NT_STRING) return defaultValue; + return py::cast(value.GetString()); + } + PutBooleanArray: + SetDefaultBooleanArray: + GetBooleanArray: + cpp_code: | + [](std::string_view key, py::object defaultValue) -> py::object { + nt::Value value; + { + py::gil_scoped_release release; + auto entry = frc::SmartDashboard::GetEntry(key); + value = nt::GetEntryValue(entry.GetHandle()); + } + if (!value || value.type() != NT_BOOLEAN_ARRAY) return defaultValue; + // ntcore will return bit vector by default. Convert to List[bool] + auto v = value.value(); + py::list l(v.data.arr_boolean.size); + for (size_t i = 0; i < v.data.arr_boolean.size; i++) { + auto b = py::bool_(v.data.arr_boolean.arr[i]); + PyList_SET_ITEM(l.ptr(), i, b.release().ptr()); + } + return l; + } + PutNumberArray: + SetDefaultNumberArray: + GetNumberArray: + cpp_code: | + [](std::string_view key, py::object defaultValue) -> py::object { + nt::Value value; + { + py::gil_scoped_release release; + auto entry = frc::SmartDashboard::GetEntry(key); + value = nt::GetEntryValue(entry.GetHandle()); + } + if (!value || value.type() != NT_DOUBLE_ARRAY) return defaultValue; + return py::cast(value.GetDoubleArray()); + } + PutStringArray: + SetDefaultStringArray: + GetStringArray: + cpp_code: | + [](std::string_view key, py::object defaultValue) -> py::object { + nt::Value value; + { + py::gil_scoped_release release; + auto entry = frc::SmartDashboard::GetEntry(key); + value = nt::GetEntryValue(entry.GetHandle()); + } + if (!value || value.type() != NT_STRING_ARRAY) return defaultValue; + return py::cast(value.GetStringArray()); + } + PutRaw: + SetDefaultRaw: + GetRaw: + cpp_code: | + [](std::string_view key, py::object defaultValue) -> py::object { + nt::Value value; + { + py::gil_scoped_release release; + auto entry = frc::SmartDashboard::GetEntry(key); + value = nt::GetEntryValue(entry.GetHandle()); + } + if (!value || value.type() != NT_STRING) return defaultValue; + return py::cast(value.GetString()); + } + PutValue: + SetDefaultValue: + GetValue: + PostListenerTask: + UpdateValues: + +inline_code: |- + // ensure that the smart dashboard data is released when python shuts down + static int unused; // the capsule needs something to reference + py::capsule cleanup(&unused, [](void *) { + rpy::destroySmartDashboardData(); + }); + m.add_object("_sd_cleanup", cleanup); + m.def("_clearSmartDashboardData", &rpy::clearSmartDashboardData); diff --git a/wpilibc/src/main/python/semiwrap/Solenoid.yml b/wpilibc/src/main/python/semiwrap/Solenoid.yml new file mode 100644 index 0000000000..8155ba4d99 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Solenoid.yml @@ -0,0 +1,20 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::Solenoid: + ignored_bases: + - wpi::SendableHelper + methods: + Solenoid: + overloads: + int, PneumaticsModuleType, int: + int, int, PneumaticsModuleType, int: + Set: + Get: + Toggle: + GetChannel: + IsDisabled: + SetPulseDuration: + StartPulse: + InitSendable: diff --git a/wpilibc/src/main/python/semiwrap/Spark.yml b/wpilibc/src/main/python/semiwrap/Spark.yml new file mode 100644 index 0000000000..6e8dd2d5be --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Spark.yml @@ -0,0 +1,7 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::Spark: + methods: + Spark: diff --git a/wpilibc/src/main/python/semiwrap/SparkMini.yml b/wpilibc/src/main/python/semiwrap/SparkMini.yml new file mode 100644 index 0000000000..fa2f380434 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/SparkMini.yml @@ -0,0 +1,4 @@ +classes: + frc::SparkMini: + methods: + SparkMini: diff --git a/wpilibc/src/main/python/semiwrap/StadiaController.yml b/wpilibc/src/main/python/semiwrap/StadiaController.yml new file mode 100644 index 0000000000..00222d78e9 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/StadiaController.yml @@ -0,0 +1,103 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::StadiaController: + ignored_bases: + - wpi::SendableHelper + methods: + StadiaController: + GetLeftX: + GetRightX: + GetLeftY: + GetRightY: + GetLeftBumper: + GetRightBumper: + GetLeftBumperPressed: + GetRightBumperPressed: + GetLeftBumperReleased: + GetRightBumperReleased: + LeftBumper: + RightBumper: + GetLeftStickButton: + GetRightStickButton: + GetLeftStickButtonPressed: + GetRightStickButtonPressed: + GetLeftStickButtonReleased: + GetRightStickButtonReleased: + LeftStick: + RightStick: + GetAButton: + GetAButtonPressed: + GetAButtonReleased: + A: + GetBButton: + GetBButtonPressed: + GetBButtonReleased: + B: + GetXButton: + GetXButtonPressed: + GetXButtonReleased: + X: + GetYButton: + GetYButtonPressed: + GetYButtonReleased: + Y: + GetEllipsesButton: + GetEllipsesButtonPressed: + GetEllipsesButtonReleased: + Ellipses: + GetHamburgerButton: + GetHamburgerButtonPressed: + GetHamburgerButtonReleased: + Hamburger: + GetStadiaButton: + GetStadiaButtonPressed: + GetStadiaButtonReleased: + Stadia: + GetGoogleButton: + GetGoogleButtonPressed: + GetGoogleButtonReleased: + Google: + GetFrameButton: + GetFrameButtonPressed: + GetFrameButtonReleased: + Frame: + GetLeftTriggerButton: + GetLeftTriggerButtonPressed: + GetLeftTriggerButtonReleased: + LeftTrigger: + GetRightTriggerButton: + GetRightTriggerButtonPressed: + GetRightTriggerButtonReleased: + RightTrigger: + GetLeftBumperButton: + GetLeftBumperButtonPressed: + GetLeftBumperButtonReleased: + GetRightBumperButton: + GetRightBumperButtonPressed: + GetRightBumperButtonReleased: + InitSendable: + frc::StadiaController::Button: + attributes: + kA: + kB: + kX: + kY: + kLeftBumper: + kRightBumper: + kLeftStick: + kRightStick: + kEllipses: + kHamburger: + kStadia: + kRightTrigger: + kLeftTrigger: + kGoogle: + kFrame: + frc::StadiaController::Axis: + attributes: + kLeftX: + kRightX: + kLeftY: + kRightY: diff --git a/wpilibc/src/main/python/semiwrap/SysIdRoutineLog.yml b/wpilibc/src/main/python/semiwrap/SysIdRoutineLog.yml new file mode 100644 index 0000000000..f8e020e5c2 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/SysIdRoutineLog.yml @@ -0,0 +1,35 @@ +defaults: + subpackage: sysid + +enums: + State: + inline_code: | + .def("__str__", &SysIdRoutineLog::StateEnumToString) + +classes: + frc::sysid::SysIdRoutineLog: + methods: + SysIdRoutineLog: + RecordState: + Motor: + StateEnumToString: + frc::sysid::SysIdRoutineLog::MotorLog: + methods: + value: + voltage: + position: + overloads: + units::meter_t: + units::turn_t: + rename: angularPosition + velocity: + overloads: + units::meters_per_second_t: + units::turns_per_second_t: + rename: angularVelocity + acceleration: + overloads: + units::meters_per_second_squared_t: + units::turns_per_second_squared_t: + rename: angularAcceleration + current: diff --git a/wpilibc/src/main/python/semiwrap/SystemServer.yml b/wpilibc/src/main/python/semiwrap/SystemServer.yml new file mode 100644 index 0000000000..21bfd10ca8 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/SystemServer.yml @@ -0,0 +1,4 @@ +classes: + frc::SystemServer: + methods: + GetSystemServer: diff --git a/wpilibc/src/main/python/semiwrap/Talon.yml b/wpilibc/src/main/python/semiwrap/Talon.yml new file mode 100644 index 0000000000..85558634ec --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Talon.yml @@ -0,0 +1,7 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::Talon: + methods: + Talon: diff --git a/wpilibc/src/main/python/semiwrap/Threads.yml b/wpilibc/src/main/python/semiwrap/Threads.yml new file mode 100644 index 0000000000..2d71b1b586 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Threads.yml @@ -0,0 +1,7 @@ +functions: + GetThreadPriority: + ignore: true + GetCurrentThreadPriority: + SetThreadPriority: + ignore: true + SetCurrentThreadPriority: diff --git a/wpilibc/src/main/python/semiwrap/TimedRobot.yml b/wpilibc/src/main/python/semiwrap/TimedRobot.yml new file mode 100644 index 0000000000..c787bb715b --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/TimedRobot.yml @@ -0,0 +1,17 @@ +classes: + frc::TimedRobot: + attributes: + kDefaultPeriod: + methods: + StartCompetition: + EndCompetition: + GetLoopStartTime: + AddPeriodic: + param_override: + offset: + default: 0_s + TimedRobot: + overloads: + double: + ignore: true + units::second_t: diff --git a/wpilibc/src/main/python/semiwrap/Timer.yml b/wpilibc/src/main/python/semiwrap/Timer.yml new file mode 100644 index 0000000000..5b50a3f2fb --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Timer.yml @@ -0,0 +1,18 @@ +functions: + Wait: + GetTime: +classes: + frc::Timer: + methods: + Timer: + Get: + Reset: + Start: + Restart: + Stop: + GetFPGATimestamp: + GetMatchTime: + HasElapsed: + AdvanceIfElapsed: + IsRunning: + GetTimestamp: diff --git a/wpilibc/src/main/python/semiwrap/TimesliceRobot.yml b/wpilibc/src/main/python/semiwrap/TimesliceRobot.yml new file mode 100644 index 0000000000..1e507942da --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/TimesliceRobot.yml @@ -0,0 +1,5 @@ +classes: + frc::TimesliceRobot: + methods: + TimesliceRobot: + Schedule: diff --git a/wpilibc/src/main/python/semiwrap/Tracer.yml b/wpilibc/src/main/python/semiwrap/Tracer.yml new file mode 100644 index 0000000000..e3c60eff8c --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Tracer.yml @@ -0,0 +1,31 @@ +extra_includes: +- wpi/SmallString.h +- wpi/raw_ostream.h + +classes: + frc::Tracer: + methods: + Tracer: + ResetTimer: + ClearEpochs: + AddEpoch: + PrintEpochs: + overloads: + '': + wpi::raw_ostream&: + ignore: true + +inline_code: |- + cls_Tracer + .def("getEpochs", + [](Tracer * self) -> py::str { + wpi::SmallString<128> buf; + wpi::raw_svector_ostream s(buf); + self->PrintEpochs(s); + return py::cast(s.str()); + }, + "Retreives list of epochs added so far as a string\n" + "\n" + ".. versionadded:: 2021.1.2\n" + "\n" + ".. note:: This function only exists in RobotPy\n"); diff --git a/wpilibc/src/main/python/semiwrap/Victor.yml b/wpilibc/src/main/python/semiwrap/Victor.yml new file mode 100644 index 0000000000..b55c81f1c7 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Victor.yml @@ -0,0 +1,7 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::Victor: + methods: + Victor: diff --git a/wpilibc/src/main/python/semiwrap/VictorSP.yml b/wpilibc/src/main/python/semiwrap/VictorSP.yml new file mode 100644 index 0000000000..19a8129cf4 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/VictorSP.yml @@ -0,0 +1,7 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h + +classes: + frc::VictorSP: + methods: + VictorSP: diff --git a/wpilibc/src/main/python/semiwrap/Watchdog.yml b/wpilibc/src/main/python/semiwrap/Watchdog.yml new file mode 100644 index 0000000000..cd00aca4b5 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/Watchdog.yml @@ -0,0 +1,22 @@ +classes: + frc::Watchdog: + methods: + Watchdog: + overloads: + units::second_t, std::function: + units::second_t, Callable&&, Arg&&, Args&&...: + ignore: true + GetTime: + SetTimeout: + overloads: + double: + ignore: true + units::second_t: + GetTimeout: + IsExpired: + AddEpoch: + PrintEpochs: + Reset: + Enable: + Disable: + SuppressTimeoutMessage: diff --git a/wpilibc/src/main/python/semiwrap/XboxController.yml b/wpilibc/src/main/python/semiwrap/XboxController.yml new file mode 100644 index 0000000000..389d70398a --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/XboxController.yml @@ -0,0 +1,92 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h +- frc/DriverStation.h +- frc/event/BooleanEvent.h + +classes: + frc::XboxController: + ignored_bases: + - wpi::SendableHelper + methods: + XboxController: + GetLeftX: + GetRightX: + GetLeftY: + GetRightY: + GetLeftTriggerAxis: + GetRightTriggerAxis: + GetLeftBumper: + GetRightBumper: + GetLeftBumperPressed: + GetRightBumperPressed: + GetLeftBumperReleased: + GetRightBumperReleased: + LeftBumper: + RightBumper: + GetLeftStickButton: + GetRightStickButton: + GetLeftStickButtonPressed: + GetRightStickButtonPressed: + GetLeftStickButtonReleased: + GetRightStickButtonReleased: + LeftStick: + RightStick: + GetAButton: + GetAButtonPressed: + GetAButtonReleased: + A: + GetBButton: + GetBButtonPressed: + GetBButtonReleased: + B: + GetXButton: + GetXButtonPressed: + GetXButtonReleased: + X: + GetYButton: + GetYButtonPressed: + GetYButtonReleased: + Y: + GetBackButton: + GetBackButtonPressed: + GetBackButtonReleased: + Back: + GetStartButton: + GetStartButtonPressed: + GetStartButtonReleased: + Start: + LeftTrigger: + overloads: + double, EventLoop* [const]: + EventLoop* [const]: + RightTrigger: + overloads: + double, EventLoop* [const]: + EventLoop* [const]: + GetLeftBumperButton: + GetLeftBumperButtonPressed: + GetLeftBumperButtonReleased: + GetRightBumperButton: + GetRightBumperButtonPressed: + GetRightBumperButtonReleased: + InitSendable: + frc::XboxController::Button: + attributes: + kLeftBumper: + kRightBumper: + kLeftStick: + kRightStick: + kA: + kB: + kX: + kY: + kBack: + kStart: + frc::XboxController::Axis: + attributes: + kLeftX: + kRightX: + kLeftY: + kRightY: + kLeftTrigger: + kRightTrigger: diff --git a/wpilibc/src/main/python/semiwrap/counter/EdgeConfiguration.yml b/wpilibc/src/main/python/semiwrap/counter/EdgeConfiguration.yml new file mode 100644 index 0000000000..ee83124224 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/counter/EdgeConfiguration.yml @@ -0,0 +1,2 @@ +enums: + EdgeConfiguration: diff --git a/wpilibc/src/main/python/semiwrap/counter/Tachometer.yml b/wpilibc/src/main/python/semiwrap/counter/Tachometer.yml new file mode 100644 index 0000000000..21a59af959 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/counter/Tachometer.yml @@ -0,0 +1,19 @@ +classes: + frc::Tachometer: + ignored_bases: + - wpi::SendableHelper + methods: + Tachometer: + overloads: + DigitalSource&: + std::shared_ptr: + GetFrequency: + GetPeriod: + GetEdgesPerRevolution: + SetEdgesPerRevolution: + GetRevolutionsPerSecond: + GetRevolutionsPerMinute: + GetStopped: + SetMaxPeriod: + InitSendable: + SetEdgeConfiguration: diff --git a/wpilibc/src/main/python/semiwrap/counter/UpDownCounter.yml b/wpilibc/src/main/python/semiwrap/counter/UpDownCounter.yml new file mode 100644 index 0000000000..9941ef0c38 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/counter/UpDownCounter.yml @@ -0,0 +1,13 @@ +classes: + frc::UpDownCounter: + ignored_bases: + - wpi::SendableHelper + methods: + UpDownCounter: + overloads: + DigitalSource&, DigitalSource&: + std::shared_ptr, std::shared_ptr: + GetCount: + Reset: + InitSendable: + SetEdgeConfiguration: diff --git a/wpilibc/src/main/python/semiwrap/drive/DifferentialDrive.yml b/wpilibc/src/main/python/semiwrap/drive/DifferentialDrive.yml new file mode 100644 index 0000000000..62dab560bc --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/drive/DifferentialDrive.yml @@ -0,0 +1,109 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h +- frc/motorcontrol/MotorController.h + +classes: + frc::DifferentialDrive: + ignored_bases: + - wpi::SendableHelper + methods: + DifferentialDrive: + overloads: + MotorController&, MotorController&: + keepalive: + - [1, 2] + - [1, 3] + std::function, std::function: + ArcadeDrive: + CurvatureDrive: + TankDrive: + ArcadeDriveIK: + CurvatureDriveIK: + TankDriveIK: + StopMotor: + GetDescription: + InitSendable: + doc: | + A class for driving differential drive/skid-steer drive platforms such as + the Kit of Parts drive base, "tank drive", or West Coast Drive. + + These drive bases typically have drop-center / skid-steer with two or more + wheels per side (e.g., 6WD or 8WD). This class takes a MotorController per + side. For four and six motor drivetrains, construct and pass in + :class:`MotorControllerGroup` instances as follows. + + Four motor drivetrain:: + + import wpilib.drive + + class Robot(wpilib.TimedRobot): + def robotInit(self): + self.front_left = wpilib.PWMVictorSPX(1) + self.rear_left = wpilib.PWMVictorSPX(2) + self.left = wpilib.MotorControllerGroup(self.front_left, self.rear_left) + + self.front_right = wpilib.PWMVictorSPX(3) + self.rear_right = wpilib.PWMVictorSPX(4) + self.right = wpilib.MotorControllerGroup(self.front_right, self.rear_right) + + self.drive = wpilib.drive.DifferentialDrive(self.left, self.right) + + Six motor drivetrain:: + + import wpilib.drive + + class Robot(wpilib.TimedRobot): + def robotInit(self): + self.front_left = wpilib.PWMVictorSPX(1) + self.mid_left = wpilib.PWMVictorSPX(2) + self.rear_left = wpilib.PWMVictorSPX(3) + self.left = wpilib.MotorControllerGroup(self.front_left, self.mid_left, self.rear_left) + + self.front_right = wpilib.PWMVictorSPX(4) + self.mid_right = wpilib.PWMVictorSPX(5) + self.rear_right = wpilib.PWMVictorSPX(6) + self.right = wpilib.MotorControllerGroup(self.front_right, self.mid_right, self.rear_right) + + self.drive = wpilib.drive.DifferentialDrive(self.left, self.right) + + A differential drive robot has left and right wheels separated by an + arbitrary width. + + Drive base diagram:: + + |_______| + | | | | + | | + |_|___|_| + | | + + Each Drive() function provides different inverse kinematic relations for a + differential drive robot. Motor outputs for the right side are negated, so + motor direction inversion by the user is usually unnecessary. + + This library uses the NED axes convention (North-East-Down as external + reference in the world frame): + http://www.nuclearprojects.com/ins/images/axis_big.png. + + The positive X axis points ahead, the positive Y axis points to the right, + and the positive Z axis points down. Rotations follow the right-hand rule, + so clockwise rotation around the Z axis is positive. + + Inputs smaller then 0.02 will be set to 0, and larger values will be scaled + so that the full range is still used. This deadband value can be changed + with SetDeadband(). + + RobotDrive porting guide: + + * :meth:`tankDrive` is equivalent to ``RobotDrive.tankDrive`` + if a deadband of 0 is used. + * :meth:`arcadeDrive` is equivalent to ``RobotDrive.arcadeDrive`` + if a deadband of 0 is used and the the rotation input is inverted, + e.g. ``arcadeDrive(y, -rotation, squareInputs=False)`` + * :meth:`curvatureDrive` is similar in concept to + ``RobotDrive.drive`` with the addition of a quick turn + mode. However, it is not designed to give exactly the same response. + frc::DifferentialDrive::WheelSpeeds: + attributes: + left: + right: diff --git a/wpilibc/src/main/python/semiwrap/drive/MecanumDrive.yml b/wpilibc/src/main/python/semiwrap/drive/MecanumDrive.yml new file mode 100644 index 0000000000..5396d11f52 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/drive/MecanumDrive.yml @@ -0,0 +1,38 @@ +extra_includes: +- wpi/sendable/SendableBuilder.h +- frc/motorcontrol/MotorController.h + +classes: + frc::MecanumDrive: + force_type_casters: + - units::radian_t + ignored_bases: + - wpi::SendableHelper + methods: + MecanumDrive: + overloads: + MotorController&, MotorController&, MotorController&, MotorController&: + keepalive: + - [1, 2] + - [1, 3] + - [1, 4] + - [1, 5] + std::function, std::function, std::function, std::function: + DriveCartesian: + param_override: + gyroAngle: + default: frc::Rotation2d(0_rad) + DrivePolar: + DriveCartesianIK: + param_override: + gyroAngle: + default: frc::Rotation2d(0_rad) + StopMotor: + GetDescription: + InitSendable: + frc::MecanumDrive::WheelSpeeds: + attributes: + frontLeft: + frontRight: + rearLeft: + rearRight: diff --git a/wpilibc/src/main/python/semiwrap/drive/RobotDriveBase.yml b/wpilibc/src/main/python/semiwrap/drive/RobotDriveBase.yml new file mode 100644 index 0000000000..83ce25c6e3 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/drive/RobotDriveBase.yml @@ -0,0 +1,21 @@ +extra_includes: +- frc/motorcontrol/MotorController.h +- wpi/SmallString.h + +classes: + frc::RobotDriveBase: + attributes: + m_deadband: + m_maxOutput: + kDefaultDeadband: + kDefaultMaxOutput: + enums: + MotorType: + methods: + RobotDriveBase: + SetDeadband: + SetMaxOutput: + FeedWatchdog: + StopMotor: + GetDescription: + Desaturate: diff --git a/wpilibc/src/main/python/semiwrap/event/BooleanEvent.yml b/wpilibc/src/main/python/semiwrap/event/BooleanEvent.yml new file mode 100644 index 0000000000..7de3bc73f4 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/event/BooleanEvent.yml @@ -0,0 +1,21 @@ +classes: + frc::BooleanEvent: + methods: + BooleanEvent: + GetAsBoolean: + IfHigh: + cpp_code: | + [](BooleanEvent *self, std::function action) { + self->IfHigh(std::move(action)); + } + CastTo: + cpp_code: | # TODO: how to annotate this correctly? + [](BooleanEvent *self, py::function constructor) -> py::object { + return constructor(self, (std::function)*self); + } + param_override: + ctor: + no_default: true + Rising: + Falling: + Debounce: diff --git a/wpilibc/src/main/python/semiwrap/event/EventLoop.yml b/wpilibc/src/main/python/semiwrap/event/EventLoop.yml new file mode 100644 index 0000000000..eab98c2db5 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/event/EventLoop.yml @@ -0,0 +1,13 @@ +classes: + frc::EventLoop: + force_type_casters: + - std::function + methods: + EventLoop: + Bind: + cpp_code: | + [](EventLoop *self, std::function action) { + self->Bind(std::move(action)); + } + Poll: + Clear: diff --git a/wpilibc/src/main/python/semiwrap/event/NetworkBooleanEvent.yml b/wpilibc/src/main/python/semiwrap/event/NetworkBooleanEvent.yml new file mode 100644 index 0000000000..2fbbcdce9d --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/event/NetworkBooleanEvent.yml @@ -0,0 +1,24 @@ +extra_includes: +- networktables/BooleanTopic.h +- networktables/NetworkTable.h +- networktables/NetworkTableInstance.h + +classes: + frc::NetworkBooleanEvent: + force_no_trampoline: true + methods: + NetworkBooleanEvent: + overloads: + EventLoop*, nt::BooleanTopic: + cpp_code: | + [](EventLoop *loop, nt::BooleanTopic &topic) { + return std::make_unique(loop, std::move(topic)); + } + EventLoop*, nt::BooleanSubscriber: + cpp_code: | + [](EventLoop *loop, nt::BooleanSubscriber &sub) { + return std::make_unique(loop, std::move(sub)); + } + EventLoop*, std::shared_ptr, std::string_view: + EventLoop*, std::string_view, std::string_view: + EventLoop*, nt::NetworkTableInstance, std::string_view, std::string_view: diff --git a/wpilibc/src/main/python/semiwrap/simulation/ADXL345Sim.yml b/wpilibc/src/main/python/semiwrap/simulation/ADXL345Sim.yml new file mode 100644 index 0000000000..aa232e08de --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/ADXL345Sim.yml @@ -0,0 +1,12 @@ +extra_includes: +- frc/ADXL345_I2C.h + +classes: + frc::sim::ADXL345Sim: + methods: + ADXL345Sim: + overloads: + const ADXL345_I2C&: + SetX: + SetY: + SetZ: diff --git a/wpilibc/src/main/python/semiwrap/simulation/AddressableLEDSim.yml b/wpilibc/src/main/python/semiwrap/simulation/AddressableLEDSim.yml new file mode 100644 index 0000000000..b4a71044e8 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/AddressableLEDSim.yml @@ -0,0 +1,26 @@ +extra_includes: +- frc/AddressableLED.h + +classes: + frc::sim::AddressableLEDSim: + force_type_casters: + - std::function + methods: + AddressableLEDSim: + overloads: + const AddressableLED&: + int: + RegisterInitializedCallback: + GetInitialized: + SetInitialized: + RegisterLengthCallback: + GetLength: + SetLength: + RegisterDataCallback: + GetData: + SetData: + RegisterStartCallback: + GetStart: + SetStart: + GetGlobalData: + SetGlobalData: diff --git a/wpilibc/src/main/python/semiwrap/simulation/AnalogEncoderSim.yml b/wpilibc/src/main/python/semiwrap/simulation/AnalogEncoderSim.yml new file mode 100644 index 0000000000..e5f63fcb4f --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/AnalogEncoderSim.yml @@ -0,0 +1,11 @@ +extra_includes: +- frc/AnalogEncoder.h + +classes: + frc::sim::AnalogEncoderSim: + force_type_casters: + - std::function + methods: + AnalogEncoderSim: + Set: + Get: diff --git a/wpilibc/src/main/python/semiwrap/simulation/AnalogInputSim.yml b/wpilibc/src/main/python/semiwrap/simulation/AnalogInputSim.yml new file mode 100644 index 0000000000..7996713c22 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/AnalogInputSim.yml @@ -0,0 +1,25 @@ +extra_includes: +- frc/AnalogInput.h + +classes: + frc::sim::AnalogInputSim: + force_type_casters: + - std::function + methods: + AnalogInputSim: + overloads: + const AnalogInput&: + int: + RegisterInitializedCallback: + GetInitialized: + SetInitialized: + RegisterAverageBitsCallback: + GetAverageBits: + SetAverageBits: + RegisterOversampleBitsCallback: + GetOversampleBits: + SetOversampleBits: + RegisterVoltageCallback: + GetVoltage: + SetVoltage: + ResetData: diff --git a/wpilibc/src/main/python/semiwrap/simulation/BatterySim.yml b/wpilibc/src/main/python/semiwrap/simulation/BatterySim.yml new file mode 100644 index 0000000000..e840ddf7fc --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/BatterySim.yml @@ -0,0 +1,13 @@ +classes: + frc::sim::BatterySim: + force_type_casters: + - units::ampere_t + methods: + Calculate: + overloads: + units::volt_t, units::ohm_t, std::span: + units::volt_t, units::ohm_t, std::initializer_list: + ignore: true + std::span: + std::initializer_list: + ignore: true diff --git a/wpilibc/src/main/python/semiwrap/simulation/CTREPCMSim.yml b/wpilibc/src/main/python/semiwrap/simulation/CTREPCMSim.yml new file mode 100644 index 0000000000..43a511218b --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/CTREPCMSim.yml @@ -0,0 +1,36 @@ +extra_includes: +- frc/Compressor.h + +classes: + frc::sim::CTREPCMSim: + typealias: + - frc::PneumaticsBase + force_type_casters: + - std::function + methods: + CTREPCMSim: + overloads: + '': + int: + const PneumaticsBase&: + RegisterInitializedCallback: + GetInitialized: + SetInitialized: + RegisterSolenoidOutputCallback: + GetSolenoidOutput: + SetSolenoidOutput: + RegisterCompressorOnCallback: + GetCompressorOn: + SetCompressorOn: + RegisterClosedLoopEnabledCallback: + GetClosedLoopEnabled: + SetClosedLoopEnabled: + RegisterPressureSwitchCallback: + GetPressureSwitch: + SetPressureSwitch: + RegisterCompressorCurrentCallback: + GetCompressorCurrent: + SetCompressorCurrent: + GetAllSolenoidOutputs: + SetAllSolenoidOutputs: + ResetData: diff --git a/wpilibc/src/main/python/semiwrap/simulation/CallbackStore.yml b/wpilibc/src/main/python/semiwrap/simulation/CallbackStore.yml new file mode 100644 index 0000000000..5254c98d48 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/CallbackStore.yml @@ -0,0 +1,26 @@ +functions: + CallbackStoreThunk: + ignore: true + ConstBufferCallbackStoreThunk: + ignore: true +classes: + frc::sim::CallbackStore: + force_type_casters: + - std::function + methods: + CallbackStore: + overloads: + # All are ignored because pybind11 can't use raw function pointers + int32_t, NotifyCallback, CancelCallbackNoIndexFunc: + ignore: true + int32_t, int32_t, NotifyCallback, CancelCallbackFunc: + ignore: true + int32_t, int32_t, int32_t, NotifyCallback, CancelCallbackChannelFunc: + ignore: true + int32_t, ConstBufferCallback, CancelCallbackNoIndexFunc: + ignore: true + int32_t, int32_t, ConstBufferCallback, CancelCallbackFunc: + ignore: true + int32_t, int32_t, int32_t, ConstBufferCallback, CancelCallbackChannelFunc: + ignore: true + SetUid: diff --git a/wpilibc/src/main/python/semiwrap/simulation/DCMotorSim.yml b/wpilibc/src/main/python/semiwrap/simulation/DCMotorSim.yml new file mode 100644 index 0000000000..19ba351052 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/DCMotorSim.yml @@ -0,0 +1,35 @@ +classes: + frc::sim::DCMotorSim: + typealias: + - frc::DCMotor + methods: + DCMotorSim: + overloads: + const LinearSystem<2, 1, 2>&, const DCMotor&, const std::array&: + param_override: + plant: + x_type: frc::LinearSystem<2,1,2> + SetState: + GetAngularPosition: + GetAngularVelocity: + GetCurrentDraw: + SetInputVoltage: + GetAngularAcceleration: + GetTorque: + GetInputVoltage: + GetGearbox: + GetGearing: + GetJ: + SetAngle: + SetAngularVelocity: + +inline_code: | + cls_DCMotorSim + // java API compatibility + .def("getAngularPositionRotations", [](const DCMotorSim &self) { + return units::turn_t{self.GetAngularPosition()}; + }, py::doc("Returns the DC motor position in rotations")) + .def("getAngularVelocityRPM", [](const DCMotorSim &self) { + return units::revolutions_per_minute_t{self.GetAngularVelocity()}; + }, py::doc("Returns the DC motor velocity in revolutions per minute")) + ; diff --git a/wpilibc/src/main/python/semiwrap/simulation/DIOSim.yml b/wpilibc/src/main/python/semiwrap/simulation/DIOSim.yml new file mode 100644 index 0000000000..12fd33aa6e --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/DIOSim.yml @@ -0,0 +1,30 @@ +extra_includes: +- frc/DigitalInput.h +- frc/DigitalOutput.h + +classes: + frc::sim::DIOSim: + force_type_casters: + - std::function + methods: + DIOSim: + overloads: + const DigitalInput&: + const DigitalOutput&: + int: + RegisterInitializedCallback: + GetInitialized: + SetInitialized: + RegisterValueCallback: + GetValue: + SetValue: + RegisterPulseLengthCallback: + GetPulseLength: + SetPulseLength: + RegisterIsInputCallback: + GetIsInput: + SetIsInput: + RegisterFilterIndexCallback: + GetFilterIndex: + SetFilterIndex: + ResetData: diff --git a/wpilibc/src/main/python/semiwrap/simulation/DifferentialDrivetrainSim.yml b/wpilibc/src/main/python/semiwrap/simulation/DifferentialDrivetrainSim.yml new file mode 100644 index 0000000000..045bfe7673 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/DifferentialDrivetrainSim.yml @@ -0,0 +1,96 @@ +classes: + frc::sim::DifferentialDrivetrainSim: + typealias: + - frc::DCMotor + - template using LinearSystem = frc::LinearSystem + methods: + DifferentialDrivetrainSim: + overloads: + LinearSystem<2, 2, 2>, units::meter_t, DCMotor, double, units::meter_t, const std::array&: + param_override: + measurementStdDevs: + default: std::array{} + ? frc::DCMotor, double, units::kilogram_square_meter_t, units::kilogram_t, units::meter_t, units::meter_t, const std::array& + : param_override: + measurementStdDevs: + default: std::array{} + ClampInput: + SetInputs: + SetGearing: + Update: + GetGearing: + GetHeading: + GetPose: + GetRightPosition: + GetRightVelocity: + GetLeftPosition: + GetLeftVelocity: + GetRightCurrentDraw: + GetLeftCurrentDraw: + GetCurrentDraw: + SetState: + SetPose: + Dynamics: + CreateKitbotSim: + overloads: + frc::DCMotor, double, units::meter_t, const std::array&: + param_override: + measurementStdDevs: + default: std::array{} + frc::DCMotor, double, units::meter_t, units::kilogram_square_meter_t, const std::array&: + param_override: + measurementStdDevs: + default: std::array{} + frc::sim::DifferentialDrivetrainSim::State: + attributes: + kX: + kY: + kHeading: + kLeftVelocity: + kRightVelocity: + kLeftPosition: + kRightPosition: + frc::sim::DifferentialDrivetrainSim::KitbotGearing: + attributes: + k12p75: + k10p71: + k8p45: + k7p31: + k5p95: + frc::sim::DifferentialDrivetrainSim::KitbotMotor: + attributes: + SingleCIMPerSide: + DualCIMPerSide: + SingleMiniCIMPerSide: + DualMiniCIMPerSide: + SingleFalcon500PerSide: + DualFalcon500PerSide: + SingleNEOPerSide: + DualNEOPerSide: + frc::sim::DifferentialDrivetrainSim::KitbotWheelSize: + attributes: + kSixInch: + kEightInch: + kTenInch: + +inline_code: |- + cls_DifferentialDrivetrainSim + .def("getLeftPositionFeet", [](DifferentialDrivetrainSim * self) -> units::foot_t { + return self->GetLeftPosition(); + }) + .def("getLeftPositionInches", [](DifferentialDrivetrainSim * self) -> units::inch_t { + return self->GetLeftPosition(); + }) + .def("getLeftVelocityFps", [](DifferentialDrivetrainSim * self) -> units::feet_per_second_t { + return self->GetLeftVelocity(); + }) + .def("getRightPositionFeet", [](DifferentialDrivetrainSim * self) -> units::foot_t { + return self->GetRightPosition(); + }) + .def("getRightPositionInches", [](DifferentialDrivetrainSim * self) -> units::inch_t { + return self->GetRightPosition(); + }) + .def("getRightVelocityFps", [](DifferentialDrivetrainSim * self) -> units::feet_per_second_t { + return self->GetRightVelocity(); + }) + ; diff --git a/wpilibc/src/main/python/semiwrap/simulation/DigitalPWMSim.yml b/wpilibc/src/main/python/semiwrap/simulation/DigitalPWMSim.yml new file mode 100644 index 0000000000..e770d5c519 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/DigitalPWMSim.yml @@ -0,0 +1,23 @@ +extra_includes: +- frc/DigitalOutput.h + +classes: + frc::sim::DigitalPWMSim: + force_type_casters: + - std::function + methods: + DigitalPWMSim: + overloads: + const DigitalOutput&: + CreateForChannel: + CreateForIndex: + RegisterInitializedCallback: + GetInitialized: + SetInitialized: + RegisterDutyCycleCallback: + GetDutyCycle: + SetDutyCycle: + RegisterPinCallback: + GetPin: + SetPin: + ResetData: diff --git a/wpilibc/src/main/python/semiwrap/simulation/DoubleSolenoidSim.yml b/wpilibc/src/main/python/semiwrap/simulation/DoubleSolenoidSim.yml new file mode 100644 index 0000000000..4b44c64500 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/DoubleSolenoidSim.yml @@ -0,0 +1,13 @@ +classes: + frc::sim::DoubleSolenoidSim: + typealias: + - frc::PneumaticsModuleType + methods: + DoubleSolenoidSim: + overloads: + std::shared_ptr, int, int: + int, PneumaticsModuleType, int, int: + PneumaticsModuleType, int, int: + Get: + Set: + GetModuleSim: diff --git a/wpilibc/src/main/python/semiwrap/simulation/DriverStationSim.yml b/wpilibc/src/main/python/semiwrap/simulation/DriverStationSim.yml new file mode 100644 index 0000000000..c58c6663b0 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/DriverStationSim.yml @@ -0,0 +1,51 @@ +classes: + frc::sim::DriverStationSim: + force_type_casters: + - std::function + methods: + RegisterEnabledCallback: + GetEnabled: + SetEnabled: + RegisterAutonomousCallback: + GetAutonomous: + SetAutonomous: + RegisterTestCallback: + GetTest: + SetTest: + RegisterEStopCallback: + GetEStop: + SetEStop: + RegisterFmsAttachedCallback: + GetFmsAttached: + SetFmsAttached: + RegisterDsAttachedCallback: + GetDsAttached: + SetDsAttached: + RegisterAllianceStationIdCallback: + GetAllianceStationId: + SetAllianceStationId: + RegisterMatchTimeCallback: + GetMatchTime: + SetMatchTime: + NotifyNewData: + SetSendError: + SetSendConsoleLine: + GetJoystickOutputs: + GetJoystickRumble: + SetJoystickButton: + SetJoystickAxis: + SetJoystickPOV: + SetJoystickButtons: + SetJoystickAxisCount: + SetJoystickPOVCount: + SetJoystickButtonCount: + SetJoystickType: + SetJoystickName: + SetJoystickAxisType: + SetGameSpecificMessage: + SetEventName: + SetMatchType: + SetMatchNumber: + SetReplayNumber: + ResetData: + SetJoystickIsGamepad: diff --git a/wpilibc/src/main/python/semiwrap/simulation/DutyCycleEncoderSim.yml b/wpilibc/src/main/python/semiwrap/simulation/DutyCycleEncoderSim.yml new file mode 100644 index 0000000000..e2a74fd9f7 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/DutyCycleEncoderSim.yml @@ -0,0 +1,14 @@ +extra_includes: +- frc/DutyCycleEncoder.h + +classes: + frc::sim::DutyCycleEncoderSim: + methods: + DutyCycleEncoderSim: + overloads: + const DutyCycleEncoder&: + int: + Get: + Set: + IsConnected: + SetConnected: diff --git a/wpilibc/src/main/python/semiwrap/simulation/DutyCycleSim.yml b/wpilibc/src/main/python/semiwrap/simulation/DutyCycleSim.yml new file mode 100644 index 0000000000..ca5f62a711 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/DutyCycleSim.yml @@ -0,0 +1,22 @@ +extra_includes: +- frc/DutyCycle.h + +classes: + frc::sim::DutyCycleSim: + force_type_casters: + - std::function + methods: + DutyCycleSim: + overloads: + const DutyCycle&: + CreateForChannel: + RegisterInitializedCallback: + GetInitialized: + SetInitialized: + RegisterFrequencyCallback: + GetFrequency: + SetFrequency: + RegisterOutputCallback: + GetOutput: + SetOutput: + ResetData: diff --git a/wpilibc/src/main/python/semiwrap/simulation/ElevatorSim.yml b/wpilibc/src/main/python/semiwrap/simulation/ElevatorSim.yml new file mode 100644 index 0000000000..8301854dfb --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/ElevatorSim.yml @@ -0,0 +1,42 @@ +classes: + frc::sim::ElevatorSim: + typealias: + - frc::DCMotor + - template using LinearSystem = frc::LinearSystem + - template using Vectord = frc::Vectord + methods: + ElevatorSim: + overloads: + ? const LinearSystem<2, 1, 2>&, const DCMotor&, units::meter_t, units::meter_t, bool, units::meter_t, const std::array& + : param_override: + measurementStdDevs: + default: std::array{0.0, 0.0} + ? const DCMotor&, double, units::kilogram_t, units::meter_t, units::meter_t, units::meter_t, bool, units::meter_t, const std::array& + : param_override: + measurementStdDevs: + default: std::array{0.0} + ? decltype(1_V/Velocity_t (1)), decltype(1_V/Acceleration_t (1)), const DCMotor&, units::meter_t, units::meter_t, bool, units::meter_t, const std::array& + : ignore: true + SetState: + WouldHitLowerLimit: + WouldHitUpperLimit: + HasHitLowerLimit: + HasHitUpperLimit: + GetPosition: + GetVelocity: + GetCurrentDraw: + SetInputVoltage: + UpdateX: + +inline_code: |- + cls_ElevatorSim + .def("getPositionFeet", [](ElevatorSim * self) -> units::foot_t { + return self->GetPosition(); + }) + .def("getPositionInches", [](ElevatorSim * self) -> units::inch_t { + return self->GetPosition(); + }) + .def("getVelocityFps", [](ElevatorSim * self) -> units::feet_per_second_t { + return self->GetVelocity(); + }) + ; diff --git a/wpilibc/src/main/python/semiwrap/simulation/EncoderSim.yml b/wpilibc/src/main/python/semiwrap/simulation/EncoderSim.yml new file mode 100644 index 0000000000..1bc697ff59 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/EncoderSim.yml @@ -0,0 +1,45 @@ +extra_includes: +- frc/Encoder.h + +classes: + frc::sim::EncoderSim: + force_type_casters: + - std::function + methods: + EncoderSim: + overloads: + const Encoder&: + CreateForChannel: + CreateForIndex: + RegisterInitializedCallback: + GetInitialized: + SetInitialized: + RegisterCountCallback: + GetCount: + SetCount: + RegisterPeriodCallback: + GetPeriod: + SetPeriod: + RegisterResetCallback: + GetReset: + SetReset: + RegisterMaxPeriodCallback: + GetMaxPeriod: + SetMaxPeriod: + RegisterDirectionCallback: + GetDirection: + SetDirection: + RegisterReverseDirectionCallback: + GetReverseDirection: + SetReverseDirection: + RegisterSamplesToAverageCallback: + GetSamplesToAverage: + SetSamplesToAverage: + RegisterDistancePerPulseCallback: + GetDistancePerPulse: + SetDistancePerPulse: + ResetData: + SetDistance: + GetDistance: + SetRate: + GetRate: diff --git a/wpilibc/src/main/python/semiwrap/simulation/FlywheelSim.yml b/wpilibc/src/main/python/semiwrap/simulation/FlywheelSim.yml new file mode 100644 index 0000000000..4e80360f8c --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/FlywheelSim.yml @@ -0,0 +1,27 @@ +classes: + frc::sim::FlywheelSim: + typealias: + - frc::DCMotor + - template using LinearSystem = frc::LinearSystem + methods: + FlywheelSim: + overloads: + const LinearSystem<1, 1, 1>&, const DCMotor&, double, const std::array&: + param_override: + measurementStdDevs: + default: std::array{0.0} + const DCMotor&, double, units::kilogram_square_meter_t, const std::array&: + param_override: + measurementStdDevs: + default: std::array{0.0} + SetState: + GetAngularVelocity: + GetCurrentDraw: + SetInputVoltage: + SetVelocity: + GetAngularAcceleration: + GetTorque: + GetInputVoltage: + Gearbox: + Gearing: + J: diff --git a/wpilibc/src/main/python/semiwrap/simulation/GenericHIDSim.yml b/wpilibc/src/main/python/semiwrap/simulation/GenericHIDSim.yml new file mode 100644 index 0000000000..176317c98a --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/GenericHIDSim.yml @@ -0,0 +1,23 @@ +classes: + frc::sim::GenericHIDSim: + methods: + GenericHIDSim: + overloads: + const GenericHID&: + int: + NotifyNewData: + SetRawButton: + SetRawAxis: + SetPOV: + overloads: + int, DriverStation::POVDirection: + DriverStation::POVDirection: + SetAxisCount: + SetPOVCount: + SetButtonCount: + SetType: + SetName: + SetAxisType: + GetOutput: + GetOutputs: + GetRumble: diff --git a/wpilibc/src/main/python/semiwrap/simulation/JoystickSim.yml b/wpilibc/src/main/python/semiwrap/simulation/JoystickSim.yml new file mode 100644 index 0000000000..a4b9680b5c --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/JoystickSim.yml @@ -0,0 +1,18 @@ +extra_includes: +- frc/Joystick.h + +classes: + frc::sim::JoystickSim: + force_no_trampoline: true + methods: + JoystickSim: + overloads: + const Joystick&: + int: + SetX: + SetY: + SetZ: + SetTwist: + SetThrottle: + SetTrigger: + SetTop: diff --git a/wpilibc/src/main/python/semiwrap/simulation/LinearSystemSim.yml b/wpilibc/src/main/python/semiwrap/simulation/LinearSystemSim.yml new file mode 100644 index 0000000000..9261649ed2 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/LinearSystemSim.yml @@ -0,0 +1,75 @@ +classes: + frc::sim::LinearSystemSim: + typealias: + - template using LinearSystem = frc::LinearSystem + - template using Vectord = frc::Vectord + template_params: + - int States + - int Inputs + - int Outputs + attributes: + m_plant: + m_x: + m_y: + m_u: + m_measurementStdDevs: + methods: + LinearSystemSim: + param_override: + measurementStdDevs: + default: std::array{} + Update: + GetOutput: + overloads: + '[const]': + int [const]: + SetInput: + overloads: + const Vectord&: + int, double: + GetInput: + overloads: + '[const]': + int [const]: + SetState: + UpdateX: + ClampInput: + + +templates: + LinearSystemSim_1_1_1: + qualname: frc::sim::LinearSystemSim + params: + - 1 + - 1 + - 1 + LinearSystemSim_1_1_2: + qualname: frc::sim::LinearSystemSim + params: + - 1 + - 1 + - 2 + LinearSystemSim_2_1_1: + qualname: frc::sim::LinearSystemSim + params: + - 2 + - 1 + - 1 + LinearSystemSim_2_1_2: + qualname: frc::sim::LinearSystemSim + params: + - 2 + - 1 + - 2 + LinearSystemSim_2_2_1: + qualname: frc::sim::LinearSystemSim + params: + - 2 + - 2 + - 1 + LinearSystemSim_2_2_2: + qualname: frc::sim::LinearSystemSim + params: + - 2 + - 2 + - 2 diff --git a/wpilibc/src/main/python/semiwrap/simulation/PS4ControllerSim.yml b/wpilibc/src/main/python/semiwrap/simulation/PS4ControllerSim.yml new file mode 100644 index 0000000000..1bfe801e5f --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/PS4ControllerSim.yml @@ -0,0 +1,33 @@ +extra_includes: +- frc/PS4Controller.h + +classes: + frc::sim::PS4ControllerSim: + force_no_trampoline: true + methods: + PS4ControllerSim: + overloads: + const PS4Controller&: + int: + SetLeftX: + SetRightX: + SetLeftY: + SetRightY: + SetL2Axis: + SetR2Axis: + SetSquareButton: + SetCrossButton: + SetCircleButton: + SetTriangleButton: + SetL1Button: + SetR1Button: + SetL2Button: + SetR2Button: + SetShareButton: + SetOptionsButton: + SetL3Button: + SetR3Button: + SetPSButton: + SetTouchpad: + ignore: true + SetTouchpadButton: diff --git a/wpilibc/src/main/python/semiwrap/simulation/PS5ControllerSim.yml b/wpilibc/src/main/python/semiwrap/simulation/PS5ControllerSim.yml new file mode 100644 index 0000000000..7b9314359a --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/PS5ControllerSim.yml @@ -0,0 +1,33 @@ +extra_includes: +- frc/PS5Controller.h + +classes: + frc::sim::PS5ControllerSim: + force_no_trampoline: true + methods: + PS5ControllerSim: + overloads: + const PS5Controller&: + int: + SetLeftX: + SetRightX: + SetLeftY: + SetRightY: + SetL2Axis: + SetR2Axis: + SetSquareButton: + SetCrossButton: + SetCircleButton: + SetTriangleButton: + SetL1Button: + SetR1Button: + SetL2Button: + SetR2Button: + SetCreateButton: + SetOptionsButton: + SetL3Button: + SetR3Button: + SetPSButton: + SetTouchpad: + ignore: true + SetTouchpadButton: diff --git a/wpilibc/src/main/python/semiwrap/simulation/PWMMotorControllerSim.yml b/wpilibc/src/main/python/semiwrap/simulation/PWMMotorControllerSim.yml new file mode 100644 index 0000000000..2b1e8e7d3d --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/PWMMotorControllerSim.yml @@ -0,0 +1,8 @@ +classes: + frc::sim::PWMMotorControllerSim: + methods: + PWMMotorControllerSim: + overloads: + const PWMMotorController&: + int: + GetSpeed: diff --git a/wpilibc/src/main/python/semiwrap/simulation/PWMSim.yml b/wpilibc/src/main/python/semiwrap/simulation/PWMSim.yml new file mode 100644 index 0000000000..e1d05a1f7b --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/PWMSim.yml @@ -0,0 +1,23 @@ +extra_includes: +- frc/PWM.h +- frc/motorcontrol/PWMMotorController.h + +classes: + frc::sim::PWMSim: + force_type_casters: + - std::function + methods: + PWMSim: + overloads: + const PWM&: + int: + RegisterInitializedCallback: + GetInitialized: + SetInitialized: + RegisterPulseMicrosecondCallback: + GetPulseMicrosecond: + SetPulseMicrosecond: + ResetData: + RegisterOutputPeriodCallback: + GetOutputPeriod: + SetOutputPeriod: diff --git a/wpilibc/src/main/python/semiwrap/simulation/PneumaticsBaseSim.yml b/wpilibc/src/main/python/semiwrap/simulation/PneumaticsBaseSim.yml new file mode 100644 index 0000000000..4dc83550b0 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/PneumaticsBaseSim.yml @@ -0,0 +1,32 @@ +classes: + frc::sim::PneumaticsBaseSim: + typealias: + - frc::PneumaticsBase + attributes: + m_index: + force_type_casters: + - std::function + methods: + GetForType: + GetInitialized: + SetInitialized: + RegisterInitializedCallback: + GetCompressorOn: + SetCompressorOn: + RegisterCompressorOnCallback: + GetSolenoidOutput: + SetSolenoidOutput: + RegisterSolenoidOutputCallback: + GetPressureSwitch: + SetPressureSwitch: + RegisterPressureSwitchCallback: + GetCompressorCurrent: + SetCompressorCurrent: + RegisterCompressorCurrentCallback: + GetAllSolenoidOutputs: + SetAllSolenoidOutputs: + ResetData: + PneumaticsBaseSim: + overloads: + const PneumaticsBase&: + const int: diff --git a/wpilibc/src/main/python/semiwrap/simulation/PowerDistributionSim.yml b/wpilibc/src/main/python/semiwrap/simulation/PowerDistributionSim.yml new file mode 100644 index 0000000000..7a1a1bc40a --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/PowerDistributionSim.yml @@ -0,0 +1,27 @@ +extra_includes: +- frc/PowerDistribution.h + +classes: + frc::sim::PowerDistributionSim: + force_type_casters: + - std::function + methods: + PowerDistributionSim: + overloads: + int: + const PowerDistribution&: + RegisterInitializedCallback: + GetInitialized: + SetInitialized: + RegisterTemperatureCallback: + GetTemperature: + SetTemperature: + RegisterVoltageCallback: + GetVoltage: + SetVoltage: + RegisterCurrentCallback: + GetCurrent: + SetCurrent: + GetAllCurrents: + SetAllCurrents: + ResetData: diff --git a/wpilibc/src/main/python/semiwrap/simulation/REVPHSim.yml b/wpilibc/src/main/python/semiwrap/simulation/REVPHSim.yml new file mode 100644 index 0000000000..e7f148d53c --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/REVPHSim.yml @@ -0,0 +1,36 @@ +extra_includes: +- frc/Compressor.h + +classes: + frc::sim::REVPHSim: + typealias: + - frc::PneumaticsBase + force_type_casters: + - std::function + methods: + REVPHSim: + overloads: + '': + int: + const PneumaticsBase&: + RegisterInitializedCallback: + GetInitialized: + SetInitialized: + RegisterSolenoidOutputCallback: + GetSolenoidOutput: + SetSolenoidOutput: + RegisterCompressorOnCallback: + GetCompressorOn: + SetCompressorOn: + RegisterCompressorConfigTypeCallback: + GetCompressorConfigType: + SetCompressorConfigType: + RegisterPressureSwitchCallback: + GetPressureSwitch: + SetPressureSwitch: + RegisterCompressorCurrentCallback: + GetCompressorCurrent: + SetCompressorCurrent: + GetAllSolenoidOutputs: + SetAllSolenoidOutputs: + ResetData: diff --git a/wpilibc/src/main/python/semiwrap/simulation/RoboRioSim.yml b/wpilibc/src/main/python/semiwrap/simulation/RoboRioSim.yml new file mode 100644 index 0000000000..75a6cdb25d --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/RoboRioSim.yml @@ -0,0 +1,34 @@ +classes: + frc::sim::RoboRioSim: + force_type_casters: + - std::function + methods: + RegisterVInVoltageCallback: + GetVInVoltage: + SetVInVoltage: + RegisterUserVoltage3V3Callback: + GetUserVoltage3V3: + SetUserVoltage3V3: + RegisterUserCurrent3V3Callback: + GetUserCurrent3V3: + SetUserCurrent3V3: + RegisterUserActive3V3Callback: + GetUserActive3V3: + SetUserActive3V3: + RegisterUserFaults3V3Callback: + GetUserFaults3V3: + SetUserFaults3V3: + RegisterBrownoutVoltageCallback: + GetBrownoutVoltage: + SetBrownoutVoltage: + RegisterCPUTempCallback: + GetCPUTemp: + SetCPUTemp: + RegisterTeamNumberCallback: + GetTeamNumber: + SetTeamNumber: + GetSerialNumber: + SetSerialNumber: + GetComments: + SetComments: + ResetData: diff --git a/wpilibc/src/main/python/semiwrap/simulation/SendableChooserSim.yml b/wpilibc/src/main/python/semiwrap/simulation/SendableChooserSim.yml new file mode 100644 index 0000000000..8fde3ba616 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/SendableChooserSim.yml @@ -0,0 +1,8 @@ +classes: + frc::sim::SendableChooserSim: + methods: + SendableChooserSim: + overloads: + std::string_view: + nt::NetworkTableInstance, std::string_view: + SetSelected: diff --git a/wpilibc/src/main/python/semiwrap/simulation/ServoSim.yml b/wpilibc/src/main/python/semiwrap/simulation/ServoSim.yml new file mode 100644 index 0000000000..57b3483fc2 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/ServoSim.yml @@ -0,0 +1,9 @@ +classes: + frc::sim::ServoSim: + methods: + ServoSim: + overloads: + const Servo&: + int: + GetPosition: + GetAngle: diff --git a/wpilibc/src/main/python/semiwrap/simulation/SharpIRSim.yml b/wpilibc/src/main/python/semiwrap/simulation/SharpIRSim.yml new file mode 100644 index 0000000000..5b9659ae7f --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/SharpIRSim.yml @@ -0,0 +1,8 @@ +classes: + frc::SharpIRSim: + methods: + SharpIRSim: + overloads: + const SharpIR&: + int: + SetRange: diff --git a/wpilibc/src/main/python/semiwrap/simulation/SimDeviceSim.yml b/wpilibc/src/main/python/semiwrap/simulation/SimDeviceSim.yml new file mode 100644 index 0000000000..8be1e5942b --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/SimDeviceSim.yml @@ -0,0 +1,85 @@ +extra_includes: +- pybind11/stl.h + +classes: + frc::sim::SimDeviceSim: + doc: | + Interact with a generic simulated device + + Any devices that support simulation but don't have a dedicated sim + object associated with it can be interacted with via this object. + You just need to know the name of the associated object. + + Here are two ways to find the names of available devices: + + * The static function :meth:`.enumerateDevices` can give you a list of + all available devices -- note that the device must be created first + before this will return any results! + * When running the WPILib simulation GUI, the names of the 'Other Devices' + panel are names of devices that you can interact with via this class. + + Once you've created a simulated device, you can use the :meth:`.enumerateValues` + method to determine what values you can interact with. + + + .. note:: WPILib has simulation support for all of its devices. Some + vendors may only have limited support for simulation -- read + the vendor's documentation or contact them for more information. + force_type_casters: + - std::function + - std::string_view + methods: + SimDeviceSim: + overloads: + const char*: + const char*, int: + const char*, int, int: + HAL_SimDeviceHandle: + GetName: + GetValue: + doc: | + Provides a readonly mechanism to retrieve all types of device values + GetInt: + doc: | + Retrieves an object that allows you to interact with simulated values + represented as an integer. + GetLong: + doc: | + Retrieves an object that allows you to interact with simulated values + represented as a long. + GetDouble: + doc: | + Retrieves an object that allows you to interact with simulated values + represented as a double. + GetEnum: + GetBoolean: + doc: | + Retrieves an object that allows you to interact with simulated values + represented as a boolean. + GetEnumOptions: + EnumerateValues: + ignore: true + EnumerateDevices: + ignore: true + ResetData: + +inline_code: | + cls_SimDeviceSim + .def("enumerateValues", [](frc::sim::SimDeviceSim * that) { + std::vector> values; + that->EnumerateValues([&values](const char * name, HAL_SimValueHandle handle, + HAL_Bool readonly, const struct HAL_Value * value){ + values.push_back(std::pair(name, readonly)); + }); + return values; + }, release_gil(), + "Returns a list of (name, readonly) tuples of available values for this device") + .def_static("enumerateDevices", [](const char * prefix) { + std::vector devices; + frc::sim::SimDeviceSim::EnumerateDevices(prefix, [&devices](const char * name, HAL_SimDeviceHandle handle) { + devices.push_back(std::string(name)); + }); + return devices; + }, release_gil(), + py::arg("prefix")="", + "Returns a list of available device names\n"); diff --git a/wpilibc/src/main/python/semiwrap/simulation/SimHooks.yml b/wpilibc/src/main/python/semiwrap/simulation/SimHooks.yml new file mode 100644 index 0000000000..856d4c8ac7 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/SimHooks.yml @@ -0,0 +1,11 @@ +functions: + SetRuntimeType: + WaitForProgramStart: + SetProgramStarted: + GetProgramStarted: + RestartTiming: + PauseTiming: + ResumeTiming: + IsTimingPaused: + StepTiming: + StepTimingAsync: diff --git a/wpilibc/src/main/python/semiwrap/simulation/SingleJointedArmSim.yml b/wpilibc/src/main/python/semiwrap/simulation/SingleJointedArmSim.yml new file mode 100644 index 0000000000..7321580345 --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/SingleJointedArmSim.yml @@ -0,0 +1,38 @@ +classes: + frc::sim::SingleJointedArmSim: + typealias: + - frc::DCMotor + - template using LinearSystem = frc::LinearSystem + - template using Vectord = frc::Vectord + methods: + SingleJointedArmSim: + overloads: + ? const LinearSystem<2, 1, 2>&, const DCMotor&, double, units::meter_t, units::radian_t, units::radian_t, bool, units::radian_t, const std::array& + : param_override: + measurementStdDevs: + default: std::array{0.0, 0.0} + ? const DCMotor&, double, units::kilogram_square_meter_t, units::meter_t, units::radian_t, units::radian_t, bool, units::radian_t, const std::array& + : param_override: + measurementStdDevs: + default: std::array{0.0, 0.0} + SetState: + WouldHitLowerLimit: + WouldHitUpperLimit: + HasHitLowerLimit: + HasHitUpperLimit: + GetAngle: + GetVelocity: + GetCurrentDraw: + SetInputVoltage: + EstimateMOI: + UpdateX: + +inline_code: |- + cls_SingleJointedArmSim + .def("getAngleDegrees", [](SingleJointedArmSim * self) -> units::degree_t { + return self->GetAngle(); + }) + .def("getVelocityDps", [](SingleJointedArmSim * self) -> units::degrees_per_second_t { + return self->GetVelocity(); + }) + ; diff --git a/wpilibc/src/main/python/semiwrap/simulation/SolenoidSim.yml b/wpilibc/src/main/python/semiwrap/simulation/SolenoidSim.yml new file mode 100644 index 0000000000..8cee1025ad --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/SolenoidSim.yml @@ -0,0 +1,16 @@ +classes: + frc::sim::SolenoidSim: + force_type_casters: + - std::function + typealias: + - frc::PneumaticsModuleType + methods: + SolenoidSim: + overloads: + std::shared_ptr, int: + int, PneumaticsModuleType, int: + PneumaticsModuleType, int: + GetOutput: + SetOutput: + RegisterOutputCallback: + GetModuleSim: diff --git a/wpilibc/src/main/python/semiwrap/simulation/StadiaControllerSim.yml b/wpilibc/src/main/python/semiwrap/simulation/StadiaControllerSim.yml new file mode 100644 index 0000000000..41acf5b15c --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/StadiaControllerSim.yml @@ -0,0 +1,30 @@ +extra_includes: +- frc/StadiaController.h + +classes: + frc::sim::StadiaControllerSim: + force_no_trampoline: true + methods: + StadiaControllerSim: + overloads: + const StadiaController&: + int: + SetLeftX: + SetRightX: + SetLeftY: + SetRightY: + SetAButton: + SetBButton: + SetXButton: + SetYButton: + SetLeftBumperButton: + SetRightBumperButton: + SetLeftStickButton: + SetRightStickButton: + SetEllipsesButton: + SetHamburgerButton: + SetStadiaButton: + SetRightTriggerButton: + SetLeftTriggerButton: + SetGoogleButton: + SetFrameButton: diff --git a/wpilibc/src/main/python/semiwrap/simulation/XboxControllerSim.yml b/wpilibc/src/main/python/semiwrap/simulation/XboxControllerSim.yml new file mode 100644 index 0000000000..815c61c1cd --- /dev/null +++ b/wpilibc/src/main/python/semiwrap/simulation/XboxControllerSim.yml @@ -0,0 +1,33 @@ +extra_includes: +- frc/XboxController.h + +classes: + frc::sim::XboxControllerSim: + force_no_trampoline: true + typealias: + - frc::XboxController + methods: + XboxControllerSim: + overloads: + const XboxController&: + int: + SetLeftX: + SetRightX: + SetLeftY: + SetRightY: + SetLeftTriggerAxis: + SetRightTriggerAxis: + SetLeftBumper: + ignore: true + SetRightBumper: + ignore: true + SetLeftStickButton: + SetRightStickButton: + SetAButton: + SetBButton: + SetXButton: + SetYButton: + SetBackButton: + SetStartButton: + SetLeftBumperButton: + SetRightBumperButton: diff --git a/wpilibc/src/main/python/wpilib/__init__.py b/wpilibc/src/main/python/wpilib/__init__.py new file mode 100644 index 0000000000..8a6e799a7c --- /dev/null +++ b/wpilibc/src/main/python/wpilib/__init__.py @@ -0,0 +1,210 @@ +from . import _init__wpilib + +# TODO: robotpy-build subpackage bug +from wpimath._controls._controls import trajectory as _ + +# autogenerated by 'semiwrap create-imports wpilib wpilib._wpilib' +from ._wpilib import ( + ADXL345_I2C, + AddressableLED, + Alert, + AnalogAccelerometer, + AnalogEncoder, + AnalogGyro, + AnalogInput, + AnalogPotentiometer, + CAN, + CANStatus, + Color, + Color8Bit, + Compressor, + CompressorConfigType, + DMC60, + DSControlWord, + DataLogManager, + DigitalInput, + DigitalOutput, + DoubleSolenoid, + DriverStation, + DutyCycle, + DutyCycleEncoder, + Encoder, + Field2d, + FieldObject2d, + I2C, + IterativeRobotBase, + Jaguar, + Joystick, + Koors40, + LEDPattern, + Mechanism2d, + MechanismLigament2d, + MechanismObject2d, + MechanismRoot2d, + MotorControllerGroup, + MotorSafety, + Notifier, + OnboardIMU, + PS4Controller, + PS5Controller, + PWM, + PWMMotorController, + PWMSparkFlex, + PWMSparkMax, + PWMTalonFX, + PWMTalonSRX, + PWMVenom, + PWMVictorSPX, + PneumaticHub, + PneumaticsBase, + PneumaticsControlModule, + PneumaticsModuleType, + PowerDistribution, + Preferences, + RobotBase, + RobotController, + RobotState, + RuntimeType, + SD540, + SendableBuilderImpl, + SendableChooser, + SendableChooserBase, + SensorUtil, + SerialPort, + Servo, + SharpIR, + SmartDashboard, + Solenoid, + Spark, + SparkMini, + StadiaController, + SystemServer, + Talon, + TimedRobot, + Timer, + TimesliceRobot, + Tracer, + Victor, + VictorSP, + Watchdog, + XboxController, + getCurrentThreadPriority, + getDeployDirectory, + getErrorMessage, + getOperatingDirectory, + getTime, + setCurrentThreadPriority, + wait, +) + +__all__ = [ + "ADXL345_I2C", + "AddressableLED", + "Alert", + "AnalogAccelerometer", + "AnalogEncoder", + "AnalogGyro", + "AnalogInput", + "AnalogPotentiometer", + "CAN", + "CANStatus", + "Color", + "Color8Bit", + "Compressor", + "CompressorConfigType", + "DMC60", + "DSControlWord", + "DataLogManager", + "DigitalInput", + "DigitalOutput", + "DoubleSolenoid", + "DriverStation", + "DutyCycle", + "DutyCycleEncoder", + "Encoder", + "Field2d", + "FieldObject2d", + "I2C", + "IterativeRobotBase", + "Jaguar", + "Joystick", + "Koors40", + "LEDPattern", + "Mechanism2d", + "MechanismLigament2d", + "MechanismObject2d", + "MechanismRoot2d", + "MotorControllerGroup", + "MotorSafety", + "Notifier", + "OnboardIMU", + "PS4Controller", + "PS5Controller", + "PWM", + "PWMMotorController", + "PWMSparkFlex", + "PWMSparkMax", + "PWMTalonFX", + "PWMTalonSRX", + "PWMVenom", + "PWMVictorSPX", + "PneumaticHub", + "PneumaticsBase", + "PneumaticsControlModule", + "PneumaticsModuleType", + "PowerDistribution", + "Preferences", + "RobotBase", + "RobotController", + "RobotState", + "RuntimeType", + "SD540", + "SendableBuilderImpl", + "SendableChooser", + "SendableChooserBase", + "SensorUtil", + "SerialPort", + "Servo", + "SharpIR", + "SmartDashboard", + "Solenoid", + "Spark", + "SparkMini", + "StadiaController", + "SystemServer", + "Talon", + "TimedRobot", + "Timer", + "TimesliceRobot", + "Tracer", + "Victor", + "VictorSP", + "Watchdog", + "XboxController", + "getCurrentThreadPriority", + "getDeployDirectory", + "getErrorMessage", + "getOperatingDirectory", + "getTime", + "setCurrentThreadPriority", + "wait", +] + +# Error reporting +from ._impl.report_error import reportError, reportWarning + +__all__ += ["reportError", "reportWarning"] + +del _init__wpilib + +from .cameraserver import CameraServer +from .deployinfo import getDeployData + +try: + from .version import version as __version__ +except ImportError: + __version__ = "master" + +from ._impl.main import run + +__all__ += ["CameraServer", "run"] diff --git a/wpilibc/src/main/python/wpilib/_impl/__init__.py b/wpilibc/src/main/python/wpilib/_impl/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/wpilibc/src/main/python/wpilib/_impl/main.py b/wpilibc/src/main/python/wpilib/_impl/main.py new file mode 100644 index 0000000000..80de3ee24c --- /dev/null +++ b/wpilibc/src/main/python/wpilib/_impl/main.py @@ -0,0 +1,18 @@ +import inspect +import sys + + +def run(robot_class, **kwargs): + """ + ``wpilib.run`` is no longer used. You should run your robot code via one of + the following methods instead: + + * Windows: ``py -m robotpy [arguments]`` + * Linux/macOS: ``python -m robotpy [arguments]`` + + In a virtualenv the ``robotpy`` command can be used directly. + """ + + msg = inspect.cleandoc(inspect.getdoc(run) or "`wpilib.run` is no longer used") + print(msg, file=sys.stderr) + sys.exit(1) diff --git a/wpilibc/src/main/python/wpilib/_impl/report_error.py b/wpilibc/src/main/python/wpilib/_impl/report_error.py new file mode 100644 index 0000000000..f9e447dcec --- /dev/null +++ b/wpilibc/src/main/python/wpilib/_impl/report_error.py @@ -0,0 +1,91 @@ +import hal +import sys +import traceback +import logging +import inspect + +robotpy_logger = logging.getLogger("robotpy") +user_logger = logging.getLogger("your.robot") + + +def reportErrorInternal( + error: str, + printTrace: bool = False, + fromUser: bool = False, + isWarning: bool = True, + code: int = 1, +) -> None: + traceString = "" + + if fromUser: + log = user_logger + else: + log = robotpy_logger + + if printTrace: + exc_info = sys.exc_info() + + exc = exc_info[0] + if exc is None: + tb = traceback.extract_stack()[:-2] + else: + tb = traceback.extract_tb(exc_info[2]) + + locString = "%s.%s:%s" % (tb[-1][0], tb[-1][1], tb[-1][2]) + + trc = "Traceback (most recent call last):\n" + stackstr = trc + "".join(traceback.format_list(tb)) + if exc is not None: + stackstr += " " + ("".join(traceback.format_exception(*exc_info))).lstrip( + trc + ) + traceString += "\n" + stackstr + + if exc is None: + log.error(error + "\n" + traceString) + else: + log.error(error, exc_info=exc_info) + else: + if isWarning: + log.warning(error) + else: + log.error(error) + + try: + frame = inspect.stack(context=2)[-1] + locString = f"{frame.filename}:{frame.lineno}" + except Exception: + locString = "" + + if not hal.__hal_simulation__: + hal.sendError( + not isWarning, + code, + False, + error, + locString, + traceString, + True, + ) + + +def reportError(error: str, printTrace: bool = False) -> None: + """ + Report error to Driver Station, and also prints error to ``sys.stderr``. + Optionally appends stack trace to error message. + + :param error: message to show + :param printTrace: If True, appends stack trace to error string + """ + reportErrorInternal(error, printTrace, fromUser=True) + + +def reportWarning(error: str, printTrace: bool = False) -> None: + """ + Report warning to Driver Station, and also prints error to ``sys.stderr``. + Optionally appends stack trace to error message. + + :param error: message to show + :param printTrace: If True, appends stack trace to error string + """ + reportErrorInternal(error, printTrace, fromUser=True, isWarning=True) diff --git a/wpilibc/src/main/python/wpilib/_impl/start.py b/wpilibc/src/main/python/wpilib/_impl/start.py new file mode 100644 index 0000000000..7cb442b1f6 --- /dev/null +++ b/wpilibc/src/main/python/wpilib/_impl/start.py @@ -0,0 +1,250 @@ +import hal +import wpilib +import logging +import os.path +import sys +import threading +import time +import typing + +import importlib.metadata + +if sys.version_info < (3, 10): + + def entry_points(group): + eps = importlib.metadata.entry_points() + return eps.get(group, []) + +else: + entry_points = importlib.metadata.entry_points + + +from .report_error import reportError, reportErrorInternal + + +def _log_versions(robotpy_version: typing.Optional[str]): + import wpilib + import wpilib.deployinfo + + import logging + + data = wpilib.deployinfo.getDeployData() + if data: + logger = logging.getLogger("deploy-info") + logger.info( + "%s@%s at %s", + data.get("deploy-user", ""), + data.get("deploy-host", ""), + data.get("deploy-date", ""), + ) + if "git-hash" in data: + logger.info( + "- git info: %s (branch=%s)", + data.get("git-desc", ""), + data.get("git-branch", ""), + ) + + logger = logging.getLogger("wpilib") + + if robotpy_version: + logger.info("RobotPy version %s", robotpy_version) + + logger.info("WPILib version %s", wpilib.__version__) + + if wpilib.RobotBase.isSimulation(): + logger.info("Running with simulated HAL.") + + # check to see if we're on a RoboRIO + # NOTE: may have false positives, but it should work well enough + if os.path.exists("/etc/natinst/share/scs_imagemetadata.ini"): + logger.warning( + "Running simulation HAL on actual roboRIO! This probably isn't what you want, and will probably cause difficult-to-debug issues!" + ) + + if logger.isEnabledFor(logging.DEBUG): + versions = {} + + # Log third party versions + for group in ("robotpylib", "robotpybuild"): + for entry_point in entry_points(group=group): + # Don't actually load the entry points -- just print the + # packages unless we need to load them + dist = entry_point.dist + versions[dist.name] = dist.version + + for k, v in versions.items(): + if k != "wpilib": + logger.debug("%s version %s", k, v) + + +class Main: + """ + Executes the robot code using the currently installed HAL (this is probably not what you want unless you're on the roboRIO) + """ + + def __init__(self, parser): + pass + + def run(self, options, robot_class, **static_options): + return robot_class.main(robot_class) + + +class RobotStarter: + def __init__(self): + self.logger = logging.getLogger("robotpy") + self.robot = None + self.suppressExitWarning = False + self._robotpy_version = None + + @property + def robotpy_version(self) -> typing.Optional[str]: + if not self._robotpy_version: + try: + pkg = importlib.metadata.metadata("robotpy") + except importlib.metadata.PackageNotFoundError: + pass + else: + self._robotpy_version = pkg.get("Version", None) + + return self._robotpy_version + + def run(self, robot_cls: wpilib.RobotBase) -> bool: + _log_versions(self.robotpy_version) + + retval = False + if hal.hasMain(): + rval = [False] + + def _start(): + try: + rval[0] = self.start(robot_cls) + finally: + hal.exitMain() + + th = threading.Thread(target=_start, name="RobotThread", daemon=True) + th.start() + try: + hal.runMain() + except KeyboardInterrupt: + self.logger.exception( + "THIS IS NOT AN ERROR: The user hit CTRL-C to kill the robot" + ) + self.logger.info("Exiting because of keyboard interrupt") + + self.suppressExitWarning = True + robot = self.robot + if robot: + try: + robot.endCompetition() + except: + self.logger.warning("endCompetition raised an exception") + + th.join(1) + if th.is_alive(): + self.logger.warning("robot thread didn't die, crash may occur next!") + retval = rval[0] + else: + retval = self.start(robot_cls) + + from wpilib import RobotBase + + if RobotBase.isSimulation(): + import wpilib.simulation + + wpilib.simulation._simulation._resetMotorSafety() + + return retval + + def start(self, robot_cls: wpilib.RobotBase) -> bool: + try: + return self._start(robot_cls) + except: + reportErrorInternal( + "The robot program quit unexpectedly. This is usually due to a code error.\n" + "The above stacktrace can help determine where the error occurred.\n", + True, + ) + return False + + def _start(self, robot_cls: wpilib.RobotBase) -> bool: + hal.reportUsage("Language", "Python") + + if not wpilib.Notifier.setHALThreadPriority(True, 40): + reportErrorInternal( + "Setting HAL Notifier RT priority to 40 failed", isWarning=True + ) + + isSimulation = wpilib.RobotBase.isSimulation() + + # hack: initialize networktables before creating the robot + # class, otherwise our logger doesn't get created + import ntcore + + inst = ntcore.NetworkTableInstance.getDefault() + + # subscribe to "" to force persistent values to progagate to local + msub = ntcore.MultiSubscriber(inst, [""]) + + if not isSimulation: + inst.startServer("/home/systemcore/networktables.ini") + else: + inst.startServer() + + # wait for the NT server to actually start + for i in range(100): + if ( + inst.getNetworkMode() + & ntcore.NetworkTableInstance.NetworkMode.kNetModeStarting + ) == 0: + break + # real sleep since we're waiting for the server, not simulated sleep + time.sleep(0.010) + else: + reportErrorInternal( + "timed out while waiting for NT server to start", isWarning=True + ) + + wpilib.SmartDashboard.init() + + # Call DriverStation.refreshData() to kick things off + wpilib.DriverStation.refreshData() + + try: + self.robot = robot_cls() + except: + reportError( + f"Unhandled exception instantiating robot {robot_cls.__name__}", True + ) + reportErrorInternal(f"Could not instantiate robot {robot_cls.__name__}!") + raise + + # TODO: Add a check to see if the user forgot to call super().__init__() + # if not hasattr(robot, "_RobotBase__initialized"): + # logger.error( + # "If your robot class has an __init__ function, it must call super().__init__()!" + # ) + # return False + + try: + self.robot.startCompetition() + except KeyboardInterrupt: + self.robot = None + self.logger.exception( + "THIS IS NOT AN ERROR: The user hit CTRL-C to kill the robot" + ) + self.logger.info("Exiting because of keyboard interrupt") + return True + except: + self.robot = None + + reportError("Unhandled exception", True) + raise + else: + self.robot = None + if self.suppressExitWarning: + self.logger.info("Robot code exited") + return True + else: + # startCompetition never returns unless exception occurs.... + reportError("Unexpected return from startCompetition() method.", False) + return False diff --git a/wpilibc/src/main/python/wpilib/_impl/utils.py b/wpilibc/src/main/python/wpilib/_impl/utils.py new file mode 100644 index 0000000000..706135dd15 --- /dev/null +++ b/wpilibc/src/main/python/wpilib/_impl/utils.py @@ -0,0 +1,231 @@ +# novalidate + +import sys +import inspect + + +def match_arglist(name, args, kwargs, templates, allow_extra_kwargs=False): + """ + This compares args and kwargs against the argument templates in templates. + + :param name: Name of the function being checked + :param args: The list of positional arguments + :param kwargs: The list of keyword arguments + :param templates: A list of dictionaries corresponding to possible + argument list formats. + :param allow_extra_kwargs: Whether or not to allow extra keyword arguments. If this + is true, then extra keyword arguments will be added to the result dictionary. + + An argument template is structured as follows: + + Each element in the list should be a tuple, corresponding to an argument name, + and argument type condition. See types_match() for argument type condition structures. + + :returns The id of the selected template + :returns A dictionary of argument name, value tuples. + : + """ + return __match_arglist(name, args, kwargs, templates, False, allow_extra_kwargs) + + +def _print(*args, **kwargs): + print(*args, **kwargs) + + +def __match_arglist(name, args, kwargs, templates, err, allow_extra_kwargs=False): + # TODO: we can do better at giving the user an error message... + + if err: + _print("*" * 50) + _print("ERROR: Invalid arguments passed to %s()!!" % name) + _print(" checking args against %s possible templates" % len(templates)) + _print("*" * 50) + if len(args): + _print("Your non-keyword arguments: ") + for i, arg in enumerate(args): + _print(" #%d: value %s, type %s" % (i, arg, type(arg))) + if len(kwargs): + _print("Your keyword args: ") + for k, v in kwargs.items(): + _print(" %s: value %s, type %s" % (k, v, type(v))) + _print("*" * 50) + + # Try each template until we find one that works + for i, template in enumerate(templates): + # List copies of the arguments + args_copy = list(reversed(args)) + kwargs_copy = list(kwargs.copy()) + kwarg_found = False + results = dict() + + if err: + _print( + "Checking template %s: %s" % (i, ", ".join(an for an, _ in template)) + ) + showed_error = False + + # Scan through all arguments and set valid to false if we find an issue. + for j, (arg_name, arg_type_condition) in enumerate(template): + # Check kwargs first, then check args + if arg_name in kwargs_copy: + kwargs_copy.remove(arg_name) + value = kwargs[arg_name] + match_type = "keyword" + kwarg_found = True + elif not kwarg_found and len(args_copy) > 0: + value = args_copy.pop() + match_type = "non-keyword" + else: + value = None + match_type = "optional" + + results[arg_name] = value + + # Check to see if identities match: + if not types_match(value, arg_type_condition): + if err: + _print( + "- Error at arg %d: %s != %s" + % (j, arg_name, typematch_to_str(arg_type_condition)) + ) + _print( + " your arg: %s; value %s %s" + % (match_type, value, type(value)) + ) + _print() + showed_error = True + break + + else: + # If the results are valid and the argument lists are empty, return the results. + if len(args_copy) == 0 and (len(kwargs_copy) == 0 or allow_extra_kwargs): + output = kwargs.copy() + output.update(results) + return templates.index(template), output + + if err and not showed_error: + if len(args_copy) != 0: + _print("- Error: too many arguments") + elif len(kwargs_copy): + _print( + "- Error: unused parameters: %s" + % ", ".join("%s" % s for s in kwargs_copy) + ) + + _print() + # _print("Template %s unmatched:" % i) + # if len(args_copy): + # _print("- Args: %s" % (' '.join('%s' % s for s in args_copy))) + # if len(kwargs_copy): + # _print("- Kwargs: %s" % (' '.join('%s' % s for s in kwargs_copy))) + + # We found nothing, then... but we need to give the user a good + # error message, so run it again in verbose mode, then raise the error! + if not err: + __match_arglist(name, args, kwargs, templates, True) + else: + _print("*" * 50) + raise ValueError( + "Attribute error, attributes given did not match any argument templates. See messages above for more info" + ) + + +def types_match(object, type_structure): + """ + :param object: the object to check + :param type_structure: The structure, composed of lists, strings, and types, + representing conditions that object must meet. + + Here are the possibilities for type_structure: + - HasAttribute + - type_condition + - list of type_structures + - None + + If type_structure is an HasAttribute, this will return False if + any contained attribute is not present in object + + If type_structure is a type_condition, then object must be of the same type + + If type_structure is a list of type_structures, it is handled by running + types_match on each item, and returning true if at least one matches. + + If type_structure is None, than everything matches it, and true is returned. + """ + + if type_structure is None: + return True + + # Is it an attribute list? + elif hasattr(type_structure, "matches"): + return type_structure.matches(object) + + elif isinstance(type_structure, list) and len(type_structure) != 0: + # If the list is not an attribute condition, check each element + # and return true if a match is found + for type_condition in type_structure: + if types_match(object, type_condition): + return True + else: + return isinstance(object, type_structure) + + +def typematch_to_str(type_structure): + """Only used for debugging""" + + if type_structure is None: + return "always matches" + + elif isinstance(type_structure, HasAttribute): + return "hasattr(%s)" % " or ".join(type_structure.conditions) + + elif isinstance(type_structure, list): + return " or ".join(typematch_to_str(tc) for tc in type_structure) + + else: + return "%s" % type_structure + + +class HasAttribute: + def __init__(self, *args): + self.conditions = args[:] + + def matches(self, object): + for attribute in self.conditions: + if not hasattr(object, attribute): + return False + + return True + + +def reset_wpilib(): + """ + Clears all devices from WPILib, and resets the hal data + + .. warning:: This is only intended to be used by test frameworks and + other debugging tools with the simulated HAL! + """ + + raise NotImplementedError("Not supported yet for 2020") + + modules = [ + "wpilib", + "wpilib.buttons", + "wpilib.command", + "wpilib.interfaces", + "wpilib.shuffleboard", + ] + + for modname in modules: + try: + module = sys.modules[modname] + except KeyError: + continue + + for _, cls in inspect.getmembers(module, inspect.isclass): + if hasattr(cls, "_reset"): + cls._reset() + + import hal_impl.functions + + hal_impl.functions.reset_hal() diff --git a/wpilibc/src/main/python/wpilib/cameraserver.py b/wpilibc/src/main/python/wpilib/cameraserver.py new file mode 100644 index 0000000000..397e3a9adb --- /dev/null +++ b/wpilibc/src/main/python/wpilib/cameraserver.py @@ -0,0 +1,94 @@ +from typing import Optional + +import hal +import subprocess +import threading + +import logging + +logger = logging.getLogger("wpilib.cs") + +__all__ = ["CameraServer"] + + +class CameraServer: + """ + Provides a way to launch an out of process cscore-based camera + service instance, for streaming or for image processing. + + .. note:: This does not correspond directly to the wpilib + CameraServer object; that can be found as + :class:`cscore.CameraServer`. However, you should + not use cscore directly from your robot code, see + the documentation for details + """ + + _alive = False + _launched = False + + @classmethod + def is_alive(cls) -> bool: + """:returns: True if the CameraServer is still alive""" + return cls._alive + + @classmethod + def launch(cls, vision_py: Optional[str] = None) -> None: + """ + Launches the CameraServer process in autocapture mode or + using a user-specified python script + + :param vision_py: If specified, this is the relative path to + a filename with a function in it + + Example usage:: + + wpilib.CameraServer.launch("vision.py:main") + + .. warning:: You must have robotpy-cscore installed, or this + function will fail without returning an error + (you will see an error in the console). + + """ + + if cls._launched: + return + + cls._launched = True + from ._wpilib import RobotBase + + if RobotBase.isSimulation(): + logger.info("Would launch CameraServer with vision_py=%s", vision_py) + cls._alive = True + else: + logger.info("Launching CameraServer process") + + # Launch the cscore launcher in a separate process + import sys + + args = [sys.executable, "-m", "cscore"] + + # TODO: Get accurate reporting data from the other cscore process. For + # now, just differentiate between users with a custom py file and those + # who do not. cscore handle values indicate type with bits 24-30 + + if vision_py: + hal.reportUsage("RobotPy/CameraServer", vision_py) + if not vision_py.startswith("/"): + vision_py = "/home/systemcore/py/" + vision_py + args.append(vision_py) + else: + hal.reportUsage("RobotPy/CameraServer", "") + + # We open a pipe to it so that when this process exits, it dies + proc = subprocess.Popen( + args, close_fds=True, stdin=subprocess.PIPE, cwd="/home/systemcore/py" + ) + th = threading.Thread(target=cls._monitor_child, args=(proc,)) + th.daemon = True + th.start() + + @classmethod + def _monitor_child(cls, proc: subprocess.Popen) -> None: + proc.wait() + logger.warning("CameraServer process exited with exitcode %s", proc.returncode) + cls._alive = False diff --git a/wpilibc/src/main/python/wpilib/counter/__init__.py b/wpilibc/src/main/python/wpilib/counter/__init__.py new file mode 100644 index 0000000000..756950c354 --- /dev/null +++ b/wpilibc/src/main/python/wpilib/counter/__init__.py @@ -0,0 +1,6 @@ +from . import _init__counter + +# autogenerated by 'semiwrap create-imports wpilib.counter wpilib.counter._counter' +from ._counter import EdgeConfiguration, Tachometer, UpDownCounter + +__all__ = ["EdgeConfiguration", "Tachometer", "UpDownCounter"] diff --git a/wpilibc/src/main/python/wpilib/counter/counter.cpp b/wpilibc/src/main/python/wpilib/counter/counter.cpp new file mode 100644 index 0000000000..7dbe6e455a --- /dev/null +++ b/wpilibc/src/main/python/wpilib/counter/counter.cpp @@ -0,0 +1,7 @@ + +#include "semiwrap_init.wpilib.counter._counter.hpp" + +SEMIWRAP_PYBIND11_MODULE(m) +{ + initWrapper(m); +} diff --git a/wpilibc/src/main/python/wpilib/deployinfo.py b/wpilibc/src/main/python/wpilib/deployinfo.py new file mode 100644 index 0000000000..b07be46cff --- /dev/null +++ b/wpilibc/src/main/python/wpilib/deployinfo.py @@ -0,0 +1,33 @@ +from ._wpilib import RobotBase +import json +import typing + + +def getDeployData() -> typing.Optional[typing.Dict[str, str]]: + """ + Utility function useful for retrieving deploy-related information + that pyfrc stores with your robot code. The dictionary has the + following keys: + + * deploy-host + * deploy-user + * deploy-date + * code-path + + If the code is deployed from a git repo, and the git program is in + your path, the following keys will also be present: + + * git-hash + * git-desc + * git-branch + + :returns: None in simulation, or a dictionary + """ + if not RobotBase.isReal(): + return None + + try: + with open("/home/systemcore/py/deploy.json") as fp: + return json.load(fp) + except FileNotFoundError: + return {} diff --git a/wpilibc/src/main/python/wpilib/drive/__init__.py b/wpilibc/src/main/python/wpilib/drive/__init__.py new file mode 100644 index 0000000000..5c9b913e81 --- /dev/null +++ b/wpilibc/src/main/python/wpilib/drive/__init__.py @@ -0,0 +1,8 @@ +from . import _init__drive + +# autogenerated by 'semiwrap create-imports wpilib.drive wpilib.drive._drive' +from ._drive import DifferentialDrive, MecanumDrive, RobotDriveBase + +__all__ = ["DifferentialDrive", "MecanumDrive", "RobotDriveBase"] + +del _init__drive diff --git a/wpilibc/src/main/python/wpilib/drive/drive.cpp b/wpilibc/src/main/python/wpilib/drive/drive.cpp new file mode 100644 index 0000000000..2ef4e0efd0 --- /dev/null +++ b/wpilibc/src/main/python/wpilib/drive/drive.cpp @@ -0,0 +1,7 @@ + +#include "semiwrap_init.wpilib.drive._drive.hpp" + +SEMIWRAP_PYBIND11_MODULE(m) +{ + initWrapper(m); +} diff --git a/wpilibc/src/main/python/wpilib/event/__init__.py b/wpilibc/src/main/python/wpilib/event/__init__.py new file mode 100644 index 0000000000..3bef5e2308 --- /dev/null +++ b/wpilibc/src/main/python/wpilib/event/__init__.py @@ -0,0 +1,6 @@ +from . import _init__event + +# autogenerated by 'semiwrap create-imports wpilib.event wpilib.event._event' +from ._event import BooleanEvent, EventLoop, NetworkBooleanEvent + +__all__ = ["BooleanEvent", "EventLoop", "NetworkBooleanEvent"] diff --git a/wpilibc/src/main/python/wpilib/event/event.cpp b/wpilibc/src/main/python/wpilib/event/event.cpp new file mode 100644 index 0000000000..2a317e39c9 --- /dev/null +++ b/wpilibc/src/main/python/wpilib/event/event.cpp @@ -0,0 +1,7 @@ + +#include "semiwrap_init.wpilib.event._event.hpp" + +SEMIWRAP_PYBIND11_MODULE(m) +{ + initWrapper(m); +} diff --git a/wpilibc/src/main/python/wpilib/interfaces/__init__.py b/wpilibc/src/main/python/wpilib/interfaces/__init__.py new file mode 100644 index 0000000000..db37b3bf1f --- /dev/null +++ b/wpilibc/src/main/python/wpilib/interfaces/__init__.py @@ -0,0 +1,4 @@ +# autogenerated by 'semiwrap create-imports wpilib.interfaces wpilib._wpilib.interfaces' +from .._wpilib.interfaces import CounterBase, GenericHID, MotorController + +__all__ = ["CounterBase", "GenericHID", "MotorController"] diff --git a/wpilibc/src/main/python/wpilib/py.typed b/wpilibc/src/main/python/wpilib/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/wpilibc/src/main/python/wpilib/simulation/__init__.py b/wpilibc/src/main/python/wpilib/simulation/__init__.py new file mode 100644 index 0000000000..fa55e172eb --- /dev/null +++ b/wpilibc/src/main/python/wpilib/simulation/__init__.py @@ -0,0 +1,117 @@ +from . import _init__simulation + +# needed for dcmotor return value, TODO fix in robotpy-build +from wpimath._controls._controls import plant as _ + +# autogenerated by 'semiwrap create-imports wpilib.simulation wpilib.simulation._simulation' +from ._simulation import ( + ADXL345Sim, + AddressableLEDSim, + AnalogEncoderSim, + AnalogInputSim, + BatterySim, + CTREPCMSim, + CallbackStore, + DCMotorSim, + DIOSim, + DifferentialDrivetrainSim, + DigitalPWMSim, + DoubleSolenoidSim, + DriverStationSim, + DutyCycleEncoderSim, + DutyCycleSim, + ElevatorSim, + EncoderSim, + FlywheelSim, + GenericHIDSim, + JoystickSim, + LinearSystemSim_1_1_1, + LinearSystemSim_1_1_2, + LinearSystemSim_2_1_1, + LinearSystemSim_2_1_2, + LinearSystemSim_2_2_1, + LinearSystemSim_2_2_2, + PS4ControllerSim, + PS5ControllerSim, + PWMMotorControllerSim, + PWMSim, + PneumaticsBaseSim, + PowerDistributionSim, + REVPHSim, + RoboRioSim, + SendableChooserSim, + ServoSim, + SharpIRSim, + SimDeviceSim, + SingleJointedArmSim, + SolenoidSim, + StadiaControllerSim, + XboxControllerSim, + getProgramStarted, + isTimingPaused, + pauseTiming, + restartTiming, + resumeTiming, + setProgramStarted, + setRuntimeType, + stepTiming, + stepTimingAsync, + waitForProgramStart, +) + +__all__ = [ + "ADXL345Sim", + "AddressableLEDSim", + "AnalogEncoderSim", + "AnalogInputSim", + "BatterySim", + "CTREPCMSim", + "CallbackStore", + "DCMotorSim", + "DIOSim", + "DifferentialDrivetrainSim", + "DigitalPWMSim", + "DoubleSolenoidSim", + "DriverStationSim", + "DutyCycleEncoderSim", + "DutyCycleSim", + "ElevatorSim", + "EncoderSim", + "FlywheelSim", + "GenericHIDSim", + "JoystickSim", + "LinearSystemSim_1_1_1", + "LinearSystemSim_1_1_2", + "LinearSystemSim_2_1_1", + "LinearSystemSim_2_1_2", + "LinearSystemSim_2_2_1", + "LinearSystemSim_2_2_2", + "PS4ControllerSim", + "PS5ControllerSim", + "PWMMotorControllerSim", + "PWMSim", + "PneumaticsBaseSim", + "PowerDistributionSim", + "REVPHSim", + "RoboRioSim", + "SendableChooserSim", + "ServoSim", + "SharpIRSim", + "SimDeviceSim", + "SingleJointedArmSim", + "SolenoidSim", + "StadiaControllerSim", + "XboxControllerSim", + "getProgramStarted", + "isTimingPaused", + "pauseTiming", + "restartTiming", + "resumeTiming", + "setProgramStarted", + "setRuntimeType", + "stepTiming", + "stepTimingAsync", + "waitForProgramStart", +] + +del _init__simulation diff --git a/wpilibc/src/main/python/wpilib/simulation/simulation.cpp b/wpilibc/src/main/python/wpilib/simulation/simulation.cpp new file mode 100644 index 0000000000..107d55fc5c --- /dev/null +++ b/wpilibc/src/main/python/wpilib/simulation/simulation.cpp @@ -0,0 +1,36 @@ + +#include "semiwrap_init.wpilib.simulation._simulation.hpp" + +#ifndef __FRC_SYSTEMCORE__ + +namespace frc::impl { +void ResetSmartDashboardInstance(); +void ResetMotorSafety(); +} // namespace frc::impl + +namespace wpi::impl { +void ResetSendableRegistry(); +} // namespace wpi::impl + +void resetWpilibSimulationData() { + frc::impl::ResetSmartDashboardInstance(); + frc::impl::ResetMotorSafety(); + wpi::impl::ResetSendableRegistry(); +} + +void resetMotorSafety() { + frc::impl::ResetMotorSafety(); +} + +#else +void resetWpilibSimulationData() {} +void resetMotorSafety() {} +#endif + +SEMIWRAP_PYBIND11_MODULE(m) { + initWrapper(m); + + m.def("_resetWpilibSimulationData", &resetWpilibSimulationData, + release_gil()); + m.def("_resetMotorSafety", &resetMotorSafety, release_gil()); +} diff --git a/wpilibc/src/main/python/wpilib/src/main.cpp b/wpilibc/src/main/python/wpilib/src/main.cpp new file mode 100644 index 0000000000..ea7f803161 --- /dev/null +++ b/wpilibc/src/main/python/wpilib/src/main.cpp @@ -0,0 +1,4 @@ + +#include "semiwrap_init.wpilib._wpilib.hpp" + +SEMIWRAP_PYBIND11_MODULE(m) { initWrapper(m); } diff --git a/wpilibc/src/main/python/wpilib/src/rpy/ControlWord.cpp b/wpilibc/src/main/python/wpilib/src/rpy/ControlWord.cpp new file mode 100644 index 0000000000..2ca92921cb --- /dev/null +++ b/wpilibc/src/main/python/wpilib/src/rpy/ControlWord.cpp @@ -0,0 +1,18 @@ +#include "rpy/ControlWord.h" + +#include + +namespace rpy { + +std::tuple GetControlState() { + HAL_ControlWord controlWord; + HAL_GetControlWord(&controlWord); + + bool enable = controlWord.enabled != 0 && controlWord.dsAttached != 0; + bool auton = controlWord.autonomous != 0; + bool test = controlWord.test != 0; + + return std::make_tuple(enable, auton, test); +} + +} // namespace rpy diff --git a/wpilibc/src/main/python/wpilib/src/rpy/ControlWord.h b/wpilibc/src/main/python/wpilib/src/rpy/ControlWord.h new file mode 100644 index 0000000000..c708af7cd3 --- /dev/null +++ b/wpilibc/src/main/python/wpilib/src/rpy/ControlWord.h @@ -0,0 +1,7 @@ +#include + +namespace rpy { + +std::tuple GetControlState(); + +} // namespace rpy diff --git a/wpilibc/src/main/python/wpilib/src/rpy/Filesystem.h b/wpilibc/src/main/python/wpilib/src/rpy/Filesystem.h new file mode 100644 index 0000000000..d26d2117d8 --- /dev/null +++ b/wpilibc/src/main/python/wpilib/src/rpy/Filesystem.h @@ -0,0 +1,33 @@ + +#pragma once + +#include + +namespace robotpy::filesystem { + +/** + * Obtains the operating directory of the program. On the robot, this + * is /home/systemcore/py. In simulation, it is the location of robot.py + * + * @return The result of the operating directory lookup. + */ +std::string GetOperatingDirectory(); + +/** + * Obtains the deploy directory of the program, which is the remote location + * the deploy directory is deployed to by default. On the robot, this is + * /home/systemcore/py/deploy. In simulation, it is where the simulation was launched + * from, in the subdirectory "deploy" (`dirname(robot.py)`/deploy). + * + * @return The result of the operating directory lookup + */ +std::string GetDeployDirectory(); + +// intended to be used by C++ bindings, returns same as GetOperatingDirectory +fs::path GetOperatingDirectoryFs(); +// intended to be used by C++ bindings, returns same as GetDeployDirectory +fs::path GetDeployDirectoryFs(); + +} // namespace robotpy::filesystem + +#include "Filesystem.inc" diff --git a/wpilibc/src/main/python/wpilib/src/rpy/Filesystem.inc b/wpilibc/src/main/python/wpilib/src/rpy/Filesystem.inc new file mode 100644 index 0000000000..4aad136dab --- /dev/null +++ b/wpilibc/src/main/python/wpilib/src/rpy/Filesystem.inc @@ -0,0 +1,54 @@ + +// TODO: this should be in a shared library, but robotpy-build does not support that + +#include +#include + +namespace robotpy::filesystem { + +static fs::path getMainPath() { + py::gil_scoped_acquire gil; + py::dict locals; + py::exec(R"( + found = False + try: + from robotpy.main import robot_py_path + if robot_py_path: + main_path = str(robot_py_path.parent.absolute()) + found = True + except ImportError: + pass + + if not found: + import sys, os.path + main = sys.modules['__main__']; + if hasattr(main, '__file__'): + main_path = os.path.abspath(os.path.dirname(main.__file__)) + )", + py::globals(), locals); + + if (locals.contains("main_path")) { + return fs::path(py::cast(locals["main_path"])); + } else { +#ifdef __FRC_SYSTEMCORE__ + return fs::path("/home/systemcore/py"); +#else + return fs::current_path(); +#endif + } +} + +inline std::string GetOperatingDirectory() { + return GetOperatingDirectoryFs().string(); +} + +inline std::string GetDeployDirectory() { return GetDeployDirectoryFs().string(); } + +inline fs::path GetOperatingDirectoryFs() { + static fs::path operatingPath = getMainPath(); + return operatingPath; +} + +inline fs::path GetDeployDirectoryFs() { return GetOperatingDirectoryFs() / "deploy"; } + +} // namespace robotpy::filesystem \ No newline at end of file diff --git a/wpilibc/src/main/python/wpilib/src/rpy/MotorControllerGroup.cpp b/wpilibc/src/main/python/wpilib/src/rpy/MotorControllerGroup.cpp new file mode 100644 index 0000000000..7fe0239113 --- /dev/null +++ b/wpilibc/src/main/python/wpilib/src/rpy/MotorControllerGroup.cpp @@ -0,0 +1,62 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#include "MotorControllerGroup.h" + +#include "wpi/sendable/SendableBuilder.h" + +using namespace frc; + +void PyMotorControllerGroup::Initialize() { + for (auto motorController : m_motorControllers) { + wpi::SendableRegistry::AddChild(this, motorController.get()); + } + static int instances = 0; + ++instances; + wpi::SendableRegistry::Add(this, "MotorControllerGroup", instances); +} + +void PyMotorControllerGroup::Set(double speed) { + for (auto motorController : m_motorControllers) { + motorController->Set(m_isInverted ? -speed : speed); + } +} + +void PyMotorControllerGroup::SetVoltage(units::volt_t output) { + for (auto motorController : m_motorControllers) { + motorController->SetVoltage(m_isInverted ? -output : output); + } +} + +double PyMotorControllerGroup::Get() const { + if (!m_motorControllers.empty()) { + return m_motorControllers.front()->Get() * (m_isInverted ? -1 : 1); + } + return 0.0; +} + +void PyMotorControllerGroup::SetInverted(bool isInverted) { + m_isInverted = isInverted; +} + +bool PyMotorControllerGroup::GetInverted() const { return m_isInverted; } + +void PyMotorControllerGroup::Disable() { + for (auto motorController : m_motorControllers) { + motorController->Disable(); + } +} + +void PyMotorControllerGroup::StopMotor() { + for (auto motorController : m_motorControllers) { + motorController->StopMotor(); + } +} + +void PyMotorControllerGroup::InitSendable(wpi::SendableBuilder& builder) { + builder.SetSmartDashboardType("Motor Controller"); + builder.SetActuator(true); + builder.AddDoubleProperty("Value", [=, this]() { return Get(); }, + [=, this](double value) { Set(value); }); +} diff --git a/wpilibc/src/main/python/wpilib/src/rpy/MotorControllerGroup.h b/wpilibc/src/main/python/wpilib/src/rpy/MotorControllerGroup.h new file mode 100644 index 0000000000..a34ada9449 --- /dev/null +++ b/wpilibc/src/main/python/wpilib/src/rpy/MotorControllerGroup.h @@ -0,0 +1,46 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#pragma once + +#include +#include +#include + + +#include "frc/motorcontrol/MotorController.h" +#include "wpi/sendable/Sendable.h" +#include "wpi/sendable/SendableHelper.h" + +namespace frc { + +class PyMotorControllerGroup : public wpi::Sendable, + public MotorController, + public wpi::SendableHelper { + public: + PyMotorControllerGroup(std::vector> &&args) : + m_motorControllers(args) {} + ~PyMotorControllerGroup() override = default; + + PyMotorControllerGroup(PyMotorControllerGroup&&) = default; + PyMotorControllerGroup& operator=(PyMotorControllerGroup&&) = default; + + void Set(double speed) override; + void SetVoltage(units::volt_t output) override; + double Get() const override; + void SetInverted(bool isInverted) override; + bool GetInverted() const override; + void Disable() override; + void StopMotor() override; + + void InitSendable(wpi::SendableBuilder& builder) override; + + private: + void Initialize(); + + bool m_isInverted = false; + std::vector> m_motorControllers; +}; + +} // namespace rpy diff --git a/wpilibc/src/main/python/wpilib/src/rpy/Notifier.cpp b/wpilibc/src/main/python/wpilib/src/rpy/Notifier.cpp new file mode 100644 index 0000000000..2e3ced010d --- /dev/null +++ b/wpilibc/src/main/python/wpilib/src/rpy/Notifier.cpp @@ -0,0 +1,189 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#include "rpy/Notifier.h" + +#include + +#include +#include +#include + +#include "frc/Errors.h" +#include "frc/Timer.h" + +#include +#include + +using namespace frc; +using namespace pybind11::literals; + +// Hang the thread since returning to the caller is going to crash when we try +// to obtain the GIL again +// - this is a daemon thread so it's fine? +// - Python 3.14 does this too +static void _hang_thread_if_finalizing() { + if (Py_IsFinalizing()) { + while (true) { + std::this_thread::sleep_for(std::chrono::seconds(1000)); + } + } +} + +PyNotifier::PyNotifier(std::function handler) { + if (!handler) { + throw FRC_MakeError(err::NullParameter, "handler"); + } + m_handler = handler; + int32_t status = 0; + m_notifier = HAL_InitializeNotifier(&status); + FRC_CheckErrorStatus(status, "InitializeNotifier"); + + std::function target([this] { + py::gil_scoped_release release; + + try { + for (;;) { + int32_t status = 0; + HAL_NotifierHandle notifier = m_notifier.load(); + if (notifier == 0) { + break; + } + uint64_t curTime = HAL_WaitForNotifierAlarm(notifier, &status); + if (curTime == 0 || status != 0) { + break; + } + + std::function handler; + { + std::scoped_lock lock(m_processMutex); + handler = m_handler; + if (m_periodic) { + m_expirationTime += m_period; + UpdateAlarm(); + } else { + // need to update the alarm to cause it to wait again + UpdateAlarm(UINT64_MAX); + } + } + + // call callback + if (handler) { + if (Py_IsFinalizing()) { + break; + } + + handler(); + } + } + } catch (...) { + _hang_thread_if_finalizing(); + throw; + } + + _hang_thread_if_finalizing(); + }); + + py::gil_scoped_acquire acquire; + + // create a python thread and start it + auto Thread = py::module::import("threading").attr("Thread"); + m_thread = Thread("target"_a = target, "daemon"_a = true, + "name"_a = "notifier-thread"); + m_thread.attr("start")(); +} + +PyNotifier::~PyNotifier() { + int32_t status = 0; + // atomically set handle to 0, then clean + HAL_NotifierHandle handle = m_notifier.exchange(0); + HAL_StopNotifier(handle, &status); + FRC_ReportError(status, "StopNotifier"); + + // Join the thread to ensure the handler has exited. + if (m_thread) { + m_thread.attr("join")(); + } + + HAL_CleanNotifier(handle); +} + +PyNotifier::PyNotifier(PyNotifier &&rhs) + : m_thread(std::move(rhs.m_thread)), + m_notifier(rhs.m_notifier.load()), + m_handler(std::move(rhs.m_handler)), + m_expirationTime(std::move(rhs.m_expirationTime)), + m_period(std::move(rhs.m_period)), + m_periodic(std::move(rhs.m_periodic)) { + rhs.m_notifier = HAL_kInvalidHandle; +} + +PyNotifier &PyNotifier::operator=(PyNotifier &&rhs) { + m_thread = std::move(rhs.m_thread); + m_notifier = rhs.m_notifier.load(); + rhs.m_notifier = HAL_kInvalidHandle; + m_handler = std::move(rhs.m_handler); + m_expirationTime = std::move(rhs.m_expirationTime); + m_period = std::move(rhs.m_period); + m_periodic = std::move(rhs.m_periodic); + + return *this; +} + +void PyNotifier::SetName(std::string_view name) { + fmt::memory_buffer buf; + fmt::format_to(fmt::appender{buf}, "{}", name); + buf.push_back('\0'); // null terminate + int32_t status = 0; + HAL_SetNotifierName(m_notifier, buf.data(), &status); +} + +void PyNotifier::SetCallback(std::function handler) { + std::scoped_lock lock(m_processMutex); + m_handler = handler; +} + +void PyNotifier::StartSingle(units::second_t delay) { + std::scoped_lock lock(m_processMutex); + m_periodic = false; + m_period = delay; + m_expirationTime = Timer::GetFPGATimestamp() + m_period; + UpdateAlarm(); +} + +void PyNotifier::StartPeriodic(units::second_t period) { + std::scoped_lock lock(m_processMutex); + m_periodic = true; + m_period = period; + m_expirationTime = Timer::GetFPGATimestamp() + m_period; + UpdateAlarm(); +} + +void PyNotifier::Stop() { + std::scoped_lock lock(m_processMutex); + m_periodic = false; + int32_t status = 0; + HAL_CancelNotifierAlarm(m_notifier, &status); + FRC_CheckErrorStatus(status, "CancelNotifierAlarm"); +} + +void PyNotifier::UpdateAlarm(uint64_t triggerTime) { + int32_t status = 0; + // Return if we are being destructed, or were not created successfully + auto notifier = m_notifier.load(); + if (notifier == 0) { + return; + } + HAL_UpdateNotifierAlarm(notifier, triggerTime, &status); + FRC_CheckErrorStatus(status, "UpdateNotifierAlarm"); +} + +void PyNotifier::UpdateAlarm() { + UpdateAlarm(static_cast(m_expirationTime * 1e6)); +} + +bool PyNotifier::SetHALThreadPriority(bool realTime, int32_t priority) { + int32_t status = 0; + return HAL_SetNotifierThreadPriority(realTime, priority, &status); +} \ No newline at end of file diff --git a/wpilibc/src/main/python/wpilib/src/rpy/Notifier.h b/wpilibc/src/main/python/wpilib/src/rpy/Notifier.h new file mode 100644 index 0000000000..0e48165dc9 --- /dev/null +++ b/wpilibc/src/main/python/wpilib/src/rpy/Notifier.h @@ -0,0 +1,146 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace frc { + +class PyNotifier { + public: + /** + * Create a Notifier for timer event notification. + * + * @param handler The handler is called at the notification time which is set + * using StartSingle or StartPeriodic. + */ + explicit PyNotifier(std::function handler); + + // template + // PyNotifier(Callable &&f, Arg &&arg, Args &&... args) + // : PyNotifier(std::bind(std::forward(f), + // std::forward(arg), + // std::forward(args)...)) {} + + /** + * Free the resources for a timer event. + */ + virtual ~PyNotifier(); + + PyNotifier(PyNotifier &&rhs); + PyNotifier &operator=(PyNotifier &&rhs); + + /** + * Sets the name of the notifier. Used for debugging purposes only. + * + * @param name Name + */ + void SetName(std::string_view name); + + /** + * Change the handler function. + * + * @param handler Handler + */ + void SetCallback(std::function handler); + + /** + * Register for single event notification. + * + * A timer event is queued for a single event after the specified delay. + * + * @param delay Amount of time to wait before the handler is called. + */ + void StartSingle(units::second_t delay); + + /** + * Register for periodic event notification. + * + * A timer event is queued for periodic event notification. Each time the + * interrupt occurs, the event will be immediately requeued for the same time + * interval. + * + * @param period Period to call the handler starting one period + * after the call to this method. + */ + void StartPeriodic(units::second_t period); + + /** + * Stop timer events from occurring. + * + * Stop any repeating timer events from occurring. This will also remove any + * single notification events from the queue. + * + * If a timer-based call to the registered handler is in progress, this + * function will block until the handler call is complete. + */ + void Stop(); + + /** + * Sets the HAL notifier thread priority. + * + * The HAL notifier thread is responsible for managing the FPGA's notifier + * interrupt and waking up user's Notifiers when it's their time to run. + * Giving the HAL notifier thread real-time priority helps ensure the user's + * real-time Notifiers, if any, are notified to run in a timely manner. + * + * @param realTime Set to true to set a real-time priority, false for standard + * priority. + * @param priority Priority to set the thread to. For real-time, this is 1-99 + * with 99 being highest. For non-real-time, this is forced to + * 0. See "man 7 sched" for more details. + * @return True on success. + */ + static bool SetHALThreadPriority(bool realTime, int32_t priority); + +private: + /** + * Update the HAL alarm time. + * + * @param triggerTime the time at which the next alarm will be triggered + */ + void UpdateAlarm(uint64_t triggerTime); + + /** + * Update the HAL alarm time based on m_expirationTime. + */ + void UpdateAlarm(); + + // The thread waiting on the HAL alarm + py::object m_thread; + + // Held while updating process information + wpi::mutex m_processMutex; + + // HAL handle, atomic for proper destruction + std::atomic m_notifier{0}; + + // Address of the handler + std::function m_handler; + + // The absolute expiration time + units::second_t m_expirationTime = 0_s; + + // The relative time (either periodic or single) + units::second_t m_period = 0_s; + + // True if this is a periodic event + bool m_periodic = false; +}; + +} // namespace frc diff --git a/wpilibc/src/main/python/wpilib/src/rpy/SmartDashboardData.cpp b/wpilibc/src/main/python/wpilib/src/rpy/SmartDashboardData.cpp new file mode 100644 index 0000000000..13cf561f21 --- /dev/null +++ b/wpilibc/src/main/python/wpilib/src/rpy/SmartDashboardData.cpp @@ -0,0 +1,43 @@ + +#include "SmartDashboardData.h" + +namespace rpy { + +// +// Ensures that python objects added to the SmartDashboard have at least one +// reference to them +// +// All functions here must be called with the GIL held +// + +static py::dict &getSmartDashboardData() { + static py::dict data; + return data; +} + +void addSmartDashboardData(py::str &key, std::shared_ptr data) { + auto &sdData = getSmartDashboardData(); + sdData[key] = py::cast(data); +} + +void clearSmartDashboardData() { + auto &sdData = getSmartDashboardData(); + if (sdData) { + sdData.clear(); + } +} + +void destroySmartDashboardData() { + auto &sdData = getSmartDashboardData(); + if (sdData) { + sdData.clear(); + // force the dictionary to be deleted otherwise it'll crash when libc++ + // is unwinding static objects after interpreter destruction + sdData.dec_ref(); + // release our reference to this otherwise the destructor will try to + // delete a non-existant PyObject* after interpreter destruction + sdData.release(); + } +} + +} // namespace rpy diff --git a/wpilibc/src/main/python/wpilib/src/rpy/SmartDashboardData.h b/wpilibc/src/main/python/wpilib/src/rpy/SmartDashboardData.h new file mode 100644 index 0000000000..8323ebd112 --- /dev/null +++ b/wpilibc/src/main/python/wpilib/src/rpy/SmartDashboardData.h @@ -0,0 +1,17 @@ + +#pragma once + +#include +#include + +namespace rpy { + +// +// These functions must be called with the GIL held +// + +void addSmartDashboardData(py::str &key, std::shared_ptr data); +void clearSmartDashboardData(); +void destroySmartDashboardData(); + +} // namespace rpy \ No newline at end of file diff --git a/wpilibc/src/main/python/wpilib/sysid/__init__.py b/wpilibc/src/main/python/wpilib/sysid/__init__.py new file mode 100644 index 0000000000..476248e28d --- /dev/null +++ b/wpilibc/src/main/python/wpilib/sysid/__init__.py @@ -0,0 +1,4 @@ +# autogenerated by 'semiwrap create-imports wpilib.sysid wpilib._wpilib.sysid' +from .._wpilib.sysid import State, SysIdRoutineLog + +__all__ = ["State", "SysIdRoutineLog"] diff --git a/wpilibc/src/test/python/conftest.py b/wpilibc/src/test/python/conftest.py new file mode 100644 index 0000000000..88728aa91f --- /dev/null +++ b/wpilibc/src/test/python/conftest.py @@ -0,0 +1,31 @@ +import logging + +import pytest +import ntcore +import wpilib +from wpilib.simulation._simulation import _resetWpilibSimulationData + + +@pytest.fixture +def cfg_logging(caplog): + caplog.set_level(logging.INFO) + + +@pytest.fixture(scope="function") +def wpilib_state(): + try: + yield None + finally: + _resetWpilibSimulationData() + + +@pytest.fixture(scope="function") +def nt(cfg_logging, wpilib_state): + instance = ntcore.NetworkTableInstance.getDefault() + instance.startLocal() + + try: + yield instance + finally: + instance.stopLocal() + instance._reset() diff --git a/wpilibc/src/test/python/test_alert.py b/wpilibc/src/test/python/test_alert.py new file mode 100644 index 0000000000..ceecc3d879 --- /dev/null +++ b/wpilibc/src/test/python/test_alert.py @@ -0,0 +1,223 @@ +import typing as T + +import pytest + +from ntcore import NetworkTableInstance +from wpilib import Alert, SmartDashboard +from wpilib.simulation import pauseTiming, resumeTiming, stepTiming + +AlertType = Alert.AlertType + + +@pytest.fixture(scope="function") +def group_name(nt, request): + + group_name = f"AlertTest_{request.node.name}" + yield group_name + + SmartDashboard.updateValues() + assert len(get_active_alerts(nt, group_name, AlertType.kError)) == 0 + assert len(get_active_alerts(nt, group_name, AlertType.kWarning)) == 0 + assert len(get_active_alerts(nt, group_name, AlertType.kInfo)) == 0 + + +def get_subscriber_for_type( + nt: NetworkTableInstance, group_name: str, alert_type: AlertType +): + subtable_name = { + AlertType.kError: "errors", + AlertType.kWarning: "warnings", + AlertType.kInfo: "infos", + }.get(alert_type, "unknown") + topic = f"/SmartDashboard/{group_name}/{subtable_name}" + return nt.getStringArrayTopic(topic).subscribe([]) + + +def get_active_alerts( + nt: NetworkTableInstance, group_name: str, alert_type: AlertType +) -> T.List[str]: + SmartDashboard.updateValues() + with get_subscriber_for_type(nt, group_name, alert_type) as sub: + return sub.get() + + +def is_alert_active( + nt: NetworkTableInstance, group_name: str, text: str, alert_type: AlertType +): + active_alerts = get_active_alerts(nt, group_name, alert_type) + return text in active_alerts + + +def assert_state( + nt: NetworkTableInstance, + group_name: str, + alert_type: AlertType, + expected_state: T.List[str], +): + assert expected_state == get_active_alerts(nt, group_name, alert_type) + + +def test_set_unset_single(nt, group_name): + with Alert(group_name, "one", AlertType.kError) as one: + + assert not is_alert_active(nt, group_name, "one", AlertType.kError) + assert not is_alert_active(nt, group_name, "two", AlertType.kInfo) + + one.set(True) + assert is_alert_active(nt, group_name, "one", AlertType.kError) + + one.set(True) + assert is_alert_active(nt, group_name, "one", AlertType.kError) + + one.set(False) + assert not is_alert_active(nt, group_name, "one", AlertType.kError) + + +def test_set_unset_multiple(nt, group_name): + with ( + Alert(group_name, "one", AlertType.kError) as one, + Alert(group_name, "two", AlertType.kInfo) as two, + ): + + assert not is_alert_active(nt, group_name, "one", AlertType.kError) + assert not is_alert_active(nt, group_name, "two", AlertType.kInfo) + + one.set(True) + assert is_alert_active(nt, group_name, "one", AlertType.kError) + assert not is_alert_active(nt, group_name, "two", AlertType.kInfo) + + one.set(True) + two.set(True) + assert is_alert_active(nt, group_name, "one", AlertType.kError) + assert is_alert_active(nt, group_name, "two", AlertType.kInfo) + + one.set(False) + assert not is_alert_active(nt, group_name, "one", AlertType.kError) + assert is_alert_active(nt, group_name, "two", AlertType.kInfo) + + +def test_set_is_idempotent(nt, group_name): + group_name = group_name + with ( + Alert(group_name, "A", AlertType.kInfo) as a, + Alert(group_name, "B", AlertType.kInfo) as b, + Alert(group_name, "C", AlertType.kInfo) as c, + ): + + a.set(True) + b.set(True) + c.set(True) + + start_state = get_active_alerts(nt, group_name, AlertType.kInfo) + assert set(start_state) == {"A", "B", "C"} + + b.set(True) + assert_state(nt, group_name, AlertType.kInfo, start_state) + + a.set(True) + assert_state(nt, group_name, AlertType.kInfo, start_state) + + +def test_close_unsets_alert(nt, group_name): + group_name = group_name + with Alert(group_name, "alert", AlertType.kWarning) as alert: + alert.set(True) + assert is_alert_active(nt, group_name, "alert", AlertType.kWarning) + assert not is_alert_active(nt, group_name, "alert", AlertType.kWarning) + + +def test_set_text_while_unset(nt, group_name): + group_name = group_name + with Alert(group_name, "BEFORE", AlertType.kInfo) as alert: + assert alert.getText() == "BEFORE" + alert.set(True) + assert is_alert_active(nt, group_name, "BEFORE", AlertType.kInfo) + alert.set(False) + assert not is_alert_active(nt, group_name, "BEFORE", AlertType.kInfo) + alert.setText("AFTER") + assert alert.getText() == "AFTER" + alert.set(True) + assert not is_alert_active(nt, group_name, "BEFORE", AlertType.kInfo) + assert is_alert_active(nt, group_name, "AFTER", AlertType.kInfo) + + +def test_set_text_while_set(nt, group_name): + with Alert(group_name, "BEFORE", AlertType.kInfo) as alert: + assert alert.getText() == "BEFORE" + alert.set(True) + assert is_alert_active(nt, group_name, "BEFORE", AlertType.kInfo) + alert.setText("AFTER") + assert alert.getText() == "AFTER" + assert not is_alert_active(nt, group_name, "BEFORE", AlertType.kInfo) + assert is_alert_active(nt, group_name, "AFTER", AlertType.kInfo) + + +def test_set_text_does_not_affect_sort(nt, group_name): + pauseTiming() + try: + with ( + Alert(group_name, "A", AlertType.kInfo) as a, + Alert(group_name, "B", AlertType.kInfo) as b, + Alert(group_name, "C", AlertType.kInfo) as c, + ): + + a.set(True) + stepTiming(1) + b.set(True) + stepTiming(1) + c.set(True) + + expected_state = get_active_alerts(nt, group_name, AlertType.kInfo) + expected_state[expected_state.index("B")] = "AFTER" + + b.setText("AFTER") + assert_state(nt, group_name, AlertType.kInfo, expected_state) + finally: + resumeTiming() + + +def test_sort_order(nt, group_name): + pauseTiming() + try: + with ( + Alert(group_name, "A", AlertType.kInfo) as a, + Alert(group_name, "B", AlertType.kInfo) as b, + Alert(group_name, "C", AlertType.kInfo) as c, + ): + + a.set(True) + assert_state(nt, group_name, AlertType.kInfo, ["A"]) + + stepTiming(1) + b.set(True) + assert_state(nt, group_name, AlertType.kInfo, ["B", "A"]) + + stepTiming(1) + c.set(True) + assert_state(nt, group_name, AlertType.kInfo, ["C", "B", "A"]) + + stepTiming(1) + c.set(False) + assert_state(nt, group_name, AlertType.kInfo, ["B", "A"]) + + stepTiming(1) + c.set(True) + assert_state(nt, group_name, AlertType.kInfo, ["C", "B", "A"]) + + stepTiming(1) + a.set(False) + assert_state(nt, group_name, AlertType.kInfo, ["C", "B"]) + + stepTiming(1) + b.set(False) + assert_state(nt, group_name, AlertType.kInfo, ["C"]) + + stepTiming(1) + b.set(True) + assert_state(nt, group_name, AlertType.kInfo, ["B", "C"]) + + stepTiming(1) + a.set(True) + assert_state(nt, group_name, AlertType.kInfo, ["A", "B", "C"]) + finally: + resumeTiming() diff --git a/wpilibc/src/test/python/test_datalogmanager.py b/wpilibc/src/test/python/test_datalogmanager.py new file mode 100644 index 0000000000..7d800696d8 --- /dev/null +++ b/wpilibc/src/test/python/test_datalogmanager.py @@ -0,0 +1,13 @@ +import pathlib +import wpilib +import pytest +import sys + + +@pytest.mark.skipif(sys.platform == "darwin", reason="DataLogManager crashes on exit") +def test_get_log(tmp_path: pathlib.Path): + log_dir = tmp_path / "wpilogs" + log_dir.mkdir() + wpilib.DataLogManager.start(str(log_dir)) + log = wpilib.DataLogManager.getLog() + assert log is not None diff --git a/wpilibc/src/test/python/test_joystick.py b/wpilibc/src/test/python/test_joystick.py new file mode 100644 index 0000000000..f58dcbaa6b --- /dev/null +++ b/wpilibc/src/test/python/test_joystick.py @@ -0,0 +1,144 @@ +import math + +from wpilib import Joystick +from wpilib.simulation import JoystickSim + + +def test_getX() -> None: + joy = Joystick(1) + joysim = JoystickSim(joy) + + joysim.setX(0.25) + joysim.notifyNewData() + assert math.isclose(joy.getX(), 0.25) + + joysim.setX(0) + joysim.notifyNewData() + assert math.isclose(joy.getX(), 0.0) + + +def test_getY() -> None: + joy = Joystick(1) + joysim = JoystickSim(joy) + + joysim.setY(0.25) + joysim.notifyNewData() + assert math.isclose(joy.getY(), 0.25) + + joysim.setY(0) + joysim.notifyNewData() + assert math.isclose(joy.getY(), 0.0) + + +def test_getZ() -> None: + joy = Joystick(1) + joysim = JoystickSim(joy) + + joysim.setZ(0.25) + joysim.notifyNewData() + assert math.isclose(joy.getZ(), 0.25) + + joysim.setZ(0) + joysim.notifyNewData() + assert math.isclose(joy.getZ(), 0.0) + + +def test_getTwist() -> None: + joy = Joystick(1) + joysim = JoystickSim(joy) + + joysim.setTwist(0.25) + joysim.notifyNewData() + assert math.isclose(joy.getTwist(), 0.25) + + joysim.setTwist(0) + joysim.notifyNewData() + assert math.isclose(joy.getTwist(), 0.0) + + +def test_getThrottle() -> None: + joy = Joystick(1) + joysim = JoystickSim(joy) + + joysim.setThrottle(0.25) + joysim.notifyNewData() + assert math.isclose(joy.getThrottle(), 0.25) + + joysim.setThrottle(0) + joysim.notifyNewData() + assert math.isclose(joy.getThrottle(), 0.0) + + +def test_getTrigger() -> None: + joy = Joystick(1) + joysim = JoystickSim(joy) + + joysim.setTrigger(True) + joysim.notifyNewData() + assert joy.getTrigger() + + joysim.setTrigger(False) + joysim.notifyNewData() + assert not joy.getTrigger() + + +def test_getTop() -> None: + joy = Joystick(1) + joysim = JoystickSim(joy) + + joysim.setTop(True) + joysim.notifyNewData() + assert joy.getTop() + + joysim.setTop(False) + joysim.notifyNewData() + assert not joy.getTop() + + +def test_getMagnitude() -> None: + joy = Joystick(1) + joysim = JoystickSim(joy) + + # X Only + joysim.setX(0.5) + joysim.setY(0.0) + joysim.notifyNewData() + assert math.isclose(joy.getMagnitude(), 0.5) + + # Y Only + joysim.setX(0.0) + joysim.setY(-0.5) + joysim.notifyNewData() + assert math.isclose(joy.getMagnitude(), 0.5) + + # Both + joysim.setX(0.5) + joysim.setY(-0.5) + joysim.notifyNewData() + assert math.isclose(joy.getMagnitude(), 0.70710678118) + + +def test_getDirection() -> None: + joy = Joystick(1) + joysim = JoystickSim(joy) + + # X Only + joysim.setX(0.5) + joysim.setY(0.0) + joysim.notifyNewData() + assert math.isclose(joy.getDirectionDegrees(), 90) + assert math.isclose(joy.getDirectionRadians(), math.radians(90)) + + # Y Only + joysim.setX(0.0) + joysim.setY(-0.5) + joysim.notifyNewData() + assert math.isclose(joy.getDirectionDegrees(), 0) + assert math.isclose(joy.getDirectionRadians(), math.radians(0)) + + # Both + joysim.setX(0.5) + joysim.setY(-0.5) + joysim.notifyNewData() + assert math.isclose(joy.getDirectionDegrees(), 45) + assert math.isclose(joy.getDirectionRadians(), math.radians(45)) diff --git a/wpilibc/src/test/python/test_mechanism2d.py b/wpilibc/src/test/python/test_mechanism2d.py new file mode 100644 index 0000000000..82a9abb5eb --- /dev/null +++ b/wpilibc/src/test/python/test_mechanism2d.py @@ -0,0 +1,11 @@ +from wpilib import Mechanism2d + + +def test_create_mechanism(): + m = Mechanism2d(100, 100) + r1 = m.getRoot("r1", 10, 10) + l1 = r1.appendLigament("l1", 4, 3) + l2 = l1.appendLigament("l2", 4, 3) + assert l2 is not None + + # TODO... check that they do something? diff --git a/wpilibc/src/test/python/test_notifier.py b/wpilibc/src/test/python/test_notifier.py new file mode 100644 index 0000000000..a4fa479076 --- /dev/null +++ b/wpilibc/src/test/python/test_notifier.py @@ -0,0 +1,62 @@ +# Copyright (c) FIRST and other WPILib contributors. +# Open Source Software; you can modify and/or share it under the terms of +# the WPILib BSD license file in the root directory of this project. + +import threading + +import pytest + +from wpilib import Notifier +from wpilib.simulation import pauseTiming, restartTiming, resumeTiming, stepTiming + +# These tests fail because of race conditions in simulation +if False: + + class AtomicInteger: + def __init__(self) -> None: + self.lock = threading.Lock() + self.val = 0 + + def get(self) -> int: + with self.lock: + return self.val + + def getAndIncrement(self) -> int: + with self.lock: + val = self.val + self.val += 1 + return val + + @pytest.fixture + def counter(): + return AtomicInteger() + + @pytest.fixture + def notifier(counter): + pauseTiming() + restartTiming() + n = Notifier(counter.getAndIncrement) + yield n + del n + resumeTiming() + + @pytest.mark.xfail(strict=False) + def test_testStartPeriodicAndStop(counter: AtomicInteger, notifier: Notifier): + notifier.startPeriodic(1.0) + + stepTiming(10) + + notifier.stop() + assert counter.get() == 10 + + stepTiming(3) + + assert counter.get() == 10 + + @pytest.mark.xfail(strict=False) + def test_testStartSingle(counter, notifier): + notifier.startSingle(1.0) + + stepTiming(10) + + assert counter.get() == 1 diff --git a/wpilibc/src/test/python/test_sendable_chooser.py b/wpilibc/src/test/python/test_sendable_chooser.py new file mode 100644 index 0000000000..0976516441 --- /dev/null +++ b/wpilibc/src/test/python/test_sendable_chooser.py @@ -0,0 +1,55 @@ +import pytest + +from ntcore import NetworkTableInstance +from wpilib import SendableChooser, SmartDashboard + + +@pytest.fixture +def chooser() -> SendableChooser: + chooser = SendableChooser() + for i in range(1, 4): + chooser.addOption(str(i), i) + return chooser + + +@pytest.mark.parametrize("value", [0, 1, 2, 3]) +def test_returns_selected( + nt: NetworkTableInstance, chooser: SendableChooser, value: int +): + chooser.setDefaultOption("0", 0) + + with nt.getStringTopic( + "/SmartDashboard/ReturnsSelectedChooser/selected" + ).publish() as pub: + SmartDashboard.putData("ReturnsSelectedChooser", chooser) + SmartDashboard.updateValues() + print("set", value) + pub.set(str(value)) + SmartDashboard.updateValues() + print("get", chooser.getSelected()) + assert value == chooser.getSelected() + + +def test_default_is_returned_on_no_select(chooser: SendableChooser): + chooser.setDefaultOption("4", 4) + assert 4 == chooser.getSelected() + + +def test_default_constructable_is_returned_on_no_select_and_no_default( + chooser: SendableChooser, +): + assert chooser.getSelected() is None + + +def test_change_listener(nt: NetworkTableInstance, chooser: SendableChooser): + current_val = [0] + + def on_change(val): + current_val[0] = val + + chooser.onChange(on_change) + SmartDashboard.putData("ChangeListenerChooser", chooser) + SmartDashboard.updateValues() + SmartDashboard.putString("ChangeListenerChooser/selected", "3") + SmartDashboard.updateValues() + assert 3 == current_val[0] diff --git a/wpilibc/src/test/python/test_wpilib.py b/wpilibc/src/test/python/test_wpilib.py new file mode 100644 index 0000000000..2882de3148 --- /dev/null +++ b/wpilibc/src/test/python/test_wpilib.py @@ -0,0 +1,43 @@ +import pytest +import re +import weakref + +import wpilib + + +def test_sendable_chooser(): + chooser = wpilib.SendableChooser() + assert chooser.getSelected() is None + + chooser.setDefaultOption("option", True) + assert chooser.getSelected() is True + + +def test_smart_dashboard_putdata(): + t = wpilib.Talon(4) + ref = weakref.ref(t) + wpilib.SmartDashboard.putData("talon", t) + del t + assert bool(ref) is True + assert wpilib.SmartDashboard.getData("talon") is ref() + + +def test_motorcontrollergroup(): + t1 = wpilib.Talon(7) + t2 = wpilib.Talon(8) + g = wpilib.MotorControllerGroup(t1, t2) + + g.set(1) + assert t1.get() == pytest.approx(1) + assert t2.get() == pytest.approx(1) + + g.set(-1) + assert t1.get() == pytest.approx(-1) + assert t2.get() == pytest.approx(-1) + + +def test_motorcontrollergroup_error(): + with pytest.raises( + TypeError, match=re.escape("Argument 1 must be a MotorController (got '1')") + ): + wpilib.MotorControllerGroup(1) diff --git a/wpilibc/src/test/python/test_wpilib_drive.py b/wpilibc/src/test/python/test_wpilib_drive.py new file mode 100644 index 0000000000..af1d2a672b --- /dev/null +++ b/wpilibc/src/test/python/test_wpilib_drive.py @@ -0,0 +1,5 @@ +import wpilib.drive + + +def test_wpilib_drive(): + pass diff --git a/wpilibc/src/test/python/test_wpilib_interfaces.py b/wpilibc/src/test/python/test_wpilib_interfaces.py new file mode 100644 index 0000000000..aff2714c4e --- /dev/null +++ b/wpilibc/src/test/python/test_wpilib_interfaces.py @@ -0,0 +1,5 @@ +import wpilib.interfaces + + +def test_wpilib_interfaces(): + pass diff --git a/wpilibc/src/test/python/test_wpilib_simulation.py b/wpilibc/src/test/python/test_wpilib_simulation.py new file mode 100644 index 0000000000..561b2bb05b --- /dev/null +++ b/wpilibc/src/test/python/test_wpilib_simulation.py @@ -0,0 +1,5 @@ +import wpilib.simulation + + +def test_wpilib_simulation(): + pass diff --git a/wpimath/.styleguide b/wpimath/.styleguide index 80b4f407dd..7f3f80be8f 100644 --- a/wpimath/.styleguide +++ b/wpimath/.styleguide @@ -23,6 +23,9 @@ generatedFileExclude { src/test/native/include/drake/ src/generated/main/java/edu/wpi/first/math/proto src/generated/main/native/cpp + + src/main/python/ + src/test/python/ } repoRootNameOverride { diff --git a/wpimath/src/main/python/README.md b/wpimath/src/main/python/README.md new file mode 100644 index 0000000000..57505085e4 --- /dev/null +++ b/wpimath/src/main/python/README.md @@ -0,0 +1,7 @@ +robotpy-wpimath +=============== + +Python wrappers for WPILib's wpimath library. + +* Installation instructions can be found in the [RobotPy documentation](https://robotpy.readthedocs.io/en/latest/getting_started.html) +* Documentation can be found at [readthedocs](https://robotpy.readthedocs.io/projects/wpimath/en/stable/api.html) diff --git a/wpimath/src/main/python/native-pyproject.toml b/wpimath/src/main/python/native-pyproject.toml new file mode 100644 index 0000000000..efd5da56e9 --- /dev/null +++ b/wpimath/src/main/python/native-pyproject.toml @@ -0,0 +1,39 @@ +[build-system] +build-backend = "hatchling.build" +requires = [ + "hatchling", + "hatch-nativelib~=0.2.0", + "hatch-robotpy~=0.2.1", + "robotpy-native-wpiutil==2027.0.0a2", +] + +[project] +name = "robotpy-native-wpimath" +version = "2027.0.0a2" +description = "WPILib Math Library" +license = "BSD-3-Clause" + +dependencies = [ + "robotpy-native-wpiutil==2027.0.0a2", +] + +[tool.hatch.build.targets.wheel] +packages = ["src/native"] + +[[tool.hatch.build.hooks.robotpy.maven_lib_download]] +artifact_id = "wpimath-cpp" +group_id = "edu.wpi.first.wpimath" +repo_url = "https://frcmaven.wpi.edu/artifactory/release-2027" +version = "2027.0.0-alpha-2" + +extract_to = "src/native/wpimath" +libs = ["wpimath"] + +[[tool.hatch.build.hooks.nativelib.pcfile]] +pcfile = "src/native/wpimath/robotpy-native-wpimath.pc" +name = "wpimath" + +includedir = "src/native/wpimath/include" +libdir = "src/native/wpimath/lib" +shared_libraries = ["wpimath"] +requires = ["robotpy-native-wpiutil"] diff --git a/wpimath/src/main/python/pyproject.toml b/wpimath/src/main/python/pyproject.toml new file mode 100644 index 0000000000..0ac94934b4 --- /dev/null +++ b/wpimath/src/main/python/pyproject.toml @@ -0,0 +1,1638 @@ +[build-system] +build-backend = "hatchling.build" +requires = [ + "semiwrap~=0.1.7", + "hatch-meson~=0.1.0b2", + "hatch-robotpy~=0.2.1", + "hatchling", + "robotpy-native-wpimath==2027.0.0a2", + "robotpy-wpiutil==2027.0.0a2", + # "numpy", # broken in raspbian CI +] + +[project] +name = "robotpy-wpimath" +version = "2027.0.0a2" +description = "Binary wrapper for FRC WPIMath library" +authors = [ + {name = "RobotPy Development Team", email = "robotpy@googlegroups.com"}, +] +license = "BSD-3-Clause" +dependencies = [ + "robotpy-native-wpimath==2027.0.0a2", + "robotpy-wpiutil==2027.0.0a2", +] + +[project.urls] +"Source code" = "https://github.com/robotpy/mostrobotpy" + + +[tool.hatch.build.hooks.robotpy] +version_file = "wpimath/version.py" + +[tool.hatch.build.hooks.semiwrap] + +[tool.hatch.build.hooks.meson] + +[tool.hatch.build.targets.wheel] +packages = ["wpimath"] + + +[tool.semiwrap] +update_init = [ + "wpimath", + "wpimath.controller wpimath._controls._controls.controller", + "wpimath.estimator wpimath._controls._controls.estimator", + "wpimath.filter", + "wpimath.geometry", + "wpimath.optimization wpimath._controls._controls.optimization", + "wpimath.path wpimath._controls._controls.path", + "wpimath.spline", + "wpimath.system wpimath._controls._controls.system", + "wpimath.trajectory wpimath._controls._controls.trajectory", + "wpimath.trajectory.constraint wpimath._controls._controls.constraint", +] + +scan_headers_ignore = [ + + "frc/ct_matrix.h", + "frc/DARE.h", + "frc/EigenCore.h", + "frc/StateSpaceUtil.h", + + "frc/fmt/Eigen.h", + + "frc/estimator/AngleStatistics.h", + "frc/estimator/KalmanFilterLatencyCompensator.h", + "frc/estimator/MerweScaledSigmaPoints.h", + "frc/estimator/MerweUKF.h", + "frc/estimator/S3SigmaPoints.h", + "frc/estimator/S3UKF.h", + "frc/estimator/SigmaPoints.h", + "frc/estimator/SteadyStateKalmanFilter.h", + "frc/estimator/UnscentedKalmanFilter.h", + "frc/estimator/UnscentedTransform.h", + + "frc/system/Discretization.h", + "frc/system/NumericalIntegration.h", + "frc/system/NumericalJacobian.h", + + "frc/proto/*", + "frc/*/proto/*", + + "frc/struct/*", + "frc/*/struct/*", + + "[eE]igen/*", + + "gcem.hpp", + "gcem_incl/*", + + "gch/*", + "sleipnir/*", + "type_casters/*", + "unsupported/*", + "units/*", + "wpimath/protobuf/*", + + "wpimath/MathShared.h", + + "rpy/geometryToString.h", + "PyTrajectoryConstraint.h", + "_units_base_type_caster.h", +] + +[tool.semiwrap.export_type_casters.wpimath-casters] +pypackage = "wpimath" +requires = ["wpiutil"] +includedir = ["wpimath/_impl/src", "wpimath/_impl/src/type_casters"] + + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_acceleration_type_caster.h" +types = [ + "units::feet_per_second_squared_t", + "units::meters_per_second_squared_t", + "units::standard_gravity_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_acceleration_type_caster.h" +types = [ + "units::feet_per_second_squared", + "units::feet_per_second_squared", + "units::meters_per_second_squared", + "units::meters_per_second_squared", + "units::standard_gravity", + "units::standard_gravity", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_angle_type_caster.h" +types = [ + "units::arcminute_t", + "units::arcsecond_t", + "units::degree_t", + "units::gradian_t", + "units::kiloradian_t", + "units::microradian_t", + "units::milliarcsecond_t", + "units::milliradian_t", + "units::nanoradian_t", + "units::radian_t", + "units::turn_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_angle_type_caster.h" +types = [ + "units::arcminute", + "units::arcminutes", + "units::arcsecond", + "units::arcseconds", + "units::degree", + "units::degrees", + "units::gradian", + "units::gradians", + "units::kiloradian", + "units::kiloradians", + "units::microradian", + "units::microradians", + "units::milliarcsecond", + "units::milliarcseconds", + "units::milliradian", + "units::milliradians", + "units::nanoradian", + "units::nanoradians", + "units::radian", + "units::radians", + "units::turn", + "units::turns", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_angular_acceleration_type_caster.h" +types = [ + "units::radians_per_second_squared_t", + "units::degrees_per_second_squared_t", + "units::turns_per_second_squared_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_angular_acceleration_type_caster.h" +types = [ + "units::radians_per_second_squared", + "units::degrees_per_second_squared", + "units::turns_per_second_squared", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_angular_velocity_type_caster.h" +types = [ + "units::degrees_per_second_t", + "units::milliarcseconds_per_year_t", + "units::radians_per_second_t", + "units::turns_per_second_t", + "units::revolutions_per_minute_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_angular_velocity_type_caster.h" +types = [ + "units::degrees_per_second", + "units::milliarcseconds_per_year", + "units::radians_per_second", + "units::turns_per_second", + "units::revolutions_per_minute", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_area_type_caster.h" +types = [ + "units::acre_t", + "units::hectare_t", + "units::square_foot_t", + "units::square_inch_t", + "units::square_kilometer_t", + "units::square_meter_t", + "units::square_mile_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_area_type_caster.h" +types = [ + "units::acre", + "units::acres", + "units::hectare", + "units::hectares", + "units::square_feet", + "units::square_foot", + "units::square_inch", + "units::square_inches", + "units::square_kilometer", + "units::square_kilometers", + "units::square_meter", + "units::square_meters", + "units::square_mile", + "units::square_miles", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_capacitance_type_caster.h" +types = [ + "units::farad_t", + "units::kilofarad_t", + "units::microfarad_t", + "units::millifarad_t", + "units::nanofarad_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_capacitance_type_caster.h" +types = [ + "units::farad", + "units::farads", + "units::kilofarad", + "units::kilofarads", + "units::microfarad", + "units::microfarads", + "units::millifarad", + "units::millifarads", + "units::nanofarad", + "units::nanofarads", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_charge_type_caster.h" +types = [ + "units::ampere_hour_t", + "units::coulomb_t", + "units::kiloampere_hour_t", + "units::kilocoulomb_t", + "units::microampere_hour_t", + "units::microcoulomb_t", + "units::milliampere_hour_t", + "units::millicoulomb_t", + "units::nanoampere_hour_t", + "units::nanocoulomb_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_charge_type_caster.h" +types = [ + "units::ampere_hour", + "units::ampere_hours", + "units::coulomb", + "units::coulombs", + "units::kiloampere_hour", + "units::kiloampere_hours", + "units::kilocoulomb", + "units::kilocoulombs", + "units::microampere_hour", + "units::microampere_hours", + "units::microcoulomb", + "units::microcoulombs", + "units::milliampere_hour", + "units::milliampere_hours", + "units::millicoulomb", + "units::millicoulombs", + "units::nanoampere_hour", + "units::nanoampere_hours", + "units::nanocoulomb", + "units::nanocoulombs", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_concentration_type_caster.h" +types = [ + "units::percent_t", + "units::ppb_t", + "units::ppm_t", + "units::ppt_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_concentration_type_caster.h" +types = [ + "units::parts_per_billion", + "units::parts_per_million", + "units::parts_per_trillion", + "units::percent", + "units::percent", + "units::ppb", + "units::ppm", + "units::ppt", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_conductance_type_caster.h" +types = [ + "units::kilosiemens_t", + "units::microsiemens_t", + "units::millisiemens_t", + "units::nanosiemens_t", + "units::siemens_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_conductance_type_caster.h" +types = [ + "units::kilosiemens", + "units::kilosiemens", + "units::microsiemens", + "units::microsiemens", + "units::millisiemens", + "units::millisiemens", + "units::nanosiemens", + "units::nanosiemens", + "units::siemens", + "units::siemens", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_current_type_caster.h" +types = [ + "units::ampere_t", + "units::kiloampere_t", + "units::microampere_t", + "units::milliampere_t", + "units::nanoampere_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_current_type_caster.h" +types = [ + "units::ampere", + "units::amperes", + "units::kiloampere", + "units::kiloamperes", + "units::microampere", + "units::microamperes", + "units::milliampere", + "units::milliamperes", + "units::nanoampere", + "units::nanoamperes", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_data_type_caster.h" +types = [ + "units::exabit_t", + "units::exabyte_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_data_type_caster.h" +types = [ + "units::exabit", + "units::exabits", + "units::exabyte", + "units::exabytes", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_data_transfer_rate_type_caster.h" +types = [ + "units::exabits_per_second_t", + "units::exabytes_per_second_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_data_transfer_rate_type_caster.h" +types = [ + "units::exabits_per_second", + "units::exabits_per_second", + "units::exabytes_per_second", + "units::exabytes_per_second", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_density_type_caster.h" +types = [ + "units::grams_per_milliliter_t", + "units::kilograms_per_cubic_meter_t", + "units::kilograms_per_liter_t", + "units::ounces_per_cubic_foot_t", + "units::ounces_per_cubic_inch_t", + "units::ounces_per_gallon_t", + "units::pounds_per_cubic_foot_t", + "units::pounds_per_cubic_inch_t", + "units::pounds_per_gallon_t", + "units::slugs_per_cubic_foot_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_density_type_caster.h" +types = [ + "units::grams_per_milliliter", + "units::grams_per_milliliter", + "units::kilograms_per_cubic_meter", + "units::kilograms_per_cubic_meter", + "units::kilograms_per_liter", + "units::kilograms_per_liter", + "units::ounces_per_cubic_foot", + "units::ounces_per_cubic_foot", + "units::ounces_per_cubic_inch", + "units::ounces_per_cubic_inch", + "units::ounces_per_gallon", + "units::ounces_per_gallon", + "units::pounds_per_cubic_foot", + "units::pounds_per_cubic_foot", + "units::pounds_per_cubic_inch", + "units::pounds_per_cubic_inch", + "units::pounds_per_gallon", + "units::pounds_per_gallon", + "units::slugs_per_cubic_foot", + "units::slugs_per_cubic_foot", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_energy_type_caster.h" +types = [ + "units::british_thermal_unit_59_t", + "units::british_thermal_unit_iso_t", + "units::british_thermal_unit_t", + "units::calorie_t", + "units::foot_pound_t", + "units::joule_t", + "units::kilocalorie_t", + "units::kilojoule_t", + "units::kilowatt_hour_t", + "units::microcalorie_t", + "units::microjoule_t", + "units::millicalorie_t", + "units::millijoule_t", + "units::nanocalorie_t", + "units::nanojoule_t", + "units::therm_t", + "units::watt_hour_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_energy_type_caster.h" +types = [ + "units::british_thermal_unit", + "units::british_thermal_unit_59", + "units::british_thermal_unit_iso", + "units::british_thermal_units", + "units::british_thermal_units_59", + "units::british_thermal_units_iso", + "units::calorie", + "units::calories", + "units::foot_pound", + "units::foot_pounds", + "units::joule", + "units::joules", + "units::kilocalorie", + "units::kilocalories", + "units::kilojoule", + "units::kilojoules", + "units::kilowatt_hour", + "units::kilowatt_hours", + "units::microcalorie", + "units::microcalories", + "units::microjoule", + "units::microjoules", + "units::millicalorie", + "units::millicalories", + "units::millijoule", + "units::millijoules", + "units::nanocalorie", + "units::nanocalories", + "units::nanojoule", + "units::nanojoules", + "units::therm", + "units::therms", + "units::watt_hour", + "units::watt_hours", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_force_type_caster.h" +types = [ + "units::dyne_t", + "units::kilonewton_t", + "units::kilopond_t", + "units::micronewton_t", + "units::millinewton_t", + "units::nanonewton_t", + "units::newton_t", + "units::pound_t", + "units::poundal_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_force_type_caster.h" +types = [ + "units::dyne", + "units::dynes", + "units::kilonewton", + "units::kilonewtons", + "units::kilopond", + "units::kiloponds", + "units::micronewton", + "units::micronewtons", + "units::millinewton", + "units::millinewtons", + "units::nanonewton", + "units::nanonewtons", + "units::newton", + "units::newtons", + "units::pound", + "units::poundal", + "units::poundals", + "units::pounds", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_frequency_type_caster.h" +types = [ + "units::hertz_t", + "units::kilohertz_t", + "units::microhertz_t", + "units::millihertz_t", + "units::nanohertz_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_frequency_type_caster.h" +types = [ + "units::hertz", + "units::hertz", + "units::kilohertz", + "units::kilohertz", + "units::microhertz", + "units::microhertz", + "units::millihertz", + "units::millihertz", + "units::nanohertz", + "units::nanohertz", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_illuminance_type_caster.h" +types = [ + "units::footcandle_t", + "units::kilolux_t", + "units::lumens_per_square_inch_t", + "units::lux_t", + "units::microlux_t", + "units::millilux_t", + "units::nanolux_t", + "units::phot_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_illuminance_type_caster.h" +types = [ + "units::footcandle", + "units::footcandles", + "units::kilolux", + "units::kiloluxes", + "units::lumens_per_square_inch", + "units::lumens_per_square_inch", + "units::lux", + "units::luxes", + "units::microlux", + "units::microluxes", + "units::millilux", + "units::milliluxes", + "units::nanolux", + "units::nanoluxes", + "units::phot", + "units::phots", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_impedance_type_caster.h" +types = [ + "units::kiloohm_t", + "units::microohm_t", + "units::milliohm_t", + "units::nanoohm_t", + "units::ohm_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_impedance_type_caster.h" +types = [ + "units::kiloohm", + "units::kiloohms", + "units::microohm", + "units::microohms", + "units::milliohm", + "units::milliohms", + "units::nanoohm", + "units::nanoohms", + "units::ohm", + "units::ohms", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_inductance_type_caster.h" +types = [ + "units::henry_t", + "units::kilohenry_t", + "units::microhenry_t", + "units::millihenry_t", + "units::nanohenry_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_inductance_type_caster.h" +types = [ + "units::henries", + "units::henry", + "units::kilohenries", + "units::kilohenry", + "units::microhenries", + "units::microhenry", + "units::millihenries", + "units::millihenry", + "units::nanohenries", + "units::nanohenry", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_length_type_caster.h" +types = [ + "units::angstrom_t", + "units::astronicalUnit_t", + "units::centimeter_t", + "units::chain_t", + "units::cubit_t", + "units::fathom_t", + "units::foot_t", + "units::furlong_t", + "units::hand_t", + "units::inch_t", + "units::kilometer_t", + "units::league_t", + "units::lightyear_t", + "units::meter_t", + "units::micrometer_t", + "units::mil_t", + "units::mile_t", + "units::millimeter_t", + "units::nanometer_t", + "units::nauticalLeague_t", + "units::nauticalMile_t", + "units::parsec_t", + "units::yard_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_length_type_caster.h" +types = [ + "units::angstrom", + "units::angstroms", + "units::astronicalUnit", + "units::astronicalUnits", + "units::chain", + "units::chains", + "units::cubit", + "units::cubits", + "units::fathom", + "units::fathoms", + "units::feet", + "units::foot", + "units::furlong", + "units::furlongs", + "units::hand", + "units::hands", + "units::inch", + "units::inches", + "units::kilometer", + "units::kilometers", + "units::league", + "units::leagues", + "units::lightyear", + "units::lightyears", + "units::meter", + "units::meters", + "units::micrometer", + "units::micrometers", + "units::mil", + "units::mile", + "units::miles", + "units::millimeter", + "units::millimeters", + "units::mils", + "units::nanometer", + "units::nanometers", + "units::nauticalLeague", + "units::nauticalLeagues", + "units::nauticalMile", + "units::nauticalMiles", + "units::parsec", + "units::parsecs", + "units::yard", + "units::yards", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_luminous_flux_type_caster.h" +types = [ + "units::kilolumen_t", + "units::lumen_t", + "units::microlumen_t", + "units::millilumen_t", + "units::nanolumen_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_luminous_flux_type_caster.h" +types = [ + "units::kilolumen", + "units::kilolumens", + "units::lumen", + "units::lumens", + "units::microlumen", + "units::microlumens", + "units::millilumen", + "units::millilumens", + "units::nanolumen", + "units::nanolumens", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_luminous_intensity_type_caster.h" +types = [ + "units::candela_t", + "units::kilocandela_t", + "units::microcandela_t", + "units::millicandela_t", + "units::nanocandela_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_luminous_intensity_type_caster.h" +types = [ + "units::candela", + "units::candelas", + "units::kilocandela", + "units::kilocandelas", + "units::microcandela", + "units::microcandelas", + "units::millicandela", + "units::millicandelas", + "units::nanocandela", + "units::nanocandelas", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_magnetic_field_strength_type_caster.h" +types = [ + "units::gauss_t", + "units::kilotesla_t", + "units::microtesla_t", + "units::millitesla_t", + "units::nanotesla_t", + "units::tesla_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_magnetic_field_strength_type_caster.h" +types = [ + "units::gauss", + "units::gauss", + "units::kilotesla", + "units::kiloteslas", + "units::microtesla", + "units::microteslas", + "units::millitesla", + "units::milliteslas", + "units::nanotesla", + "units::nanoteslas", + "units::tesla", + "units::teslas", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_magnetic_flux_type_caster.h" +types = [ + "units::kiloweber_t", + "units::maxwell_t", + "units::microweber_t", + "units::milliweber_t", + "units::nanoweber_t", + "units::weber_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_magnetic_flux_type_caster.h" +types = [ + "units::kiloweber", + "units::kilowebers", + "units::maxwell", + "units::maxwells", + "units::microweber", + "units::microwebers", + "units::milliweber", + "units::milliwebers", + "units::nanoweber", + "units::nanowebers", + "units::weber", + "units::webers", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_mass_type_caster.h" +types = [ + "units::carat_t", + "units::gram_t", + "units::kilogram_t", + "units::long_ton_t", + "units::metric_ton_t", + "units::microgram_t", + "units::milligram_t", + "units::nanogram_t", + "units::ounce_t", + "units::pound_t", + "units::short_ton_t", + "units::slug_t", + "units::stone_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_mass_type_caster.h" +types = [ + "units::carat", + "units::carats", + "units::gram", + "units::grams", + "units::kilogram", + "units::kilograms", + "units::long_ton", + "units::long_tons", + "units::metric_ton", + "units::metric_tons", + "units::microgram", + "units::micrograms", + "units::milligram", + "units::milligrams", + "units::nanogram", + "units::nanograms", + "units::ounce", + "units::ounces", + "units::pound", + "units::pounds", + "units::short_ton", + "units::short_tons", + "units::slug", + "units::slugs", + "units::stone", + "units::stone", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_moment_of_inertia_type_caster.h" +types = [ + "units::kilogram_square_meter_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_moment_of_inertia_type_caster.h" +types = [ + "units::kilogram_square_meter", + "units::kilogram_square_meters", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_power_type_caster.h" +types = [ + "units::horsepower_t", + "units::kilowatt_t", + "units::microwatt_t", + "units::milliwatt_t", + "units::nanowatt_t", + "units::watt_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_power_type_caster.h" +types = [ + "units::horsepower", + "units::horsepower", + "units::kilowatt", + "units::kilowatts", + "units::microwatt", + "units::microwatts", + "units::milliwatt", + "units::milliwatts", + "units::nanowatt", + "units::nanowatts", + "units::watt", + "units::watts", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_pressure_type_caster.h" +types = [ + "units::atmosphere_t", + "units::bar_t", + "units::kilopascal_t", + "units::mbar_t", + "units::micropascal_t", + "units::millipascal_t", + "units::nanopascal_t", + "units::pascal_t", + "units::pounds_per_square_inch_t", + "units::torr_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_pressure_type_caster.h" +types = [ + "units::atmosphere", + "units::atmospheres", + "units::bar", + "units::bars", + "units::kilopascal", + "units::kilopascals", + "units::mbar", + "units::mbars", + "units::micropascal", + "units::micropascals", + "units::millipascal", + "units::millipascals", + "units::nanopascal", + "units::nanopascals", + "units::pascal", + "units::pascals", + "units::pounds_per_square_inch", + "units::pounds_per_square_inch", + "units::torr", + "units::torrs", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_radiation_type_caster.h" +types = [ + "units::becquerel_t", + "units::curie_t", + "units::gray_t", + "units::kilobecquerel_t", + "units::kilogray_t", + "units::kilosievert_t", + "units::microbecquerel_t", + "units::microgray_t", + "units::microsievert_t", + "units::millibecquerel_t", + "units::milligray_t", + "units::millisievert_t", + "units::nanobecquerel_t", + "units::nanogray_t", + "units::nanosievert_t", + "units::rad_t", + "units::rutherford_t", + "units::sievert_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_radiation_type_caster.h" +types = [ + "units::becquerel", + "units::becquerels", + "units::curie", + "units::curies", + "units::gray", + "units::grays", + "units::kilobecquerel", + "units::kilobecquerels", + "units::kilogray", + "units::kilograys", + "units::kilosievert", + "units::kilosieverts", + "units::microbecquerel", + "units::microbecquerels", + "units::microgray", + "units::micrograys", + "units::microsievert", + "units::microsieverts", + "units::millibecquerel", + "units::millibecquerels", + "units::milligray", + "units::milligrays", + "units::millisievert", + "units::millisieverts", + "units::nanobecquerel", + "units::nanobecquerels", + "units::nanogray", + "units::nanograys", + "units::nanosievert", + "units::nanosieverts", + "units::rad", + "units::rads", + "units::rutherford", + "units::rutherfords", + "units::sievert", + "units::sieverts", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_solid_angle_type_caster.h" +types = [ + "units::degree_squared_t", + "units::kilosteradian_t", + "units::microsteradian_t", + "units::millisteradian_t", + "units::nanosteradian_t", + "units::spat_t", + "units::steradian_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_solid_angle_type_caster.h" +types = [ + "units::degree_squared", + "units::degrees_squared", + "units::kilosteradian", + "units::kilosteradians", + "units::microsteradian", + "units::microsteradians", + "units::millisteradian", + "units::millisteradians", + "units::nanosteradian", + "units::nanosteradians", + "units::spat", + "units::spats", + "units::steradian", + "units::steradians", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_substance_type_caster.h" +types = [ + "units::mole_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_substance_type_caster.h" +types = [ + "units::mole", + "units::moles", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_temperature_type_caster.h" +types = [ + "units::celsius_t", + "units::fahrenheit_t", + "units::kelvin_t", + "units::rankine_t", + "units::reaumur_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_temperature_type_caster.h" +types = [ + "units::celsius", + "units::celsius", + "units::fahrenheit", + "units::fahrenheit", + "units::kelvin", + "units::kelvin", + "units::rankine", + "units::rankine", + "units::reaumur", + "units::reaumur", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_time_type_caster.h" +types = [ + "units::day_t", + "units::gregorian_year_t", + "units::hour_t", + "units::julian_year_t", + "units::kilosecond_t", + "units::microsecond_t", + "units::millisecond_t", + "units::minute_t", + "units::nanosecond_t", + "units::second_t", + "units::week_t", + "units::year_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_time_type_caster.h" +types = [ + "units::day", + "units::days", + "units::gregorian_year", + "units::gregorian_years", + "units::hour", + "units::hours", + "units::julian_year", + "units::julian_years", + "units::kilosecond", + "units::kiloseconds", + "units::microsecond", + "units::microseconds", + "units::millisecond", + "units::milliseconds", + "units::minute", + "units::minutes", + "units::nanosecond", + "units::nanoseconds", + "units::second", + "units::seconds", + "units::week", + "units::weeks", + "units::year", + "units::years", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_torque_type_caster.h" +types = [ + "units::foot_pound_t", + "units::foot_poundal_t", + "units::inch_pound_t", + "units::meter_kilogram_t", + "units::newton_meter_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_torque_type_caster.h" +types = [ + "units::foot_pound", + "units::foot_poundal", + "units::foot_poundals", + "units::foot_pounds", + "units::inch_pound", + "units::inch_pounds", + "units::meter_kilogram", + "units::meter_kilograms", + "units::newton_meter", + "units::newton_meters", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_velocity_type_caster.h" +types = [ + "units::feet_per_second_t", + "units::kilometers_per_hour_t", + "units::knot_t", + "units::meters_per_second_t", + "units::miles_per_hour_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_velocity_type_caster.h" +types = [ + "units::feet_per_second", + "units::feet_per_second", + "units::kilometers_per_hour", + "units::kilometers_per_hour", + "units::knot", + "units::knots", + "units::meters_per_second", + "units::meters_per_second", + "units::miles_per_hour", + "units::miles_per_hour", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_voltage_type_caster.h" +types = [ + "units::abvolt_t", + "units::kilovolt_t", + "units::microvolt_t", + "units::millivolt_t", + "units::nanovolt_t", + "units::statvolt_t", + "units::volt_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_voltage_type_caster.h" +types = [ + "units::abvolt", + "units::abvolts", + "units::kilovolt", + "units::kilovolts", + "units::microvolt", + "units::microvolts", + "units::millivolt", + "units::millivolts", + "units::nanovolt", + "units::nanovolts", + "units::statvolt", + "units::statvolts", + "units::volt", + "units::volts", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_volume_type_caster.h" +types = [ + "units::barrel_t", + "units::bushel_t", + "units::cord_t", + "units::cubic_fathom_t", + "units::cubic_foot_t", + "units::cubic_inch_t", + "units::cubic_kilometer_t", + "units::cubic_meter_t", + "units::cubic_mile_t", + "units::cubic_millimeter_t", + "units::cubic_yard_t", + "units::cup_t", + "units::dash_t", + "units::dram_t", + "units::drop_t", + "units::fifth_t", + "units::fluid_ounce_t", + "units::gallon_t", + "units::gill_t", + "units::kiloliter_t", + "units::liter_t", + "units::microliter_t", + "units::milliliter_t", + "units::nanoliter_t", + "units::peck_t", + "units::pinch_t", + "units::pint_t", + "units::quart_t", + "units::sack_t", + "units::shot_t", + "units::strike_t", + "units::tablespoon_t", + "units::teaspoon_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_volume_type_caster.h" +types = [ + "units::barrel", + "units::barrels", + "units::bushel", + "units::bushels", + "units::cord", + "units::cords", + "units::cubic_fathom", + "units::cubic_fathoms", + "units::cubic_feet", + "units::cubic_foot", + "units::cubic_inch", + "units::cubic_inches", + "units::cubic_kilometer", + "units::cubic_kilometers", + "units::cubic_meter", + "units::cubic_meters", + "units::cubic_mile", + "units::cubic_miles", + "units::cubic_millimeter", + "units::cubic_millimeters", + "units::cubic_yard", + "units::cubic_yards", + "units::cup", + "units::cups", + "units::dash", + "units::dashes", + "units::dram", + "units::drams", + "units::drop", + "units::drops", + "units::fifth", + "units::fifths", + "units::fluid_ounce", + "units::fluid_ounces", + "units::gallon", + "units::gallons", + "units::gill", + "units::gills", + "units::kiloliter", + "units::kiloliters", + "units::liter", + "units::liters", + "units::microliter", + "units::microliters", + "units::milliliter", + "units::milliliters", + "units::nanoliter", + "units::nanoliters", + "units::peck", + "units::pecks", + "units::pinch", + "units::pinches", + "units::pint", + "units::pints", + "units::quart", + "units::quarts", + "units::sack", + "units::sacks", + "units::shot", + "units::shots", + "units::strike", + "units::strikes", + "units::tablespoon", + "units::tablespoons", + "units::teaspoon", + "units::teaspoons", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_compound_type_caster.h" +types = [ + "units::curvature_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_compound_type_caster.h" +types = [ + "units::compound_unit", + "units::inverse", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_misc_type_caster.h" +types = [ + "units::dimensionless_t", + "units::dimensionless::dimensionless_t", + "units::scalar_t", + "units::dimensionless::scalar_t", +] +default_arg_cast = true + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "units_misc_type_caster.h" +types = [ + "units::dimensionless", + "units::dimensionless::dimensionless", + "units::scalar", + "units::dimensionless::scalar", +] +default_arg_cast = false + +[[tool.semiwrap.export_type_casters.wpimath-casters.headers]] +header = "frc_eigen.h" +types = [ + "frc::Vectord", + "frc::Matrixd", +] + +[tool.semiwrap.extension_modules."wpimath._wpimath"] +name = "wpimath" +wraps = ["robotpy-native-wpimath"] +includes = ["wpimath/_impl/src"] +depends = ["wpiutil", "wpimath-casters"] + +[tool.semiwrap.extension_modules."wpimath._wpimath".headers] +# frc +ComputerVisionUtil = "frc/ComputerVisionUtil.h" +# DARE = "frc/DARE.h" +# EigenCore = "frc/EigenCore.h" +MathUtil = "frc/MathUtil.h" +# StateSpaceUtil = "frc/StateSpaceUtil.h" + + +[tool.semiwrap.extension_modules."wpimath.filter._filter"] +name = "wpimath_filter" +wraps = ["robotpy-native-wpimath"] +depends = ["wpimath"] +yaml_path = "semiwrap/filter" + +[tool.semiwrap.extension_modules."wpimath.filter._filter".headers] +Debouncer = "frc/filter/Debouncer.h" +LinearFilter = "frc/filter/LinearFilter.h" +MedianFilter = "frc/filter/MedianFilter.h" +SlewRateLimiter = "frc/filter/SlewRateLimiter.h" + + +[tool.semiwrap.extension_modules."wpimath.geometry._geometry"] +name = "wpimath_geometry" +wraps = ["robotpy-native-wpimath"] +depends = ["wpimath"] +includes = ["wpimath/geometry/include"] +yaml_path = "semiwrap/geometry" + +[tool.semiwrap.extension_modules."wpimath.geometry._geometry".headers] +# frc/geometry +CoordinateAxis = "frc/geometry/CoordinateAxis.h" +CoordinateSystem = "frc/geometry/CoordinateSystem.h" +Ellipse2d = "frc/geometry/Ellipse2d.h" +Pose2d = "frc/geometry/Pose2d.h" +Pose3d = "frc/geometry/Pose3d.h" +Quaternion = "frc/geometry/Quaternion.h" +Rectangle2d = "frc/geometry/Rectangle2d.h" +Rotation2d = "frc/geometry/Rotation2d.h" +Rotation3d = "frc/geometry/Rotation3d.h" +Transform2d = "frc/geometry/Transform2d.h" +Transform3d = "frc/geometry/Transform3d.h" +Translation2d = "frc/geometry/Translation2d.h" +Translation3d = "frc/geometry/Translation3d.h" +Twist2d = "frc/geometry/Twist2d.h" +Twist3d = "frc/geometry/Twist3d.h" + +[tool.semiwrap.extension_modules."wpimath.interpolation._interpolation"] +name = "wpimath_interpolation" +wraps = ["robotpy-native-wpimath"] +depends = ["wpimath_geometry"] +yaml_path = "semiwrap/interpolation" + +[tool.semiwrap.extension_modules."wpimath.interpolation._interpolation".headers] +# frc/interpolation +TimeInterpolatableBuffer = "frc/interpolation/TimeInterpolatableBuffer.h" + + +[tool.semiwrap.extension_modules."wpimath.kinematics._kinematics"] +name = "wpimath_kinematics" +wraps = ["robotpy-native-wpimath"] +depends = ["wpimath_geometry"] +yaml_path = "semiwrap/kinematics" + +[tool.semiwrap.extension_modules."wpimath.kinematics._kinematics".headers] +# frc/kinematics +ChassisSpeeds = "frc/kinematics/ChassisSpeeds.h" +DifferentialDriveKinematics = "frc/kinematics/DifferentialDriveKinematics.h" +DifferentialDriveOdometry3d = "frc/kinematics/DifferentialDriveOdometry3d.h" +DifferentialDriveOdometry = "frc/kinematics/DifferentialDriveOdometry.h" +DifferentialDriveWheelPositions = "frc/kinematics/DifferentialDriveWheelPositions.h" +DifferentialDriveWheelSpeeds = "frc/kinematics/DifferentialDriveWheelSpeeds.h" +Kinematics = "frc/kinematics/Kinematics.h" +MecanumDriveKinematics = "frc/kinematics/MecanumDriveKinematics.h" +MecanumDriveOdometry = "frc/kinematics/MecanumDriveOdometry.h" +MecanumDriveOdometry3d = "frc/kinematics/MecanumDriveOdometry3d.h" +MecanumDriveWheelPositions = "frc/kinematics/MecanumDriveWheelPositions.h" +MecanumDriveWheelSpeeds = "frc/kinematics/MecanumDriveWheelSpeeds.h" +Odometry = "frc/kinematics/Odometry.h" +Odometry3d = "frc/kinematics/Odometry3d.h" +SwerveDriveKinematics = "frc/kinematics/SwerveDriveKinematics.h" +SwerveDriveOdometry = "frc/kinematics/SwerveDriveOdometry.h" +SwerveDriveOdometry3d = "frc/kinematics/SwerveDriveOdometry3d.h" +SwerveModulePosition = "frc/kinematics/SwerveModulePosition.h" +SwerveModuleState = "frc/kinematics/SwerveModuleState.h" + + +[tool.semiwrap.extension_modules."wpimath.spline._spline"] +name = "wpimath_spline" +wraps = ["robotpy-native-wpimath"] +depends = ["wpimath_geometry"] +yaml_path = "semiwrap/spline" + +[tool.semiwrap.extension_modules."wpimath.spline._spline".headers] +# frc/spline +CubicHermiteSpline = "frc/spline/CubicHermiteSpline.h" +QuinticHermiteSpline = "frc/spline/QuinticHermiteSpline.h" +Spline = "frc/spline/Spline.h" +SplineHelper = "frc/spline/SplineHelper.h" +SplineParameterizer = "frc/spline/SplineParameterizer.h" + + +[tool.semiwrap.extension_modules."wpimath._controls._controls"] +name = "wpimath_controls" +wraps = ["robotpy-native-wpimath"] +depends = ["wpimath", "wpimath_geometry", "wpimath_kinematics", "wpimath_spline"] +yaml_path = "semiwrap/controls" + +[tool.semiwrap.extension_modules."wpimath._controls._controls".headers] +# frc/controller +ArmFeedforward = "frc/controller/ArmFeedforward.h" +BangBangController = "frc/controller/BangBangController.h" +ControlAffinePlantInversionFeedforward = "frc/controller/ControlAffinePlantInversionFeedforward.h" +DifferentialDriveAccelerationLimiter = "frc/controller/DifferentialDriveAccelerationLimiter.h" +DifferentialDriveFeedforward = "frc/controller/DifferentialDriveFeedforward.h" +DifferentialDriveWheelVoltages = "frc/controller/DifferentialDriveWheelVoltages.h" +ElevatorFeedforward = "frc/controller/ElevatorFeedforward.h" +HolonomicDriveController = "frc/controller/HolonomicDriveController.h" +ImplicitModelFollower = "frc/controller/ImplicitModelFollower.h" +LTVDifferentialDriveController = "frc/controller/LTVDifferentialDriveController.h" +LTVUnicycleController = "frc/controller/LTVUnicycleController.h" +LinearPlantInversionFeedforward = "frc/controller/LinearPlantInversionFeedforward.h" +LinearQuadraticRegulator = "frc/controller/LinearQuadraticRegulator.h" +PIDController = "frc/controller/PIDController.h" +ProfiledPIDController = "frc/controller/ProfiledPIDController.h" +SimpleMotorFeedforward = "frc/controller/SimpleMotorFeedforward.h" + +# frc/estimator +# AngleStatistics = "frc/estimator/AngleStatistics.h" +DifferentialDrivePoseEstimator = "frc/estimator/DifferentialDrivePoseEstimator.h" +DifferentialDrivePoseEstimator3d = "frc/estimator/DifferentialDrivePoseEstimator3d.h" +ExtendedKalmanFilter = "frc/estimator/ExtendedKalmanFilter.h" +KalmanFilter = "frc/estimator/KalmanFilter.h" +# KalmanFilterLatencyCompensator = "frc/estimator/KalmanFilterLatencyCompensator.h" +MecanumDrivePoseEstimator = "frc/estimator/MecanumDrivePoseEstimator.h" +MecanumDrivePoseEstimator3d = "frc/estimator/MecanumDrivePoseEstimator3d.h" +# MerweScaledSigmaPoints = "frc/estimator/MerweScaledSigmaPoints.h" +PoseEstimator = "frc/estimator/PoseEstimator.h" +PoseEstimator3d = "frc/estimator/PoseEstimator3d.h" +SwerveDrivePoseEstimator = "frc/estimator/SwerveDrivePoseEstimator.h" +SwerveDrivePoseEstimator3d = "frc/estimator/SwerveDrivePoseEstimator3d.h" +# UnscentedKalmanFilter = "frc/estimator/UnscentedKalmanFilter.h" +# UnscentedTransform = "frc/estimator/UnscentedTransform.h" + +# frc/optimization +SimulatedAnnealing = "frc/optimization/SimulatedAnnealing.h" + +# frc/path +TravelingSalesman = "frc/path/TravelingSalesman.h" + +# frc/system +# Discretization = "frc/system/Discretization.h" +LinearSystem = "frc/system/LinearSystem.h" +LinearSystemLoop = "frc/system/LinearSystemLoop.h" +# NumericalIntegration = "frc/system/NumericalIntegration.h" +# NumericalJacobian = "frc/system/NumericalJacobian.h" + +# frc/system/plant +DCMotor = "frc/system/plant/DCMotor.h" +LinearSystemId = "frc/system/plant/LinearSystemId.h" + +# frc/trajectory +ExponentialProfile = "frc/trajectory/ExponentialProfile.h" +Trajectory = "frc/trajectory/Trajectory.h" +TrajectoryConfig = "frc/trajectory/TrajectoryConfig.h" +TrajectoryGenerator = "frc/trajectory/TrajectoryGenerator.h" +TrajectoryParameterizer = "frc/trajectory/TrajectoryParameterizer.h" +TrapezoidProfile = "frc/trajectory/TrapezoidProfile.h" + +# frc/trajectory/constraint +CentripetalAccelerationConstraint = "frc/trajectory/constraint/CentripetalAccelerationConstraint.h" +DifferentialDriveKinematicsConstraint = "frc/trajectory/constraint/DifferentialDriveKinematicsConstraint.h" +DifferentialDriveVoltageConstraint = "frc/trajectory/constraint/DifferentialDriveVoltageConstraint.h" +EllipticalRegionConstraint = "frc/trajectory/constraint/EllipticalRegionConstraint.h" +MaxVelocityConstraint = "frc/trajectory/constraint/MaxVelocityConstraint.h" +MecanumDriveKinematicsConstraint = "frc/trajectory/constraint/MecanumDriveKinematicsConstraint.h" +RectangularRegionConstraint = "frc/trajectory/constraint/RectangularRegionConstraint.h" +SwerveDriveKinematicsConstraint = "frc/trajectory/constraint/SwerveDriveKinematicsConstraint.h" +TrajectoryConstraint = "frc/trajectory/constraint/TrajectoryConstraint.h" diff --git a/wpimath/src/main/python/semiwrap/ComputerVisionUtil.yml b/wpimath/src/main/python/semiwrap/ComputerVisionUtil.yml new file mode 100644 index 0000000000..57f87b7b31 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/ComputerVisionUtil.yml @@ -0,0 +1,2 @@ +functions: + ObjectToRobotPose: diff --git a/wpimath/src/main/python/semiwrap/MathUtil.yml b/wpimath/src/main/python/semiwrap/MathUtil.yml new file mode 100644 index 0000000000..303b701840 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/MathUtil.yml @@ -0,0 +1,47 @@ +functions: + InputModulus: + template_impls: + - [double] + AngleModulus: + FloorDiv: + template_impls: + - [int64_t, int64_t] + # work around GCC 10 issue on raspbian + cpp_code: | + [](int64_t x, int64_t y) -> int64_t { + return frc::FloorDiv(x, y); + } + FloorMod: + template_impls: + - [int64_t, int64_t] + # work around GCC 10 issue on raspbian + cpp_code: | + [](int64_t x, int64_t y) -> int64_t { + return frc::FloorMod(x, y); + } + ApplyDeadband: + param_override: + maxMagnitude: + default: '1.0' + template_impls: + - [double] + CopySignPow: + param_override: + maxMagnitude: + default: '1.0' + template_impls: + - [double] + IsNear: + overloads: + T, T, T: + ignore: true + template_impls: + - [double] + T, T, T, T, T: + ignore: true + template_impls: + - [double] + SlewRateLimit: + overloads: + const Translation2d&, const Translation2d&, units::second_t, units::meters_per_second_t: + const Translation3d&, const Translation3d&, units::second_t, units::meters_per_second_t: diff --git a/wpimath/src/main/python/semiwrap/controls/ArmFeedforward.yml b/wpimath/src/main/python/semiwrap/controls/ArmFeedforward.yml new file mode 100644 index 0000000000..3c34a97d14 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/ArmFeedforward.yml @@ -0,0 +1,42 @@ +defaults: + subpackage: controller + +classes: + frc::ArmFeedforward: + force_type_casters: + - units::radians_per_second_squared + typealias: + - frc::ArmFeedforward::Acceleration + - frc::ArmFeedforward::kv_unit + - frc::ArmFeedforward::ka_unit + methods: + ArmFeedforward: + overloads: + '': + units::volt_t, units::volt_t, units::unit_t, units::unit_t: + Calculate: + overloads: + units::unit_t, units::unit_t, units::unit_t [const]: + ignore: true + units::unit_t, units::unit_t, units::unit_t, units::second_t [const]: + ignore: true + units::unit_t, units::unit_t [const]: + units::unit_t, units::unit_t, units::unit_t [const]: + MaxAchievableVelocity: + MinAchievableVelocity: + MaxAchievableAcceleration: + MinAchievableAcceleration: + GetKs: + GetKg: + GetKv: + GetKa: + SetKs: + SetKg: + SetKv: + SetKa: + +extra_includes: +- wpystruct.h + +inline_code: | + SetupWPyStruct(cls_ArmFeedforward); diff --git a/wpimath/src/main/python/semiwrap/controls/BangBangController.yml b/wpimath/src/main/python/semiwrap/controls/BangBangController.yml new file mode 100644 index 0000000000..3fd20d7e74 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/BangBangController.yml @@ -0,0 +1,21 @@ +defaults: + subpackage: controller + +classes: + frc::BangBangController: + ignored_bases: + - wpi::SendableHelper + methods: + BangBangController: + SetSetpoint: + GetSetpoint: + AtSetpoint: + SetTolerance: + GetTolerance: + GetMeasurement: + GetError: + Calculate: + overloads: + double, double: + double: + InitSendable: diff --git a/wpimath/src/main/python/semiwrap/controls/CentripetalAccelerationConstraint.yml b/wpimath/src/main/python/semiwrap/controls/CentripetalAccelerationConstraint.yml new file mode 100644 index 0000000000..d93fada353 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/CentripetalAccelerationConstraint.yml @@ -0,0 +1,17 @@ +defaults: + subpackage: constraint + +classes: + frc::CentripetalAccelerationConstraint: + typealias: + - frc::TrajectoryConstraint::MinMax + methods: + CentripetalAccelerationConstraint: + MaxVelocity: + MinMaxAcceleration: + +inline_code: |- + cls_CentripetalAccelerationConstraint + .def_static("fromFps", [](units::feet_per_second_squared_t maxCentripetalAcceleration) { + return std::make_shared(maxCentripetalAcceleration); + }, py::arg("maxCentripetalAcceleration")); diff --git a/wpimath/src/main/python/semiwrap/controls/ControlAffinePlantInversionFeedforward.yml b/wpimath/src/main/python/semiwrap/controls/ControlAffinePlantInversionFeedforward.yml new file mode 100644 index 0000000000..32451caed2 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/ControlAffinePlantInversionFeedforward.yml @@ -0,0 +1,46 @@ +defaults: + subpackage: controller + +classes: + frc::ControlAffinePlantInversionFeedforward: + template_params: + - int States + - int Inputs + methods: + ControlAffinePlantInversionFeedforward: + overloads: + std::function, units::second_t: + std::function, const Matrixd&, units::second_t: + Uff: + overloads: + '[const]': + int [const]: + R: + overloads: + '[const]': + int [const]: + Reset: + overloads: + const StateVector&: + '': + Calculate: + overloads: + const StateVector&: + const StateVector&, const StateVector&: + +templates: + ControlAffinePlantInversionFeedforward_1_1: + qualname: frc::ControlAffinePlantInversionFeedforward + params: + - 1 + - 1 + ControlAffinePlantInversionFeedforward_2_1: + qualname: frc::ControlAffinePlantInversionFeedforward + params: + - 2 + - 1 + ControlAffinePlantInversionFeedforward_2_2: + qualname: frc::ControlAffinePlantInversionFeedforward + params: + - 2 + - 2 diff --git a/wpimath/src/main/python/semiwrap/controls/DCMotor.yml b/wpimath/src/main/python/semiwrap/controls/DCMotor.yml new file mode 100644 index 0000000000..ee62a40d11 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/DCMotor.yml @@ -0,0 +1,46 @@ +defaults: + subpackage: plant + +extra_includes: +- wpystruct.h + +classes: + frc::DCMotor: + attributes: + nominalVoltage: + stallTorque: + stallCurrent: + freeCurrent: + freeSpeed: + R: + Kv: + Kt: + methods: + DCMotor: + Current: + overloads: + units::radians_per_second_t, units::volt_t [const]: + units::newton_meter_t [const]: + Torque: + Voltage: + Speed: + WithReduction: + CIM: + MiniCIM: + Bag: + Vex775Pro: + RS775_125: + BanebotsRS775: + Andymark9015: + BanebotsRS550: + NEO: + NEO550: + Falcon500: + Falcon500FOC: + RomiBuiltIn: + KrakenX60: + KrakenX60FOC: + NeoVortex: + +inline_code: | + SetupWPyStruct(cls_DCMotor); diff --git a/wpimath/src/main/python/semiwrap/controls/DifferentialDriveAccelerationLimiter.yml b/wpimath/src/main/python/semiwrap/controls/DifferentialDriveAccelerationLimiter.yml new file mode 100644 index 0000000000..bd8864415c --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/DifferentialDriveAccelerationLimiter.yml @@ -0,0 +1,12 @@ +defaults: + subpackage: controller + +classes: + frc::DifferentialDriveAccelerationLimiter: + methods: + DifferentialDriveAccelerationLimiter: + overloads: + LinearSystem<2, 2, 2>, units::meter_t, units::meters_per_second_squared_t, units::radians_per_second_squared_t: + ? LinearSystem<2, 2, 2>, units::meter_t, units::meters_per_second_squared_t, units::meters_per_second_squared_t, units::radians_per_second_squared_t + : + Calculate: diff --git a/wpimath/src/main/python/semiwrap/controls/DifferentialDriveFeedforward.yml b/wpimath/src/main/python/semiwrap/controls/DifferentialDriveFeedforward.yml new file mode 100644 index 0000000000..3f26a50ecd --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/DifferentialDriveFeedforward.yml @@ -0,0 +1,41 @@ +classes: + frc::DifferentialDriveFeedforward: + force_type_casters: + - units::meters_per_second + - units::meters_per_second_squared + - units::radians_per_second + - units::radians_per_second_squared + - units::compound_unit + attributes: + m_kVLinear: + m_kALinear: + m_kVAngular: + m_kAAngular: + methods: + DifferentialDriveFeedforward: + overloads: + decltype(1_V/1_mps), decltype(1_V/1_mps_sq), decltype(1_V/1_rad_per_s), decltype(1_V/1_rad_per_s_sq), units::meter_t: + decltype(1_V/1_mps), decltype(1_V/1_mps_sq), decltype(1_V/1_mps), decltype(1_V/1_mps_sq): + # ? decltype ( 1 _V / 1 _mps ), decltype ( 1 _V / 1 _mps_sq ), decltype ( + # 1 _V / 1 _rad_per_s ), decltype ( 1 _V / 1 _rad_per_s_sq ), units::meter_t + # : + # param_override: + # kVLinear: + # x_type: decltype(1_V / 1_mps) + # kALinear: + # x_type: decltype(1_V / 1_mps_sq) + # kVAngular: + # x_type: decltype(1_V / 1_rad_per_s) + # kAAngular: + # x_type: decltype(1_V / 1_rad_per_s_sq) + # decltype ( 1 _V / 1 _mps ), decltype ( 1 _V / 1 _mps_sq ), decltype ( 1 _V / 1 _mps ), decltype ( 1 _V / 1 _mps_sq ): + # param_override: + # kVLinear: + # x_type: decltype(1_V / 1_mps) + # kALinear: + # x_type: decltype(1_V / 1_mps_sq) + # kVAngular: + # x_type: decltype(1_V / 1_mps) + # kAAngular: + # x_type: decltype(1_V / 1_mps_sq) + Calculate: diff --git a/wpimath/src/main/python/semiwrap/controls/DifferentialDriveKinematicsConstraint.yml b/wpimath/src/main/python/semiwrap/controls/DifferentialDriveKinematicsConstraint.yml new file mode 100644 index 0000000000..f3dffbf1ba --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/DifferentialDriveKinematicsConstraint.yml @@ -0,0 +1,19 @@ +defaults: + subpackage: constraint + +classes: + frc::DifferentialDriveKinematicsConstraint: + typealias: + - frc::TrajectoryConstraint::MinMax + methods: + DifferentialDriveKinematicsConstraint: + MaxVelocity: + MinMaxAcceleration: + +inline_code: |- + cls_DifferentialDriveKinematicsConstraint + .def_static("fromFps", [](const DifferentialDriveKinematics& kinematics, + units::feet_per_second_t maxSpeed) { + return std::make_shared(kinematics, maxSpeed); + }, py::arg("kinematics"), py::arg("maxSpeed")) + ; diff --git a/wpimath/src/main/python/semiwrap/controls/DifferentialDrivePoseEstimator.yml b/wpimath/src/main/python/semiwrap/controls/DifferentialDrivePoseEstimator.yml new file mode 100644 index 0000000000..a99a28dffc --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/DifferentialDrivePoseEstimator.yml @@ -0,0 +1,48 @@ +defaults: + subpackage: estimator + +classes: + frc::DifferentialDrivePoseEstimator: + force_no_trampoline: true + doc: | + This class wraps an Unscented Kalman Filter to fuse latency-compensated + vision measurements with differential drive encoder measurements. It will + correct for noisy vision measurements and encoder drift. It is intended to be + an easy drop-in for :class:`DifferentialDriveOdometry`. In fact, if you never call + :meth:`addVisionMeasurement`, and only call :meth:`update`, this will behave exactly the + same as DifferentialDriveOdometry. + + :meth:`update` should be called every robot loop (if your robot loops are faster or + slower than the default, then you should change the nominal delta time via + the constructor). + + :meth:`addVisionMeasurement` can be called as infrequently as you want; if you + never call it, then this class will behave like regular encoder odometry. + + The state-space system used internally has the following states (x), inputs + (u), and outputs (y): + + :math:`x = [x, y, \theta, dist_l, dist_r]^T` in the field coordinate + system containing x position, y position, heading, left encoder distance, + and right encoder distance. + + :math:`u = [v_l, v_r, d\theta]^T` containing left wheel velocity, + right wheel velocity, and change in gyro heading. + + NB: Using velocities make things considerably easier, because it means that + teams don't have to worry about getting an accurate model. Basically, we + suspect that it's easier for teams to get good encoder data than it is for + them to perform system identification well enough to get a good model. + + :math:`y = [x, y, \theta]^T` from vision containing x position, y + position, and heading; or :math:`y = [dist_l, dist_r, \theta]^T` + containing left encoder position, right encoder position, and gyro heading. + methods: + DifferentialDrivePoseEstimator: + overloads: + DifferentialDriveKinematics&, const Rotation2d&, units::meter_t, units::meter_t, const Pose2d&: + ? DifferentialDriveKinematics&, const Rotation2d&, units::meter_t, units::meter_t, const Pose2d&, const wpi::array&, const wpi::array& + : + ResetPosition: + Update: + UpdateWithTime: diff --git a/wpimath/src/main/python/semiwrap/controls/DifferentialDrivePoseEstimator3d.yml b/wpimath/src/main/python/semiwrap/controls/DifferentialDrivePoseEstimator3d.yml new file mode 100644 index 0000000000..847cf4a799 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/DifferentialDrivePoseEstimator3d.yml @@ -0,0 +1,15 @@ +defaults: + subpackage: estimator + +classes: + frc::DifferentialDrivePoseEstimator3d: + force_no_trampoline: true + methods: + DifferentialDrivePoseEstimator3d: + overloads: + DifferentialDriveKinematics&, const Rotation3d&, units::meter_t, units::meter_t, const Pose3d&: + ? DifferentialDriveKinematics&, const Rotation3d&, units::meter_t, units::meter_t, const Pose3d&, const wpi::array&, const wpi::array& + : + ResetPosition: + Update: + UpdateWithTime: diff --git a/wpimath/src/main/python/semiwrap/controls/DifferentialDriveVoltageConstraint.yml b/wpimath/src/main/python/semiwrap/controls/DifferentialDriveVoltageConstraint.yml new file mode 100644 index 0000000000..2107520b50 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/DifferentialDriveVoltageConstraint.yml @@ -0,0 +1,11 @@ +defaults: + subpackage: constraint + +classes: + frc::DifferentialDriveVoltageConstraint: + typealias: + - frc::TrajectoryConstraint::MinMax + methods: + DifferentialDriveVoltageConstraint: + MaxVelocity: + MinMaxAcceleration: diff --git a/wpimath/src/main/python/semiwrap/controls/DifferentialDriveWheelVoltages.yml b/wpimath/src/main/python/semiwrap/controls/DifferentialDriveWheelVoltages.yml new file mode 100644 index 0000000000..e199e03161 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/DifferentialDriveWheelVoltages.yml @@ -0,0 +1,26 @@ +defaults: + subpackage: controller + + +classes: + frc::DifferentialDriveWheelVoltages: + force_no_default_constructor: true + attributes: + left: + right: + +extra_includes: +- wpystruct.h + +inline_code: | + cls_DifferentialDriveWheelVoltages + .def(py::init(), + py::arg("left") = 0, py::arg("right") = 0) + .def("__repr__", [](const DifferentialDriveWheelVoltages *self) { + return "DifferentialDriveWheelVoltages(" + "left=" + std::to_string(self->left()) + "," + "right=" + std::to_string(self->right()) + ")"; + }) + ; + + SetupWPyStruct(cls_DifferentialDriveWheelVoltages); diff --git a/wpimath/src/main/python/semiwrap/controls/ElevatorFeedforward.yml b/wpimath/src/main/python/semiwrap/controls/ElevatorFeedforward.yml new file mode 100644 index 0000000000..e44d36584f --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/ElevatorFeedforward.yml @@ -0,0 +1,39 @@ +defaults: + subpackage: controller + +classes: + frc::ElevatorFeedforward: + force_type_casters: + - units::meters_per_second + - meters_per_second_squared + methods: + ElevatorFeedforward: + overloads: + '': + units::volt_t, units::volt_t, units::unit_t, units::unit_t: + Calculate: + overloads: + units::unit_t, units::unit_t [const]: + ignore: true + units::unit_t, units::unit_t, units::second_t [const]: + ignore: true + units::unit_t [const]: + units::unit_t, units::unit_t [const]: + MaxAchievableVelocity: + MinAchievableVelocity: + MaxAchievableAcceleration: + MinAchievableAcceleration: + GetKs: + GetKg: + GetKv: + GetKa: + SetKs: + SetKg: + SetKv: + SetKa: + +extra_includes: +- wpystruct.h + +inline_code: | + SetupWPyStruct(cls_ElevatorFeedforward); diff --git a/wpimath/src/main/python/semiwrap/controls/EllipticalRegionConstraint.yml b/wpimath/src/main/python/semiwrap/controls/EllipticalRegionConstraint.yml new file mode 100644 index 0000000000..d48f5c2112 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/EllipticalRegionConstraint.yml @@ -0,0 +1,34 @@ +defaults: + subpackage: constraint + +extra_includes: +- PyTrajectoryConstraint.h + +classes: + frc::EllipticalRegionConstraint: + template_params: + - typename Constraint + typealias: + - frc::TrajectoryConstraint::MinMax + methods: + EllipticalRegionConstraint: + overloads: + const Translation2d&, units::meter_t, units::meter_t, const Rotation2d&, const Constraint&: + const Ellipse2d&, const Constraint&: + MaxVelocity: + MinMaxAcceleration: + + template_inline_code: | + cls_EllipticalRegionConstraint + .def_static("fromFeet", [](const Translation2d& center, units::foot_t xWidth, + units::foot_t yWidth, const Rotation2d& rotation, + const Constraint& constraint) { + return std::make_shared>(center, xWidth, yWidth, rotation, constraint); + }, py::arg("center"), py::arg("xWidth"), py::arg("yWidth"), py::arg("rotation"), py::arg("constraint")) + ; + +templates: + EllipticalRegionConstraint: + qualname: frc::EllipticalRegionConstraint + params: + - frc::PyTrajectoryConstraint diff --git a/wpimath/src/main/python/semiwrap/controls/ExponentialProfile.yml b/wpimath/src/main/python/semiwrap/controls/ExponentialProfile.yml new file mode 100644 index 0000000000..eff51e3d04 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/ExponentialProfile.yml @@ -0,0 +1,63 @@ +defaults: + subpackage: trajectory + +classes: + frc::ExponentialProfile: + force_type_casters: + - units::meters_per_second_t + template_params: + - Distance + - Input + methods: + ExponentialProfile: + Calculate: + CalculateInflectionPoint: + overloads: + const State&, const State& [const]: + TimeLeftUntil: + CalculateProfileTiming: + overloads: + const State&, const State& [const]: + frc::ExponentialProfile::Constraints: + attributes: + maxInput: + A: + B: + methods: + Constraints: + overloads: + Input_t, A_t, B_t: + ignore: true + Input_t, kV_t, kA_t: + ignore: true + MaxVelocity: + inline_code: | + .def_static("fromStateSpace", [](Input_t maxInput, A_t a, B_t b) { + return typename frc::ExponentialProfile::Constraints(maxInput, a, b); + }, py::arg("maxInput"), py::arg("a"), py::arg("b")) + .def_static("fromCharacteristics", [](Input_t maxInput, kV_t kv, kA_t ka) { + return typename frc::ExponentialProfile::Constraints(maxInput, kv, ka); + }, py::arg("maxInput"), py::arg("kV"), py::arg("kA")) + + frc::ExponentialProfile::State: + force_no_default_constructor: true + attributes: + position: + velocity: + methods: + operator==: + inline_code: | + .def(py::init()) + frc::ExponentialProfile::ProfileTiming: + attributes: + inflectionTime: + totalTime: + methods: + IsFinished: + +templates: + ExponentialProfileMeterVolts: + qualname: frc::ExponentialProfile + params: + - units::meter + - units::volt diff --git a/wpimath/src/main/python/semiwrap/controls/ExtendedKalmanFilter.yml b/wpimath/src/main/python/semiwrap/controls/ExtendedKalmanFilter.yml new file mode 100644 index 0000000000..27eae06003 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/ExtendedKalmanFilter.yml @@ -0,0 +1,66 @@ +defaults: + subpackage: estimator + +classes: + frc::ExtendedKalmanFilter: + template_params: + - int States + - int Inputs + - int Outputs + methods: + ExtendedKalmanFilter: + overloads: + ? std::function, std::function, const StateArray&, const OutputArray&, units::second_t + : + ? std::function, std::function, const StateArray&, const OutputArray&, std::function, std::function, units::second_t + : + P: + overloads: + '[const]': + int, int [const]: + SetP: + Xhat: + overloads: + '[const]': + int [const]: + SetXhat: + overloads: + const StateVector&: + int, double: + Reset: + Predict: + Correct: + overloads: + const InputVector&, const OutputVector&: + const InputVector&, const OutputVector&, const Matrixd&: + ? const InputVector&, const Vectord&, std::function (const StateVector&, const InputVector&)>, const Matrixd& + : ignore: true + ? const InputVector&, const Vectord&, std::function (const StateVector&, const InputVector&)>, const Matrixd&, std::function (const Vectord&, const Vectord&)>, std::function + : ignore: true + + +templates: + ExtendedKalmanFilter_1_1_1: + qualname: frc::ExtendedKalmanFilter + params: + - 1 + - 1 + - 1 + ExtendedKalmanFilter_2_1_1: + qualname: frc::ExtendedKalmanFilter + params: + - 2 + - 1 + - 1 + ExtendedKalmanFilter_2_1_2: + qualname: frc::ExtendedKalmanFilter + params: + - 2 + - 1 + - 2 + ExtendedKalmanFilter_2_2_2: + qualname: frc::ExtendedKalmanFilter + params: + - 2 + - 2 + - 2 diff --git a/wpimath/src/main/python/semiwrap/controls/HolonomicDriveController.yml b/wpimath/src/main/python/semiwrap/controls/HolonomicDriveController.yml new file mode 100644 index 0000000000..ff1dc52dc2 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/HolonomicDriveController.yml @@ -0,0 +1,23 @@ +defaults: + subpackage: controller + +classes: + frc::HolonomicDriveController: + methods: + HolonomicDriveController: + AtReference: + SetTolerance: + Calculate: + overloads: + const Pose2d&, const Pose2d&, units::meters_per_second_t, const Rotation2d&: + const Pose2d&, const Trajectory::State&, const Rotation2d&: + SetEnabled: + getThetaController: + ignore: true + getXController: + ignore: true + getYController: + ignore: true + GetXController: + GetYController: + GetThetaController: diff --git a/wpimath/src/main/python/semiwrap/controls/ImplicitModelFollower.yml b/wpimath/src/main/python/semiwrap/controls/ImplicitModelFollower.yml new file mode 100644 index 0000000000..e1bf7563b3 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/ImplicitModelFollower.yml @@ -0,0 +1,48 @@ +defaults: + subpackage: controller + +classes: + frc::ImplicitModelFollower: + template_params: + - int States + - int Inputs + methods: + ImplicitModelFollower: + overloads: + const LinearSystem&, const LinearSystem&: + ignore: true + ? const Matrixd&, const Matrixd&, const Matrixd&, const Matrixd& + : + U: + overloads: + '[const]': + int [const]: + Reset: + Calculate: + + template_inline_code: | + cls_ImplicitModelFollower + .def(py::init&, const frc::LinearSystem&>(), + py::arg("plant"), py::arg("plantRef")) + .def(py::init&, const frc::LinearSystem&>(), + py::arg("plant"), py::arg("plantRef")) + .def(py::init&, const frc::LinearSystem&>(), + py::arg("plant"), py::arg("plantRef")) + ; + +templates: + ImplicitModelFollower_1_1: + qualname: frc::ImplicitModelFollower + params: + - 1 + - 1 + ImplicitModelFollower_2_1: + qualname: frc::ImplicitModelFollower + params: + - 2 + - 1 + ImplicitModelFollower_2_2: + qualname: frc::ImplicitModelFollower + params: + - 2 + - 2 diff --git a/wpimath/src/main/python/semiwrap/controls/KalmanFilter.yml b/wpimath/src/main/python/semiwrap/controls/KalmanFilter.yml new file mode 100644 index 0000000000..6ea53930fd --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/KalmanFilter.yml @@ -0,0 +1,62 @@ +defaults: + subpackage: estimator + +classes: + frc::KalmanFilter: + template_params: + - int States + - int Inputs + - int Outputs + methods: + KalmanFilter: + P: + overloads: + '[const]': + int, int [const]: + SetP: + Xhat: + overloads: + '[const]': + int [const]: + SetXhat: + overloads: + const StateVector&: + int, double: + Reset: + Predict: + Correct: + overloads: + const InputVector&, const OutputVector&: + const InputVector&, const OutputVector&, const Matrixd&: + +templates: + KalmanFilter_1_1_1: + qualname: frc::KalmanFilter + params: + - 1 + - 1 + - 1 + KalmanFilter_2_1_1: + qualname: frc::KalmanFilter + params: + - 2 + - 1 + - 1 + KalmanFilter_2_1_2: + qualname: frc::KalmanFilter + params: + - 2 + - 1 + - 2 + KalmanFilter_2_2_2: + qualname: frc::KalmanFilter + params: + - 2 + - 2 + - 2 + KalmanFilter_3_2_3: + qualname: frc::KalmanFilter + params: + - 3 + - 2 + - 3 diff --git a/wpimath/src/main/python/semiwrap/controls/LTVDifferentialDriveController.yml b/wpimath/src/main/python/semiwrap/controls/LTVDifferentialDriveController.yml new file mode 100644 index 0000000000..36abca332f --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/LTVDifferentialDriveController.yml @@ -0,0 +1,14 @@ +defaults: + subpackage: controller + +classes: + frc::LTVDifferentialDriveController: + methods: + LTVDifferentialDriveController: + AtReference: + SetTolerance: + Calculate: + overloads: + ? const Pose2d&, units::meters_per_second_t, units::meters_per_second_t, const Pose2d&, units::meters_per_second_t, units::meters_per_second_t + : + const Pose2d&, units::meters_per_second_t, units::meters_per_second_t, const Trajectory::State&: diff --git a/wpimath/src/main/python/semiwrap/controls/LTVUnicycleController.yml b/wpimath/src/main/python/semiwrap/controls/LTVUnicycleController.yml new file mode 100644 index 0000000000..6611e474f7 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/LTVUnicycleController.yml @@ -0,0 +1,20 @@ +defaults: + subpackage: controller + +classes: + frc::LTVUnicycleController: + methods: + LTVUnicycleController: + overloads: + units::second_t: + const wpi::array&, const wpi::array&, units::second_t: + # param_override: + # maxVelocity: + # default: 9_mps + AtReference: + SetTolerance: + Calculate: + overloads: + const Pose2d&, const Pose2d&, units::meters_per_second_t, units::radians_per_second_t: + const Pose2d&, const Trajectory::State&: + SetEnabled: diff --git a/wpimath/src/main/python/semiwrap/controls/LinearPlantInversionFeedforward.yml b/wpimath/src/main/python/semiwrap/controls/LinearPlantInversionFeedforward.yml new file mode 100644 index 0000000000..93deccc6de --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/LinearPlantInversionFeedforward.yml @@ -0,0 +1,52 @@ +defaults: + subpackage: controller + +classes: + frc::LinearPlantInversionFeedforward: + template_params: + - int States + - int Inputs + methods: + LinearPlantInversionFeedforward: + overloads: + const LinearSystem&, units::second_t: + ignore: true + const Matrixd&, const Matrixd&, units::second_t: + Uff: + overloads: + '[const]': + int [const]: + R: + overloads: + '[const]': + int [const]: + Reset: + overloads: + const StateVector&: + '': + Calculate: + overloads: + const StateVector&: + const StateVector&, const StateVector&: + +templates: + LinearPlantInversionFeedforward_1_1: + qualname: frc::LinearPlantInversionFeedforward + params: + - 1 + - 1 + LinearPlantInversionFeedforward_2_1: + qualname: frc::LinearPlantInversionFeedforward + params: + - 2 + - 1 + LinearPlantInversionFeedforward_2_2: + qualname: frc::LinearPlantInversionFeedforward + params: + - 2 + - 2 + LinearPlantInversionFeedforward_3_2: + qualname: frc::LinearPlantInversionFeedforward + params: + - 3 + - 2 diff --git a/wpimath/src/main/python/semiwrap/controls/LinearQuadraticRegulator.yml b/wpimath/src/main/python/semiwrap/controls/LinearQuadraticRegulator.yml new file mode 100644 index 0000000000..addb967db2 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/LinearQuadraticRegulator.yml @@ -0,0 +1,66 @@ +defaults: + subpackage: controller + +classes: + frc::LinearQuadraticRegulator: + template_params: + - int States + - int Inputs + methods: + LinearQuadraticRegulator: + overloads: + const LinearSystem&, const StateArray&, const InputArray&, units::second_t: + ignore: true + const Matrixd&, const Matrixd&, const StateArray&, const InputArray&, units::second_t: + ? const Matrixd&, const Matrixd&, const Matrixd&, const Matrixd&, units::second_t + : + ? const Matrixd&, const Matrixd&, const Matrixd&, const Matrixd&, const Matrixd&, units::second_t + : + K: + overloads: + '[const]': + int, int [const]: + R: + overloads: + '[const]': + int [const]: + U: + overloads: + '[const]': + int [const]: + Reset: + Calculate: + overloads: + const StateVector&: + const StateVector&, const StateVector&: + LatencyCompensate: + template_impls: + - ['1'] + - ['2'] + template_inline_code: | + cls_LinearQuadraticRegulator + .def(py::init&, const wpi::array&, const wpi::array&, units::second_t>()) + .def(py::init&, const wpi::array&, const wpi::array&, units::second_t>()) + .def(py::init&, const wpi::array&, const wpi::array&, units::second_t>()); + +templates: + LinearQuadraticRegulator_1_1: + qualname: frc::LinearQuadraticRegulator + params: + - 1 + - 1 + LinearQuadraticRegulator_2_1: + qualname: frc::LinearQuadraticRegulator + params: + - 2 + - 1 + LinearQuadraticRegulator_2_2: + qualname: frc::LinearQuadraticRegulator + params: + - 2 + - 2 + LinearQuadraticRegulator_3_2: + qualname: frc::LinearQuadraticRegulator + params: + - 3 + - 2 diff --git a/wpimath/src/main/python/semiwrap/controls/LinearSystem.yml b/wpimath/src/main/python/semiwrap/controls/LinearSystem.yml new file mode 100644 index 0000000000..a96d320ebd --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/LinearSystem.yml @@ -0,0 +1,106 @@ +defaults: + subpackage: system + +classes: + frc::LinearSystem: + template_params: + - int States + - int Inputs + - int Outputs + methods: + LinearSystem: + A: + overloads: + '[const]': + int, int [const]: + B: + overloads: + '[const]': + int, int [const]: + C: + overloads: + '[const]': + int, int [const]: + D: + overloads: + '[const]': + int, int [const]: + CalculateX: + CalculateY: + Slice: + # TODO? + ignore: true + +templates: + LinearSystem_1_1_1: + qualname: frc::LinearSystem + params: + - 1 + - 1 + - 1 + LinearSystem_1_1_2: + qualname: frc::LinearSystem + params: + - 1 + - 1 + - 2 + LinearSystem_1_1_3: + qualname: frc::LinearSystem + params: + - 1 + - 1 + - 3 + LinearSystem_2_1_1: + qualname: frc::LinearSystem + params: + - 2 + - 1 + - 1 + LinearSystem_2_1_2: + qualname: frc::LinearSystem + params: + - 2 + - 1 + - 2 + LinearSystem_2_1_3: + qualname: frc::LinearSystem + params: + - 2 + - 1 + - 3 + LinearSystem_2_2_1: + qualname: frc::LinearSystem + params: + - 2 + - 2 + - 1 + LinearSystem_2_2_2: + qualname: frc::LinearSystem + params: + - 2 + - 2 + - 2 + LinearSystem_2_2_3: + qualname: frc::LinearSystem + params: + - 2 + - 2 + - 3 + LinearSystem_3_2_1: + qualname: frc::LinearSystem + params: + - 3 + - 2 + - 1 + LinearSystem_3_2_2: + qualname: frc::LinearSystem + params: + - 3 + - 2 + - 2 + LinearSystem_3_2_3: + qualname: frc::LinearSystem + params: + - 3 + - 2 + - 3 diff --git a/wpimath/src/main/python/semiwrap/controls/LinearSystemId.yml b/wpimath/src/main/python/semiwrap/controls/LinearSystemId.yml new file mode 100644 index 0000000000..52874e4dd4 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/LinearSystemId.yml @@ -0,0 +1,62 @@ +defaults: + subpackage: plant + +classes: + frc::LinearSystemId: + typealias: + - kv_meters = units::unit_t>> + - kv_radians = units::unit_t>> + - ka_meters = units::unit_t>> + - ka_radians = units::unit_t>> + methods: + ElevatorSystem: + SingleJointedArmSystem: + IdentifyVelocitySystem: + rename: identifyVelocitySystemMeters + cpp_code: | + [](kv_meters kV, ka_meters kA) { + return frc::LinearSystemId::IdentifyVelocitySystem(kV, kA); + } + IdentifyPositionSystem: + rename: identifyPositionSystemMeters + cpp_code: | + [](kv_meters kV, ka_meters kA) { + return frc::LinearSystemId::IdentifyPositionSystem(kV, kA); + } + IdentifyDrivetrainSystem: + overloads: + decltype(1_V/1_mps), decltype(1_V/1_mps_sq), decltype(1_V/1_mps), decltype(1_V/1_mps_sq): + cpp_code: | + [](kv_meters kVlinear, ka_meters kAlinear, kv_meters kVangular, ka_meters kAangular) { + return frc::LinearSystemId::IdentifyDrivetrainSystem(kVlinear, kAlinear, kVangular, kAangular); + } + decltype(1_V/1_mps), decltype(1_V/1_mps_sq), decltype(1_V/1_rad_per_s), decltype(1_V/1_rad_per_s_sq), units::meter_t: + cpp_code: | + [](kv_meters kVlinear, ka_meters kAlinear, kv_radians kVangular, ka_radians kAangular, units::meter_t trackWidth) { + return frc::LinearSystemId::IdentifyDrivetrainSystem(kVlinear, kAlinear, kVangular, kAangular, trackWidth); + } + FlywheelSystem: + DCMotorSystem: + overloads: + DCMotor, units::kilogram_square_meter_t, double: + decltype(1_V/Velocity_t (1)), decltype(1_V/Acceleration_t (1)): + cpp_code: | + [](kv_meters kv, ka_meters ka) { + return frc::LinearSystemId::DCMotorSystem(kv, ka); + } + DrivetrainVelocitySystem: + + inline_code: | + .def_static("DCMotorSystemRadians", [](kv_radians kV, ka_radians kA) { + return frc::LinearSystemId::DCMotorSystem(kV, kA); + }, py::arg("kV"), py::arg("kA"), release_gil() + ) + + .def_static("identifyVelocitySystemRadians", [](kv_radians kV, ka_radians kA) { + return frc::LinearSystemId::IdentifyVelocitySystem(kV, kA); + }, py::arg("kV"), py::arg("kA"), release_gil() + ) + .def_static("identifyPositionSystemRadians", [](kv_radians kV, ka_radians kA) { + return frc::LinearSystemId::IdentifyPositionSystem(kV, kA); + }, py::arg("kV"), py::arg("kA"), release_gil() + ) diff --git a/wpimath/src/main/python/semiwrap/controls/LinearSystemLoop.yml b/wpimath/src/main/python/semiwrap/controls/LinearSystemLoop.yml new file mode 100644 index 0000000000..09d0bc2b91 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/LinearSystemLoop.yml @@ -0,0 +1,79 @@ +defaults: + subpackage: system + +classes: + frc::LinearSystemLoop: + template_params: + - int States + - int Inputs + - int Outputs + methods: + LinearSystemLoop: + overloads: + ? LinearSystem&, LinearQuadraticRegulator&, KalmanFilter&, units::volt_t, units::second_t + : + ? LinearSystem&, LinearQuadraticRegulator&, KalmanFilter&, std::function, units::second_t + : + ? LinearQuadraticRegulator&, const LinearPlantInversionFeedforward&, KalmanFilter&, units::volt_t + : + ? LinearQuadraticRegulator&, const LinearPlantInversionFeedforward&, KalmanFilter&, std::function + : + Xhat: + overloads: + '[const]': + int [const]: + NextR: + overloads: + '[const]': + int [const]: + U: + overloads: + '[const]': + int [const]: + SetXhat: + overloads: + const StateVector&: + int, double: + SetNextR: + Controller: + ignore: true # TODO + Feedforward: + ignore: true # TODO + Observer: + ignore: true # TODO + Reset: + Error: + Correct: + Predict: + ClampInput: +templates: + LinearSystemLoop_1_1_1: + qualname: frc::LinearSystemLoop + params: + - 1 + - 1 + - 1 + LinearSystemLoop_2_1_1: + qualname: frc::LinearSystemLoop + params: + - 2 + - 1 + - 1 + LinearSystemLoop_2_1_2: + qualname: frc::LinearSystemLoop + params: + - 2 + - 1 + - 2 + LinearSystemLoop_2_2_2: + qualname: frc::LinearSystemLoop + params: + - 2 + - 2 + - 2 + LinearSystemLoop_3_2_3: + qualname: frc::LinearSystemLoop + params: + - 3 + - 2 + - 3 diff --git a/wpimath/src/main/python/semiwrap/controls/MaxVelocityConstraint.yml b/wpimath/src/main/python/semiwrap/controls/MaxVelocityConstraint.yml new file mode 100644 index 0000000000..45053558a1 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/MaxVelocityConstraint.yml @@ -0,0 +1,18 @@ +defaults: + subpackage: constraint + +classes: + frc::MaxVelocityConstraint: + typealias: + - frc::TrajectoryConstraint::MinMax + methods: + MaxVelocityConstraint: + MaxVelocity: + MinMaxAcceleration: + +inline_code: |- + cls_MaxVelocityConstraint + .def_static("fromFps", [](units::feet_per_second_t maxVelocity) { + return std::make_shared(maxVelocity); + }, py::arg("maxVelocity")) + ; diff --git a/wpimath/src/main/python/semiwrap/controls/MecanumDriveKinematicsConstraint.yml b/wpimath/src/main/python/semiwrap/controls/MecanumDriveKinematicsConstraint.yml new file mode 100644 index 0000000000..051b732356 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/MecanumDriveKinematicsConstraint.yml @@ -0,0 +1,19 @@ +defaults: + subpackage: constraint + +classes: + frc::MecanumDriveKinematicsConstraint: + typealias: + - frc::TrajectoryConstraint::MinMax + methods: + MecanumDriveKinematicsConstraint: + MaxVelocity: + MinMaxAcceleration: + +inline_code: |- + cls_MecanumDriveKinematicsConstraint + .def_static("fromFps", [](const frc::MecanumDriveKinematics& kinematics, + units::feet_per_second_t maxSpeed) { + return std::make_shared(kinematics, maxSpeed); + }, py::arg("kinematics"), py::arg("maxSpeed")) + ; diff --git a/wpimath/src/main/python/semiwrap/controls/MecanumDrivePoseEstimator.yml b/wpimath/src/main/python/semiwrap/controls/MecanumDrivePoseEstimator.yml new file mode 100644 index 0000000000..f666b31874 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/MecanumDrivePoseEstimator.yml @@ -0,0 +1,38 @@ +defaults: + subpackage: estimator + +classes: + frc::MecanumDrivePoseEstimator: + force_no_trampoline: true + doc: | + This class wraps an Unscented Kalman Filter to fuse latency-compensated + vision measurements with mecanum drive encoder velocity measurements. It will + correct for noisy measurements and encoder drift. It is intended to be an + easy but more accurate drop-in for :class:`MecanumDriveOdometry`. + + :meth:`update` should be called every robot loop. If your loops are faster or + slower than the default of 0.02s, then you should change the nominal delta + time by specifying it in the constructor. + + :meth:`addVisionMeasurement` can be called as infrequently as you want; if you + never call it, then this class will behave mostly like regular encoder + odometry. + + The state-space system used internally has the following states (x), inputs + (u), and outputs (y): + + :math:`x = [x, y, \theta]^T` in the field-coordinate system + containing x position, y position, and heading. + + :math:`u = [v_x, v_y, \omega]^T` containing x velocity, y velocity, + and angular velocity in the field-coordinate system. + + :math:`y = [x, y, \theta]^T` from vision containing x position, y + position, and heading; or :math:`y = [theta]^T` containing gyro + heading. + methods: + MecanumDrivePoseEstimator: + overloads: + MecanumDriveKinematics&, const Rotation2d&, const MecanumDriveWheelPositions&, const Pose2d&: + ? MecanumDriveKinematics&, const Rotation2d&, const MecanumDriveWheelPositions&, const Pose2d&, const wpi::array&, const wpi::array& + : diff --git a/wpimath/src/main/python/semiwrap/controls/MecanumDrivePoseEstimator3d.yml b/wpimath/src/main/python/semiwrap/controls/MecanumDrivePoseEstimator3d.yml new file mode 100644 index 0000000000..3cac61fb3c --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/MecanumDrivePoseEstimator3d.yml @@ -0,0 +1,12 @@ +defaults: + subpackage: estimator + +classes: + frc::MecanumDrivePoseEstimator3d: + force_no_trampoline: true + methods: + MecanumDrivePoseEstimator3d: + overloads: + MecanumDriveKinematics&, const Rotation3d&, const MecanumDriveWheelPositions&, const Pose3d&: + ? MecanumDriveKinematics&, const Rotation3d&, const MecanumDriveWheelPositions&, const Pose3d&, const wpi::array&, const wpi::array& + : diff --git a/wpimath/src/main/python/semiwrap/controls/PIDController.yml b/wpimath/src/main/python/semiwrap/controls/PIDController.yml new file mode 100644 index 0000000000..4da68d4f73 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/PIDController.yml @@ -0,0 +1,45 @@ +defaults: + subpackage: controller + +classes: + frc::PIDController: + ignored_bases: + - wpi::SendableHelper + methods: + PIDController: + param_override: + period: + default: 0.020_s + SetPID: + SetP: + SetI: + SetD: + SetIZone: + GetP: + GetI: + GetD: + GetIZone: + GetPeriod: + GetErrorTolerance: + GetErrorDerivativeTolerance: + GetPositionTolerance: + GetVelocityTolerance: + GetAccumulatedError: + SetSetpoint: + GetSetpoint: + AtSetpoint: + EnableContinuousInput: + DisableContinuousInput: + IsContinuousInputEnabled: + SetIntegratorRange: + SetTolerance: + GetError: + GetErrorDerivative: + GetPositionError: + GetVelocityError: + Calculate: + overloads: + double: + double, double: + Reset: + InitSendable: diff --git a/wpimath/src/main/python/semiwrap/controls/PoseEstimator.yml b/wpimath/src/main/python/semiwrap/controls/PoseEstimator.yml new file mode 100644 index 0000000000..cb83c6530a --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/PoseEstimator.yml @@ -0,0 +1,63 @@ +defaults: + subpackage: estimator + +extra_includes: +- frc/kinematics/DifferentialDriveWheelPositions.h +- frc/kinematics/DifferentialDriveWheelSpeeds.h +- frc/kinematics/MecanumDriveWheelPositions.h +- frc/kinematics/MecanumDriveWheelSpeeds.h +- frc/kinematics/SwerveDriveKinematics.h + + +classes: + frc::PoseEstimator: + template_params: + - WheelSpeeds + - WheelPositions + methods: + PoseEstimator: + SetVisionMeasurementStdDevs: + ResetPosition: + ResetPose: + ResetTranslation: + ResetRotation: + GetEstimatedPosition: + SampleAt: + AddVisionMeasurement: + overloads: + const Pose2d&, units::second_t: + const Pose2d&, units::second_t, const wpi::array&: + Update: + UpdateWithTime: + +templates: + DifferentialDrivePoseEstimatorBase: + qualname: frc::PoseEstimator + params: + - frc::DifferentialDriveWheelSpeeds + - frc::DifferentialDriveWheelPositions + MecanumDrivePoseEstimatorBase: + qualname: frc::PoseEstimator + params: + - frc::MecanumDriveWheelSpeeds + - frc::MecanumDriveWheelPositions + SwerveDrive2PoseEstimatorBase: + qualname: frc::PoseEstimator + params: + - wpi::array + - wpi::array + SwerveDrive3PoseEstimatorBase: + qualname: frc::PoseEstimator + params: + - wpi::array + - wpi::array + SwerveDrive4PoseEstimatorBase: + qualname: frc::PoseEstimator + params: + - wpi::array + - wpi::array + SwerveDrive6PoseEstimatorBase: + qualname: frc::PoseEstimator + params: + - wpi::array + - wpi::array diff --git a/wpimath/src/main/python/semiwrap/controls/PoseEstimator3d.yml b/wpimath/src/main/python/semiwrap/controls/PoseEstimator3d.yml new file mode 100644 index 0000000000..c035480369 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/PoseEstimator3d.yml @@ -0,0 +1,63 @@ +defaults: + subpackage: estimator + +extra_includes: +- frc/kinematics/DifferentialDriveWheelPositions.h +- frc/kinematics/DifferentialDriveWheelSpeeds.h +- frc/kinematics/MecanumDriveWheelPositions.h +- frc/kinematics/MecanumDriveWheelSpeeds.h +- frc/kinematics/SwerveDriveKinematics.h + +classes: + frc::PoseEstimator3d: + template_params: + - WheelSpeeds + - WheelPositions + methods: + PoseEstimator3d: + SetVisionMeasurementStdDevs: + ResetPosition: + ResetPose: + ResetTranslation: + ResetRotation: + GetEstimatedPosition: + SampleAt: + AddVisionMeasurement: + overloads: + const Pose3d&, units::second_t: + const Pose3d&, units::second_t, const wpi::array&: + Update: + UpdateWithTime: + + +templates: + DifferentialDrivePoseEstimator3dBase: + qualname: frc::PoseEstimator3d + params: + - frc::DifferentialDriveWheelSpeeds + - frc::DifferentialDriveWheelPositions + MecanumDrivePoseEstimator3dBase: + qualname: frc::PoseEstimator3d + params: + - frc::MecanumDriveWheelSpeeds + - frc::MecanumDriveWheelPositions + SwerveDrive2PoseEstimator3dBase: + qualname: frc::PoseEstimator3d + params: + - wpi::array + - wpi::array + SwerveDrive3PoseEstimator3dBase: + qualname: frc::PoseEstimator3d + params: + - wpi::array + - wpi::array + SwerveDrive4PoseEstimator3dBase: + qualname: frc::PoseEstimator3d + params: + - wpi::array + - wpi::array + SwerveDrive6PoseEstimator3dBase: + qualname: frc::PoseEstimator3d + params: + - wpi::array + - wpi::array diff --git a/wpimath/src/main/python/semiwrap/controls/ProfiledPIDController.yml b/wpimath/src/main/python/semiwrap/controls/ProfiledPIDController.yml new file mode 100644 index 0000000000..71cf286a0b --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/ProfiledPIDController.yml @@ -0,0 +1,80 @@ +defaults: + subpackage: controller + +functions: + IncrementAndGetProfiledPIDControllerInstances: + ignore: true +classes: + frc::ProfiledPIDController: + force_type_casters: + - units::radians_per_second + template_params: + - Distance + ignored_bases: + - wpi::SendableHelper> + typealias: + - typename frc::ProfiledPIDController::Velocity + - typename frc::ProfiledPIDController::Velocity_t + methods: + ProfiledPIDController: + param_override: + constraints: + x_type: typename TrapezoidProfile::Constraints + period: + default: 0.020_s + SetPID: + SetP: + SetI: + SetD: + SetIZone: + GetP: + GetI: + GetD: + GetIZone: + GetPeriod: + GetPositionTolerance: + GetVelocityTolerance: + GetAccumulatedError: + SetGoal: + overloads: + State: + Distance_t: + GetGoal: + AtGoal: + SetConstraints: + GetConstraints: + GetSetpoint: + AtSetpoint: + EnableContinuousInput: + DisableContinuousInput: + SetIntegratorRange: + SetTolerance: + GetPositionError: + GetVelocityError: + Calculate: + overloads: + Distance_t: + Distance_t, State: + Distance_t, Distance_t: + Distance_t, Distance_t, typename frc::TrapezoidProfile::Constraints: + param_override: + constraints: + x_type: typename TrapezoidProfile::Constraints + Reset: + overloads: + const State&: + Distance_t, Velocity_t: + Distance_t: + InitSendable: + +templates: + ProfiledPIDController: + qualname: frc::ProfiledPIDController + params: + - units::dimensionless::scalar + + # needed for HolonomicDriveController + ProfiledPIDControllerRadians: + qualname: frc::ProfiledPIDController + params: + - units::radian diff --git a/wpimath/src/main/python/semiwrap/controls/RectangularRegionConstraint.yml b/wpimath/src/main/python/semiwrap/controls/RectangularRegionConstraint.yml new file mode 100644 index 0000000000..153423ae98 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/RectangularRegionConstraint.yml @@ -0,0 +1,25 @@ +defaults: + subpackage: constraint + +extra_includes: +- PyTrajectoryConstraint.h + +classes: + frc::RectangularRegionConstraint: + template_params: + - typename Constraint + typealias: + - frc::TrajectoryConstraint::MinMax + methods: + RectangularRegionConstraint: + overloads: + const Translation2d&, const Translation2d&, const Constraint&: + const Rectangle2d&, const Constraint&: + MaxVelocity: + MinMaxAcceleration: + +templates: + RectangularRegionConstraint: + qualname: frc::RectangularRegionConstraint + params: + - frc::PyTrajectoryConstraint diff --git a/wpimath/src/main/python/semiwrap/controls/SimpleMotorFeedforward.yml b/wpimath/src/main/python/semiwrap/controls/SimpleMotorFeedforward.yml new file mode 100644 index 0000000000..5b4e567b0f --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/SimpleMotorFeedforward.yml @@ -0,0 +1,57 @@ +defaults: + subpackage: controller + +extra_includes: +- units/dimensionless.h + +classes: + frc::SimpleMotorFeedforward: + force_type_casters: + - units::meters_per_second + - units::meters_per_second_squared + - units::radians_per_second + - units::radians_per_second_squared + typealias: + - typename frc::SimpleMotorFeedforward::Velocity + - typename frc::SimpleMotorFeedforward::Acceleration + - typename frc::SimpleMotorFeedforward::kv_unit + - typename frc::SimpleMotorFeedforward::ka_unit + template_params: + - Distance + methods: + SimpleMotorFeedforward: + overloads: + '': + units::volt_t, units::unit_t, units::unit_t: + Calculate: + overloads: + units::unit_t [const]: + units::unit_t, units::unit_t [const]: + MaxAchievableVelocity: + MinAchievableVelocity: + MaxAchievableAcceleration: + MinAchievableAcceleration: + GetKs: + GetKv: + GetKa: + GetDt: + SetKs: + SetKv: + SetKa: + +templates: + # Unfortunately this is broken because calculate requires an SI unit + # SimpleMotorFeedforward: + # qualname: frc::SimpleMotorFeedforward + # params: + # - units::dimensionless::scalar + + SimpleMotorFeedforwardMeters: + qualname: frc::SimpleMotorFeedforward + params: + - units::meter + + SimpleMotorFeedforwardRadians: + qualname: frc::SimpleMotorFeedforward + params: + - units::radian diff --git a/wpimath/src/main/python/semiwrap/controls/SimulatedAnnealing.yml b/wpimath/src/main/python/semiwrap/controls/SimulatedAnnealing.yml new file mode 100644 index 0000000000..b13e812cea --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/SimulatedAnnealing.yml @@ -0,0 +1,19 @@ +defaults: + subpackage: optimization + +extra_includes: +- gilsafe_object.h + +classes: + frc::SimulatedAnnealing: + template_params: + - State + methods: + SimulatedAnnealing: + Solve: + +templates: + SimulatedAnnealing: + qualname: frc::SimulatedAnnealing + params: + - semiwrap::gilsafe_object diff --git a/wpimath/src/main/python/semiwrap/controls/SwerveDriveKinematicsConstraint.yml b/wpimath/src/main/python/semiwrap/controls/SwerveDriveKinematicsConstraint.yml new file mode 100644 index 0000000000..b9107a469b --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/SwerveDriveKinematicsConstraint.yml @@ -0,0 +1,38 @@ +defaults: + subpackage: constraint + +classes: + frc::SwerveDriveKinematicsConstraint: + template_params: + - size_t NumModules + typealias: + - frc::TrajectoryConstraint::MinMax + methods: + SwerveDriveKinematicsConstraint: + MaxVelocity: + MinMaxAcceleration: + template_inline_code: | + cls_SwerveDriveKinematicsConstraint + .def_static("fromFps", [](const frc::SwerveDriveKinematics& kinematics, + units::feet_per_second_t maxSpeed) { + return std::make_shared>(kinematics, maxSpeed); + }, py::arg("kinematics"), py::arg("maxSpeed")) + ; + +templates: + SwerveDrive2KinematicsConstraint: + qualname: frc::SwerveDriveKinematicsConstraint + params: + - 2 + SwerveDrive3KinematicsConstraint: + qualname: frc::SwerveDriveKinematicsConstraint + params: + - 3 + SwerveDrive4KinematicsConstraint: + qualname: frc::SwerveDriveKinematicsConstraint + params: + - 4 + SwerveDrive6KinematicsConstraint: + qualname: frc::SwerveDriveKinematicsConstraint + params: + - 6 diff --git a/wpimath/src/main/python/semiwrap/controls/SwerveDrivePoseEstimator.yml b/wpimath/src/main/python/semiwrap/controls/SwerveDrivePoseEstimator.yml new file mode 100644 index 0000000000..eb7f14c0f9 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/SwerveDrivePoseEstimator.yml @@ -0,0 +1,53 @@ +defaults: + subpackage: estimator + +extra_includes: +- frc/kinematics/SwerveModuleState.h + +classes: + frc::SwerveDrivePoseEstimator: + force_no_trampoline: true + doc: | + This class wraps Swerve Drive Odometry to fuse latency-compensated + vision measurements with swerve drive encoder distance measurements. It is + intended to be a drop-in for :class:`SwerveDriveOdometry`. + + :meth:`update` should be called every robot loop. + + :meth:`addVisionMeasurement` can be called as infrequently as you want; if you + never call it, then this class will behave as regular encoder odometry. + + The state-space system used internally has the following states (x) and outputs (y): + + :math:`x = [x, y, \theta]^T` in the field-coordinate system + containing x position, y position, and heading. + + :math:`y = [x, y, \theta]^T` from vision containing x position, y + position, and heading; or :math:`y = [theta]^T` containing gyro + heading. + template_params: + - size_t NumModules + methods: + SwerveDrivePoseEstimator: + overloads: + SwerveDriveKinematics&, const Rotation2d&, const wpi::array&, const Pose2d&: + ? SwerveDriveKinematics&, const Rotation2d&, const wpi::array&, const Pose2d&, const wpi::array&, const wpi::array& + : + +templates: + SwerveDrive2PoseEstimator: + qualname: frc::SwerveDrivePoseEstimator + params: + - 2 + SwerveDrive3PoseEstimator: + qualname: frc::SwerveDrivePoseEstimator + params: + - 3 + SwerveDrive4PoseEstimator: + qualname: frc::SwerveDrivePoseEstimator + params: + - 4 + SwerveDrive6PoseEstimator: + qualname: frc::SwerveDrivePoseEstimator + params: + - 6 diff --git a/wpimath/src/main/python/semiwrap/controls/SwerveDrivePoseEstimator3d.yml b/wpimath/src/main/python/semiwrap/controls/SwerveDrivePoseEstimator3d.yml new file mode 100644 index 0000000000..10300ad46d --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/SwerveDrivePoseEstimator3d.yml @@ -0,0 +1,36 @@ +defaults: + subpackage: estimator + +extra_includes: +- frc/kinematics/SwerveModuleState.h + +classes: + frc::SwerveDrivePoseEstimator3d: + force_no_trampoline: true + template_params: + - size_t NumModules + methods: + SwerveDrivePoseEstimator3d: + overloads: + SwerveDriveKinematics&, const Rotation3d&, const wpi::array&, const Pose3d&: + ? SwerveDriveKinematics&, const Rotation3d&, const wpi::array&, const Pose3d&, const wpi::array&, const wpi::array& + : + + +templates: + SwerveDrive2PoseEstimator3d: + qualname: frc::SwerveDrivePoseEstimator3d + params: + - 2 + SwerveDrive3PoseEstimator3d: + qualname: frc::SwerveDrivePoseEstimator3d + params: + - 3 + SwerveDrive4PoseEstimator3d: + qualname: frc::SwerveDrivePoseEstimator3d + params: + - 4 + SwerveDrive6PoseEstimator3d: + qualname: frc::SwerveDrivePoseEstimator3d + params: + - 6 diff --git a/wpimath/src/main/python/semiwrap/controls/Trajectory.yml b/wpimath/src/main/python/semiwrap/controls/Trajectory.yml new file mode 100644 index 0000000000..d806263936 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/Trajectory.yml @@ -0,0 +1,71 @@ +defaults: + subpackage: trajectory + +extra_includes: +- rpy/geometryToString.h + +functions: + to_json: + ignore: true + from_json: + ignore: true +classes: + frc::Trajectory: + force_type_casters: + - units::curvature_t + methods: + Trajectory: + overloads: + '': + const std::vector&: + TotalTime: + States: + Sample: + TransformBy: + RelativeTo: + InitialPose: + operator+: + operator==: + frc::Trajectory::State: + force_no_default_constructor: true + attributes: + t: + velocity: + acceleration: + pose: + curvature: + methods: + operator==: + Interpolate: + +inline_code: | + cls_State + .def( + py::init< + units::second_t, + units::meters_per_second_t, + units::meters_per_second_squared_t, + Pose2d, + units::curvature_t + >(), + py::arg("t") = 0_s, + py::arg("velocity") = 0_mps, + py::arg("acceleration") = 0_mps_sq, + py::arg("pose") = Pose2d(), + py::arg("curvature") = 0.0 + ) + .def_property_readonly("velocity_fps", [](frc::Trajectory::State * self) -> units::feet_per_second_t { + return self->velocity; + }) + .def_property_readonly("acceleration_fps", [](frc::Trajectory::State * self) -> units::feet_per_second_squared_t { + return self->acceleration; + }) + .def("__repr__", [](frc::Trajectory::State *self) { + return "Trajectory.State(" + "t=" + std::to_string(self->t()) + ", " + "velocity=" + std::to_string(self->velocity()) + ", " + "acceleration=" + std::to_string(self->acceleration()) + ", " + "pose=" + rpy::toString(self->pose) + ", " + "curvature=" + std::to_string(self->curvature()) + ")"; + }) + .def_readwrite("curvature", &frc::Trajectory::State::curvature); diff --git a/wpimath/src/main/python/semiwrap/controls/TrajectoryConfig.yml b/wpimath/src/main/python/semiwrap/controls/TrajectoryConfig.yml new file mode 100644 index 0000000000..ca1d09d828 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/TrajectoryConfig.yml @@ -0,0 +1,74 @@ +defaults: + subpackage: trajectory + +extra_includes: +- PyTrajectoryConstraint.h + +classes: + frc::TrajectoryConfig: + methods: + TrajectoryConfig: + SetStartVelocity: + SetEndVelocity: + SetReversed: + AddConstraint: + template_impls: + - [PyTrajectoryConstraint] + SetKinematics: + overloads: + const DifferentialDriveKinematics&: + MecanumDriveKinematics: + SwerveDriveKinematics&: + ignore: true + StartVelocity: + EndVelocity: + MaxVelocity: + MaxAcceleration: + Constraints: + ignore: true # seems hard, let me know if you need it + IsReversed: + +inline_code: |- + cls_TrajectoryConfig + .def_static("fromFps", [](units::feet_per_second_t maxVelocity, units::feet_per_second_squared_t maxAcceleration) { + return std::make_shared(maxVelocity, maxAcceleration); + }, py::arg("maxVelocity"), py::arg("maxAcceleration")) + + // TODO: robotpy-build bug + + .def("setKinematics", static_cast&)>( + &frc::TrajectoryConfig::SetKinematics<2>), + py::arg("kinematics"), release_gil(), py::doc( + "Adds a swerve drive kinematics constraint to ensure that\n" + "no wheel velocity of a swerve drive goes above the max velocity.\n" + "\n" + ":param kinematics: The swerve drive kinematics.") + ) + + .def("setKinematics", static_cast&)>( + &frc::TrajectoryConfig::SetKinematics<3>), + py::arg("kinematics"), release_gil(), py::doc( + "Adds a swerve drive kinematics constraint to ensure that\n" + "no wheel velocity of a swerve drive goes above the max velocity.\n" + "\n" + ":param kinematics: The swerve drive kinematics.") + ) + + .def("setKinematics", static_cast&)>( + &frc::TrajectoryConfig::SetKinematics<4>), + py::arg("kinematics"), release_gil(), py::doc( + "Adds a swerve drive kinematics constraint to ensure that\n" + "no wheel velocity of a swerve drive goes above the max velocity.\n" + "\n" + ":param kinematics: The swerve drive kinematics.") + ) + + .def("setKinematics", static_cast&)>( + &frc::TrajectoryConfig::SetKinematics<6>), + py::arg("kinematics"), release_gil(), py::doc( + "Adds a swerve drive kinematics constraint to ensure that\n" + "no wheel velocity of a swerve drive goes above the max velocity.\n" + "\n" + ":param kinematics: The swerve drive kinematics.") + ) + ; diff --git a/wpimath/src/main/python/semiwrap/controls/TrajectoryConstraint.yml b/wpimath/src/main/python/semiwrap/controls/TrajectoryConstraint.yml new file mode 100644 index 0000000000..73af5eced4 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/TrajectoryConstraint.yml @@ -0,0 +1,38 @@ +defaults: + subpackage: constraint + +classes: + frc::TrajectoryConstraint: + force_type_casters: + - units::meters_per_second_squared + methods: + TrajectoryConstraint: + MaxVelocity: + MinMaxAcceleration: + frc::TrajectoryConstraint::MinMax: + attributes: + minAcceleration: + maxAcceleration: + inline_code: | + .def(py::init([]( + units::meters_per_second_squared_t minAcceleration, + units::meters_per_second_squared_t maxAcceleration) { + return frc::TrajectoryConstraint::MinMax{minAcceleration, maxAcceleration}; + }), py::arg("minAcceleration"), py::arg("maxAcceleration")) + + .def("__len__", [](const frc::TrajectoryConstraint::MinMax& self) { return 2; }) + .def("__getitem__", [](const frc::TrajectoryConstraint::MinMax& self, int index) { + switch (index) { + case 0: + return self.minAcceleration; + case 1: + return self.maxAcceleration; + default: + throw std::out_of_range("TrajectoryConstraint.MinMax index out of range"); + } + }) + + .def("__repr__", [](const frc::TrajectoryConstraint::MinMax &self) { + return py::str("TrajectoryConstraint.MinMax(minAcceleration={}, maxAcceleration={})").format( + self.minAcceleration, self.maxAcceleration); + }) diff --git a/wpimath/src/main/python/semiwrap/controls/TrajectoryGenerator.yml b/wpimath/src/main/python/semiwrap/controls/TrajectoryGenerator.yml new file mode 100644 index 0000000000..eb441e29fe --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/TrajectoryGenerator.yml @@ -0,0 +1,25 @@ +defaults: + subpackage: trajectory + +extra_includes: +- frc/spline/CubicHermiteSpline.h +- frc/spline/QuinticHermiteSpline.h + +classes: + frc::TrajectoryGenerator: + force_type_casters: + - units::unit_t + - units::curvature_t + methods: + GenerateTrajectory: + overloads: + Spline<3>::ControlVector, const std::vector&, Spline<3>::ControlVector, const TrajectoryConfig&: + const Pose2d&, const std::vector&, const Pose2d&, const TrajectoryConfig&: + std::vector::ControlVector>, const TrajectoryConfig&: + const std::vector&, const TrajectoryConfig&: + SplinePointsFromSplines: + template_impls: + - [CubicHermiteSpline] + - [QuinticHermiteSpline] + SetErrorHandler: + diff --git a/wpimath/src/main/python/semiwrap/controls/TrajectoryParameterizer.yml b/wpimath/src/main/python/semiwrap/controls/TrajectoryParameterizer.yml new file mode 100644 index 0000000000..97f4b524c1 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/TrajectoryParameterizer.yml @@ -0,0 +1,10 @@ +defaults: + subpackage: trajectory + +classes: + frc::TrajectoryParameterizer: + force_type_casters: + - units::unit_t + - units::curvature_t + methods: + TimeParameterizeTrajectory: diff --git a/wpimath/src/main/python/semiwrap/controls/TrapezoidProfile.yml b/wpimath/src/main/python/semiwrap/controls/TrapezoidProfile.yml new file mode 100644 index 0000000000..2122c9f1e7 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/TrapezoidProfile.yml @@ -0,0 +1,106 @@ +defaults: + subpackage: trajectory + +classes: + frc::TrapezoidProfile: + force_type_casters: + - units::second_t + - units::radians_per_second + - units::radians_per_second_squared + template_params: + - Distance + doc: | + A trapezoid-shaped velocity profile. + + While this class can be used for a profiled movement from start to finish, + the intended usage is to filter a reference's dynamics based on trapezoidal + velocity constraints. To compute the reference obeying this constraint, do + the following. + + Initialization:: + + constraints = TrapezoidProfile.Constraints(kMaxV, kMaxA) + previousProfiledReference = initialReference + + Run on update:: + + profile = TrapezoidProfile(constraints, unprofiledReference, previousProfiledReference) + previousProfiledReference = profile.calculate(timeSincePreviousUpdate) + + where ``unprofiledReference`` is free to change between calls. Note that + when the unprofiled reference is within the constraints, + :meth:`calculate` returns the unprofiled reference unchanged. + + Otherwise, a timer can be started to provide monotonic values for + ``calculate()`` and to determine when the profile has completed via + :meth:`isFinished`. + methods: + TrapezoidProfile: + param_override: + constraints: + x_type: typename TrapezoidProfile::Constraints + goal: + x_type: typename TrapezoidProfile::State + initial: + x_type: typename TrapezoidProfile::State + Calculate: + TimeLeftUntil: + TotalTime: + IsFinished: + template_inline_code: | + { + std::string clsNameCopy = clsName; + + cls_Constraints + .def("__repr__", [clsNameCopy](const Constraints &self) { + return clsNameCopy + ".Constraints(" + "maxVelocity=" + std::to_string(self.maxVelocity()) + ", " + "maxAcceleration=" + std::to_string(self.maxAcceleration()) + ")"; + }); + + cls_State + .def( + py::init(), + py::arg("position") = 0, + py::arg("velocity") = 0 + ) + .def("__repr__", [clsNameCopy](const State &self) { + return clsNameCopy + ".State(" + "position=" + std::to_string(self.position()) + ", " + "velocity=" + std::to_string(self.velocity()) + ")"; + }); + } + frc::TrapezoidProfile::Constraints: + attributes: + maxVelocity: + maxAcceleration: + methods: + Constraints: + overloads: + '': + ignore: true + Velocity_t, Acceleration_t: + param_override: + maxVelocity: + default: '0' + maxAcceleration: + default: '0' + frc::TrapezoidProfile::State: + force_no_default_constructor: true + attributes: + position: + velocity: + methods: + operator==: + cpp_code: py::self == State() +templates: + TrapezoidProfile: + qualname: frc::TrapezoidProfile + params: + - units::dimensionless::scalar + + # needed for HolonomicDriveController + TrapezoidProfileRadians: + qualname: frc::TrapezoidProfile + params: + - units::radian diff --git a/wpimath/src/main/python/semiwrap/controls/TravelingSalesman.yml b/wpimath/src/main/python/semiwrap/controls/TravelingSalesman.yml new file mode 100644 index 0000000000..ad40d99f46 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/controls/TravelingSalesman.yml @@ -0,0 +1,15 @@ +defaults: + subpackage: path + +classes: + frc::TravelingSalesman: + methods: + TravelingSalesman: + overloads: + '': + std::function: + Solve: + overloads: + const wpi::array&, int: + ignore: true + std::span, int: diff --git a/wpimath/src/main/python/semiwrap/filter/Debouncer.yml b/wpimath/src/main/python/semiwrap/filter/Debouncer.yml new file mode 100644 index 0000000000..fd98a0a50f --- /dev/null +++ b/wpimath/src/main/python/semiwrap/filter/Debouncer.yml @@ -0,0 +1,14 @@ +classes: + frc::Debouncer: + enums: + DebounceType: + methods: + Debouncer: + param_override: + type: + default: frc::Debouncer::DebounceType::kRising + Calculate: + SetDebounceTime: + GetDebounceTime: + SetDebounceType: + GetDebounceType: diff --git a/wpimath/src/main/python/semiwrap/filter/LinearFilter.yml b/wpimath/src/main/python/semiwrap/filter/LinearFilter.yml new file mode 100644 index 0000000000..98b044af4f --- /dev/null +++ b/wpimath/src/main/python/semiwrap/filter/LinearFilter.yml @@ -0,0 +1,29 @@ +classes: + frc::LinearFilter: + template_params: + - T + methods: + LinearFilter: + overloads: + std::span, std::span: + std::initializer_list, std::initializer_list: + ignore: true + SinglePoleIIR: + HighPass: + MovingAverage: + Reset: + overloads: + '': + std::span, std::span: + Calculate: + LastValue: + BackwardFiniteDifference: + ignore: true # TODO: template_impls + FiniteDifference: + ignore: true # TODO: template_impls + +templates: + LinearFilter: + qualname: frc::LinearFilter + params: + - double diff --git a/wpimath/src/main/python/semiwrap/filter/MedianFilter.yml b/wpimath/src/main/python/semiwrap/filter/MedianFilter.yml new file mode 100644 index 0000000000..28f21609f4 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/filter/MedianFilter.yml @@ -0,0 +1,15 @@ +classes: + frc::MedianFilter: + template_params: + - T + methods: + MedianFilter: + Calculate: + LastValue: + Reset: + +templates: + MedianFilter: + qualname: frc::MedianFilter + params: + - double diff --git a/wpimath/src/main/python/semiwrap/filter/SlewRateLimiter.yml b/wpimath/src/main/python/semiwrap/filter/SlewRateLimiter.yml new file mode 100644 index 0000000000..f174c90762 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/filter/SlewRateLimiter.yml @@ -0,0 +1,21 @@ +classes: + frc::SlewRateLimiter: + template_params: + - Unit + typealias: + - Unit_t = units::unit_t + - Rate = units::compound_unit> + methods: + SlewRateLimiter: + overloads: + Rate_t, Rate_t, Unit_t: + Rate_t: + Calculate: + LastValue: + Reset: + +templates: + SlewRateLimiter: + qualname: frc::SlewRateLimiter + params: + - units::dimensionless::scalar diff --git a/wpimath/src/main/python/semiwrap/geometry/CoordinateAxis.yml b/wpimath/src/main/python/semiwrap/geometry/CoordinateAxis.yml new file mode 100644 index 0000000000..98f03e5d72 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/geometry/CoordinateAxis.yml @@ -0,0 +1,10 @@ +classes: + frc::CoordinateAxis: + methods: + CoordinateAxis: + N: + S: + E: + W: + U: + D: diff --git a/wpimath/src/main/python/semiwrap/geometry/CoordinateSystem.yml b/wpimath/src/main/python/semiwrap/geometry/CoordinateSystem.yml new file mode 100644 index 0000000000..b633e9e34b --- /dev/null +++ b/wpimath/src/main/python/semiwrap/geometry/CoordinateSystem.yml @@ -0,0 +1,13 @@ +classes: + frc::CoordinateSystem: + methods: + CoordinateSystem: + NWU: + EDN: + NED: + Convert: + overloads: + const Translation3d&, const CoordinateSystem&, const CoordinateSystem&: + const Rotation3d&, const CoordinateSystem&, const CoordinateSystem&: + const Pose3d&, const CoordinateSystem&, const CoordinateSystem&: + const Transform3d&, const CoordinateSystem&, const CoordinateSystem&: diff --git a/wpimath/src/main/python/semiwrap/geometry/Ellipse2d.yml b/wpimath/src/main/python/semiwrap/geometry/Ellipse2d.yml new file mode 100644 index 0000000000..1d9bb7ed76 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/geometry/Ellipse2d.yml @@ -0,0 +1,46 @@ +extra_includes: +- rpy/geometryToString.h +- wpystruct.h + +classes: + frc::Ellipse2d: + force_type_casters: + - units::foot_t + - units::meter_t + methods: + Ellipse2d: + overloads: + const Pose2d&, units::meter_t, units::meter_t: + const Translation2d&, double: + Center: + Rotation: + XSemiAxis: + ignore: true + YSemiAxis: + ignore: true + FocalPoints: + TransformBy: + RotateBy: + Intersects: + Contains: + Distance: + operator==: + Nearest: + +inline_code: |- + cls_Ellipse2d + .def_static("fromFeet", [](const Pose2d& center, units::foot_t xSemiAxis, units::foot_t ySemiAxis) { + return std::make_unique(center, xSemiAxis, ySemiAxis); + }, py::arg("center"), py::arg("xSemiAxis"), py::arg("ySemiAxis")) + .def_property_readonly("xsemiaxis", &Ellipse2d::XSemiAxis) + .def_property_readonly("ysemiaxis", &Ellipse2d::YSemiAxis) + .def_property_readonly("xsemiaxis_feet", [](Ellipse2d &self) -> units::foot_t { + return self.XSemiAxis(); + }) + .def_property_readonly("ysemiaxis_feet", [](Ellipse2d &self) -> units::foot_t { + return self.YSemiAxis(); + }) + .def("__repr__", py::overload_cast(&rpy::toString)); + + + SetupWPyStruct(cls_Ellipse2d); diff --git a/wpimath/src/main/python/semiwrap/geometry/Pose2d.yml b/wpimath/src/main/python/semiwrap/geometry/Pose2d.yml new file mode 100644 index 0000000000..0c95c98613 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/geometry/Pose2d.yml @@ -0,0 +1,68 @@ +extra_includes: +- rpy/geometryToString.h +- wpystruct.h + +functions: + to_json: + ignore: true + from_json: + ignore: true +classes: + frc::Pose2d: + force_type_casters: + - units::foot_t + - units::meter_t + - units::radian_t + methods: + Pose2d: + overloads: + '': + Translation2d, Rotation2d: + units::meter_t, units::meter_t, Rotation2d: + const Eigen::Matrix3d&: + rename: fromMatrix + keepalive: [] + Translation: + Rotation: + RotateBy: + TransformBy: + RelativeTo: + Exp: + Log: + ToMatrix: + Nearest: + overloads: + std::span [const]: + std::initializer_list [const]: + ignore: true + X: + Y: + operator+: + operator-: + operator==: + operator*: + operator/: + RotateAround: + +inline_code: | + cls_Pose2d + .def_static("fromFeet", [](units::foot_t x, units::foot_t y, Rotation2d r) { + return std::make_unique(x, y, r); + }, py::arg("x"), py::arg("y"), py::arg("r")) + .def_static("fromFeet", [](units::foot_t x, units::foot_t y, units::radian_t angle) { + return std::make_unique(x, y, Rotation2d(angle)); + }, py::arg("x"), py::arg("y"), py::arg("angle")) + .def(py::init([](units::meter_t x, units::meter_t y, units::radian_t angle) { + return std::make_unique(x, y, angle); + }), py::arg("x"), py::arg("y"), py::arg("angle")) + .def_property_readonly("x", &Pose2d::X) + .def_property_readonly("y", &Pose2d::Y) + .def_property_readonly("x_feet", [](Pose2d * self) -> units::foot_t { + return self->X(); + }) + .def_property_readonly("y_feet", [](Pose2d * self) -> units::foot_t { + return self->Y(); + }) + .def("__repr__", py::overload_cast(&rpy::toString)); + + SetupWPyStruct(cls_Pose2d); diff --git a/wpimath/src/main/python/semiwrap/geometry/Pose3d.yml b/wpimath/src/main/python/semiwrap/geometry/Pose3d.yml new file mode 100644 index 0000000000..7e9909dc5b --- /dev/null +++ b/wpimath/src/main/python/semiwrap/geometry/Pose3d.yml @@ -0,0 +1,71 @@ +extra_includes: +- rpy/geometryToString.h +- wpystruct.h + +functions: + to_json: + ignore: true + from_json: + ignore: true + RotationVectorToMatrix: + overloads: + const ct_vector3d&: + ignore: true + const Eigen::Vector3d&: +classes: + frc::Pose3d: + methods: + Pose3d: + overloads: + '': + Translation3d, Rotation3d: + units::meter_t, units::meter_t, units::meter_t, Rotation3d: + const Pose2d&: + keepalive: [] + const Eigen::Matrix4d&: + rename: fromMatrix + keepalive: [] + operator+: + operator-: + operator==: + Translation: + X: + Y: + Z: + Rotation: + operator*: + operator/: + RotateBy: + RotateAround: + TransformBy: + RelativeTo: + Exp: + Log: + ToMatrix: + ToPose2d: + Nearest: + overloads: + std::span [const]: + std::initializer_list [const]: + ignore: true + +inline_code: | + cls_Pose3d + .def_static("fromFeet", [](units::foot_t x, units::foot_t y, units::foot_t z, Rotation3d r) { + return std::make_unique(x, y, z, r); + }, py::arg("x"), py::arg("y"), py::arg("z"), py::arg("r")) + .def_property_readonly("x", &Pose3d::X) + .def_property_readonly("y", &Pose3d::Y) + .def_property_readonly("z", &Pose3d::Z) + .def_property_readonly("x_feet", [](const Pose3d * self) -> units::foot_t { + return self->X(); + }) + .def_property_readonly("y_feet", [](const Pose3d * self) -> units::foot_t { + return self->Y(); + }) + .def_property_readonly("z_feet", [](const Pose3d * self) -> units::foot_t { + return self->Z(); + }) + .def("__repr__", py::overload_cast(&rpy::toString)); + + SetupWPyStruct(cls_Pose3d); diff --git a/wpimath/src/main/python/semiwrap/geometry/Quaternion.yml b/wpimath/src/main/python/semiwrap/geometry/Quaternion.yml new file mode 100644 index 0000000000..74886f4d76 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/geometry/Quaternion.yml @@ -0,0 +1,50 @@ +extra_includes: +- rpy/geometryToString.h +- wpystruct.h + +functions: + to_json: + ignore: true + from_json: + ignore: true +classes: + frc::Quaternion: + methods: + Quaternion: + overloads: + '': + double, double, double, double: + operator+: + operator-: + operator*: + overloads: + double [const]: + const Quaternion& [const]: + operator/: + operator==: + Dot: + Conjugate: + Inverse: + Normalize: + Norm: + Pow: + Exp: + overloads: + const Quaternion& [const]: + '[const]': + Log: + overloads: + const Quaternion& [const]: + '[const]': + W: + X: + Y: + Z: + ToRotationVector: + FromRotationVector: + +inline_code: | + cls_Quaternion + .def("__repr__", py::overload_cast(&rpy::toString)); + + SetupWPyStruct(cls_Quaternion); diff --git a/wpimath/src/main/python/semiwrap/geometry/Rectangle2d.yml b/wpimath/src/main/python/semiwrap/geometry/Rectangle2d.yml new file mode 100644 index 0000000000..bb8c895f77 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/geometry/Rectangle2d.yml @@ -0,0 +1,45 @@ +extra_includes: +- rpy/geometryToString.h +- wpystruct.h + +classes: + frc::Rectangle2d: + force_type_casters: + - units::foot_t + - units::meter_t + methods: + Rectangle2d: + overloads: + const Pose2d&, units::meter_t, units::meter_t: + const Translation2d&, const Translation2d&: + Center: + Rotation: + XWidth: + ignore: true + YWidth: + ignore: true + TransformBy: + RotateBy: + Intersects: + Contains: + Distance: + operator==: + Nearest: + +inline_code: |- + cls_Rectangle2d + .def_static("fromFeet", [](const Pose2d& center, units::foot_t xWidth, units::foot_t yWidth) { + return std::make_unique(center, xWidth, yWidth); + }, py::arg("center"), py::arg("xWidth"), py::arg("yWidth")) + .def_property_readonly("xwidth", &Rectangle2d::XWidth) + .def_property_readonly("ywidth", &Rectangle2d::YWidth) + .def_property_readonly("xwidth_feet", [](Rectangle2d &self) -> units::foot_t { + return self.XWidth(); + }) + .def_property_readonly("ywidth_feet", [](Rectangle2d &self) -> units::foot_t { + return self.YWidth(); + }) + .def("__repr__", py::overload_cast(&rpy::toString)); + + + SetupWPyStruct(cls_Rectangle2d); diff --git a/wpimath/src/main/python/semiwrap/geometry/Rotation2d.yml b/wpimath/src/main/python/semiwrap/geometry/Rotation2d.yml new file mode 100644 index 0000000000..162c1a77c6 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/geometry/Rotation2d.yml @@ -0,0 +1,55 @@ +extra_includes: +- rpy/geometryToString.h +- wpystruct.h + +functions: + to_json: + ignore: true + from_json: + ignore: true +classes: + frc::Rotation2d: + methods: + Rotation2d: + overloads: + '': + auto: + doc: | + Constructs a Rotation2d with the given radian value. + :param value: The value of the angle in radians. + param_override: + value: + x_type: units::radian_t + template_impls: + - [units::radian_t] + double, double: + const Eigen::Matrix2d&: + rename: fromMatrix + keepalive: [] + RotateBy: + ToMatrix: + Radians: + Degrees: + Cos: + Sin: + Tan: + operator+: + operator-: + overloads: + const Rotation2d& [const]: + '[const]': + operator*: + operator/: + operator==: + +inline_code: | + cls_Rotation2d + .def_static("fromDegrees", [](units::degree_t value) { + return std::make_unique(value); + }, py::arg("value")) + .def_static("fromRotations", [](units::turn_t value) { + return std::make_unique(value); + }) + .def("__repr__", py::overload_cast(&rpy::toString)); + + SetupWPyStruct(cls_Rotation2d); diff --git a/wpimath/src/main/python/semiwrap/geometry/Rotation3d.yml b/wpimath/src/main/python/semiwrap/geometry/Rotation3d.yml new file mode 100644 index 0000000000..881a995710 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/geometry/Rotation3d.yml @@ -0,0 +1,71 @@ +extra_includes: +- rpy/geometryToString.h +- wpystruct.h + +functions: + to_json: + ignore: true + from_json: + ignore: true +classes: + frc::Rotation3d: + methods: + Rotation3d: + overloads: + '': + const Quaternion&: + keepalive: [] + units::radian_t, units::radian_t, units::radian_t: + const Eigen::Vector3d&, units::radian_t: + keepalive: [] + const Eigen::Vector3d&: + keepalive: [] + const Eigen::Matrix3d&: + keepalive: [] + const Eigen::Vector3d&, const Eigen::Vector3d&: + keepalive: [] + const Rotation2d&: + keepalive: [] + operator+: + operator-: + overloads: + const Rotation3d& [const]: + '[const]': + operator*: + operator/: + operator==: + RotateBy: + GetQuaternion: + X: + Y: + Z: + Axis: + Angle: + ToMatrix: + ToRotation2d: + ToVector: + +inline_code: | + cls_Rotation3d + .def_static("fromDegrees", [](units::degree_t roll, units::degree_t pitch, units::degree_t yaw) { + return std::make_unique(roll, pitch, yaw); + }, py::arg("roll"), py::arg("pitch"), py::arg("yaw")) + .def_property_readonly("x", &Rotation3d::X) + .def_property_readonly("y", &Rotation3d::Y) + .def_property_readonly("z", &Rotation3d::Z) + .def_property_readonly("angle", &Rotation3d::Angle) + .def_property_readonly("x_degrees", [](const Rotation3d * self) -> units::degree_t { + return self->X(); + }) + .def_property_readonly("y_degrees", [](const Rotation3d * self) -> units::degree_t { + return self->Y(); + }) + .def_property_readonly("z_degrees", [](const Rotation3d * self) -> units::degree_t { + return self->Z(); + }) + .def_property_readonly("angle_degrees", [](const Rotation3d * self) -> units::degree_t { + return self->Angle(); + }) + .def("__repr__", py::overload_cast(&rpy::toString)); + + SetupWPyStruct(cls_Rotation3d); diff --git a/wpimath/src/main/python/semiwrap/geometry/Transform2d.yml b/wpimath/src/main/python/semiwrap/geometry/Transform2d.yml new file mode 100644 index 0000000000..3da1ea1e5f --- /dev/null +++ b/wpimath/src/main/python/semiwrap/geometry/Transform2d.yml @@ -0,0 +1,53 @@ +extra_includes: +- frc/geometry/Pose2d.h +- rpy/geometryToString.h +- wpystruct.h + +classes: + frc::Transform2d: + force_type_casters: + - units::foot_t + - units::meter_t + - units::radian_t + methods: + Transform2d: + overloads: + const Pose2d&, const Pose2d&: + keepalive: [] + Translation2d, Rotation2d: + units::meter_t, units::meter_t, Rotation2d: + const Eigen::Matrix3d&: + rename: fromMatrix + keepalive: [] + '': + Translation: + X: + Y: + ToMatrix: + Rotation: + Inverse: + operator*: + operator/: + operator==: + operator+: + +inline_code: | + cls_Transform2d + .def_static("fromFeet", [](units::foot_t x, units::foot_t y, units::radian_t angle){ + return std::make_unique(Translation2d(x, y), Rotation2d(angle)); + }, py::arg("x"), py::arg("y"), py::arg("angle")) + .def(py::init([](units::meter_t x, units::meter_t y, units::radian_t angle) { + return std::make_unique(Translation2d(x, y), Rotation2d(angle)); + }), py::arg("x"), py::arg("y"), py::arg("angle")) + .def_property_readonly("x", &Transform2d::X) + .def_property_readonly("y", &Transform2d::Y) + .def_property_readonly("x_feet", [](Transform2d * self) -> units::foot_t { + return self->X(); + }) + .def_property_readonly("y_feet", [](Transform2d * self) -> units::foot_t { + return self->Y(); + }) + .def("__repr__", py::overload_cast(&rpy::toString)); + ; + + SetupWPyStruct(cls_Transform2d); diff --git a/wpimath/src/main/python/semiwrap/geometry/Transform3d.yml b/wpimath/src/main/python/semiwrap/geometry/Transform3d.yml new file mode 100644 index 0000000000..d48cbd5cc1 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/geometry/Transform3d.yml @@ -0,0 +1,49 @@ +extra_includes: +- rpy/geometryToString.h +- wpystruct.h + +classes: + frc::Transform3d: + methods: + Transform3d: + overloads: + const Pose3d&, const Pose3d&: + keepalive: [] + Translation3d, Rotation3d: + units::meter_t, units::meter_t, units::meter_t, Rotation3d: + const Eigen::Matrix4d&: + rename: fromMatrix + keepalive: [] + '': + const frc::Transform2d&: + keepalive: [] + Translation: + X: + Y: + Z: + ToMatrix: + Rotation: + Inverse: + operator*: + operator/: + operator+: + operator==: + + +inline_code: | + cls_Transform3d + .def_property_readonly("x", &Transform3d::X) + .def_property_readonly("y", &Transform3d::Y) + .def_property_readonly("z", &Transform3d::Z) + .def_property_readonly("x_feet", [](const Transform3d * self) -> units::foot_t { + return self->X(); + }) + .def_property_readonly("y_feet", [](const Transform3d * self) -> units::foot_t { + return self->Y(); + }) + .def_property_readonly("z_feet", [](const Transform3d * self) -> units::foot_t { + return self->Z(); + }) + .def("__repr__", py::overload_cast(&rpy::toString)); + + SetupWPyStruct(cls_Transform3d); diff --git a/wpimath/src/main/python/semiwrap/geometry/Translation2d.yml b/wpimath/src/main/python/semiwrap/geometry/Translation2d.yml new file mode 100644 index 0000000000..faa5be02c4 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/geometry/Translation2d.yml @@ -0,0 +1,78 @@ +extra_includes: +- rpy/geometryToString.h +- wpystruct.h +- pybind11/eigen.h + +functions: + to_json: + ignore: true + from_json: + ignore: true +classes: + frc::Translation2d: + methods: + Translation2d: + overloads: + '': + units::meter_t, units::meter_t: + units::meter_t, const Rotation2d&: + const Eigen::Vector2d&: + Distance: + X: + Y: + ToVector: + Norm: + Angle: + RotateBy: + RotateAround: + Nearest: + overloads: + std::span [const]: + std::initializer_list [const]: + ignore: true + operator+: + operator-: + overloads: + const Translation2d& [const]: + '[const]': + operator*: + operator/: + operator==: + +inline_code: | + cls_Translation2d + .def_static("fromFeet", [](units::foot_t x, units::foot_t y){ + return std::make_unique(x, y); + }, py::arg("x"), py::arg("y")) + .def_static("fromFeet", [](units::foot_t distance, const Rotation2d &angle) { + return std::make_unique(distance, angle); + }, py::arg("distance"), py::arg("angle")) + .def_property_readonly("x", &Translation2d::X) + .def_property_readonly("y", &Translation2d::Y) + .def_property_readonly("x_feet", [](Translation2d * self) -> units::foot_t { + return self->X(); + }) + .def_property_readonly("y_feet", [](Translation2d * self) -> units::foot_t { + return self->Y(); + }) + .def("distanceFeet", [](Translation2d * self, const Translation2d &other) -> units::foot_t { + return self->Distance(other); + }) + .def("normFeet", [](Translation2d * self) -> units::foot_t { + return self->Norm(); + }) + .def("__abs__", &Translation2d::Norm) + .def("__len__", [](const Translation2d& self) { return 2; }) + .def("__getitem__", [](const Translation2d& self, int index) { + switch (index) { + case 0: + return self.X(); + case 1: + return self.Y(); + default: + throw std::out_of_range("Translation2d index out of range"); + } + }) + .def("__repr__", py::overload_cast(&rpy::toString)); + + SetupWPyStruct(cls_Translation2d); diff --git a/wpimath/src/main/python/semiwrap/geometry/Translation3d.yml b/wpimath/src/main/python/semiwrap/geometry/Translation3d.yml new file mode 100644 index 0000000000..b728732ddf --- /dev/null +++ b/wpimath/src/main/python/semiwrap/geometry/Translation3d.yml @@ -0,0 +1,83 @@ +extra_includes: +- rpy/geometryToString.h +- wpystruct.h +- pybind11/eigen.h + +functions: + to_json: + ignore: true + from_json: + ignore: true +classes: + frc::Translation3d: + methods: + Translation3d: + overloads: + '': + units::meter_t, units::meter_t, units::meter_t: + units::meter_t, const Rotation3d&: + const Eigen::Vector3d&: + const Translation2d&: + Distance: + X: + Y: + Z: + ToVector: + Norm: + RotateBy: + ToTranslation2d: + operator+: + operator-: + overloads: + const Translation3d& [const]: + '[const]': + operator*: + operator/: + operator==: + Nearest: + overloads: + std::span [const]: + std::initializer_list [const]: + ignore: true + + RotateAround: +inline_code: | + cls_Translation3d + .def_static("fromFeet", [](units::foot_t x, units::foot_t y, units::foot_t z){ + return std::make_unique(x, y, z); + }, py::arg("x"), py::arg("y"), py::arg("z")) + .def_property_readonly("x", &Translation3d::X) + .def_property_readonly("y", &Translation3d::Y) + .def_property_readonly("z", &Translation3d::Z) + .def_property_readonly("x_feet", [](const Translation3d * self) -> units::foot_t { + return self->X(); + }) + .def_property_readonly("y_feet", [](const Translation3d * self) -> units::foot_t { + return self->Y(); + }) + .def_property_readonly("z_feet", [](const Translation3d * self) -> units::foot_t { + return self->Z(); + }) + .def("distanceFeet", [](Translation3d * self, const Translation3d &other) -> units::foot_t { + return self->Distance(other); + }) + .def("normFeet", [](const Translation3d * self) -> units::foot_t { + return self->Norm(); + }) + .def("__abs__", &Translation3d::Norm) + .def("__len__", [](const Translation3d& self) { return 3; }) + .def("__getitem__", [](const Translation3d& self, int index) { + switch (index) { + case 0: + return self.X(); + case 1: + return self.Y(); + case 2: + return self.Z(); + default: + throw std::out_of_range("Translation3d index out of range"); + } + }) + .def("__repr__", py::overload_cast(&rpy::toString)); + + SetupWPyStruct(cls_Translation3d); diff --git a/wpimath/src/main/python/semiwrap/geometry/Twist2d.yml b/wpimath/src/main/python/semiwrap/geometry/Twist2d.yml new file mode 100644 index 0000000000..f634c2c77d --- /dev/null +++ b/wpimath/src/main/python/semiwrap/geometry/Twist2d.yml @@ -0,0 +1,55 @@ +extra_includes: +- wpystruct.h + +classes: + frc::Twist2d: + force_no_default_constructor: true + attributes: + dx: + dy: + dtheta: + methods: + operator==: + operator*: + +inline_code: | + cls_Twist2d + .def( + py::init(), + py::arg("dx") = 0, py::arg("dy") = 0, py::arg("dtheta") = 0 + ) + .def_static("fromFeet", [](units::foot_t dx, units::foot_t dy, units::radian_t dtheta){ + return Twist2d{dx, dy, dtheta}; + }, py::arg("dx") = 0, py::arg("dy") = 0, py::arg("dtheta") = 0) + .def_property("dx_feet", + [](Twist2d * self) -> units::foot_t { + return self->dx; + }, + [](Twist2d * self, units::foot_t dx) { + self->dx = dx; + } + ) + .def_property("dy_feet", + [](Twist2d * self) -> units::foot_t { + return self->dy; + }, + [](Twist2d * self, units::foot_t dy) { + self->dy = dy; + } + ) + .def_property("dtheta_degrees", + [](Twist2d * self) -> units::degree_t { + return self->dtheta; + }, + [](Twist2d * self, units::degree_t dtheta) { + self->dtheta = dtheta; + } + ) + .def("__repr__", [](const Twist2d &tw) -> std::string { + return "Twist2d(dx=" + std::to_string(tw.dx()) + ", " + "dy=" + std::to_string(tw.dy()) + ", " + "dtheta=" + std::to_string(tw.dtheta()) + ")"; + }) + ; + + SetupWPyStruct(cls_Twist2d); diff --git a/wpimath/src/main/python/semiwrap/geometry/Twist3d.yml b/wpimath/src/main/python/semiwrap/geometry/Twist3d.yml new file mode 100644 index 0000000000..1d4b270dba --- /dev/null +++ b/wpimath/src/main/python/semiwrap/geometry/Twist3d.yml @@ -0,0 +1,90 @@ +extra_includes: +- wpystruct.h + +classes: + frc::Twist3d: + force_no_default_constructor: true + attributes: + dx: + dy: + dz: + rx: + ry: + rz: + methods: + operator==: + operator*: + +inline_code: |- + cls_Twist3d + .def( + py::init(), + py::arg("dx") = 0, py::arg("dy") = 0, py::arg("dz") = 0, + py::arg("rx") = 0, py::arg("ry") = 0, py::arg("rz") = 0) + .def_static("fromFeet", []( + units::foot_t dx, units::foot_t dy, units::foot_t dz, + units::radian_t rx, units::radian_t ry, units::radian_t rz){ + return Twist3d{dx, dy, dz, rx, ry, rz}; + }, + py::arg("dx") = 0, py::arg("dy") = 0, py::arg("dz") = 0, + py::arg("rx") = 0, py::arg("ry") = 0, py::arg("rz") = 0) + .def_property("dx_feet", + [](Twist3d * self) -> units::foot_t { + return self->dx; + }, + [](Twist3d * self, units::foot_t dx) { + self->dx = dx; + } + ) + .def_property("dy_feet", + [](Twist3d * self) -> units::foot_t { + return self->dy; + }, + [](Twist3d * self, units::foot_t dy) { + self->dy = dy; + } + ) + .def_property("dz_feet", + [](Twist3d * self) -> units::foot_t { + return self->dz; + }, + [](Twist3d * self, units::foot_t dz) { + self->dz = dz; + } + ) + .def_property("rx_degrees", + [](Twist3d * self) -> units::degree_t { + return self->rx; + }, + [](Twist3d * self, units::degree_t rx) { + self->rx = rx; + } + ) + .def_property("ry_degrees", + [](Twist3d * self) -> units::degree_t { + return self->ry; + }, + [](Twist3d * self, units::degree_t ry) { + self->ry = ry; + } + ) + .def_property("rz_degrees", + [](Twist3d * self) -> units::degree_t { + return self->rz; + }, + [](Twist3d * self, units::degree_t rz) { + self->rz = rz; + } + ) + .def("__repr__", [](const Twist3d &tw) -> std::string { + return "Twist3d(dx=" + std::to_string(tw.dx()) + ", " + "dy=" + std::to_string(tw.dy()) + ", " + "dz=" + std::to_string(tw.dz()) + ", " + "rx=" + std::to_string(tw.rx()) + ", " + "ry=" + std::to_string(tw.ry()) + ", " + "rz=" + std::to_string(tw.rz()) + ")"; + }) + ; + + SetupWPyStruct(cls_Twist3d); diff --git a/wpimath/src/main/python/semiwrap/interpolation/TimeInterpolatableBuffer.yml b/wpimath/src/main/python/semiwrap/interpolation/TimeInterpolatableBuffer.yml new file mode 100644 index 0000000000..2269cb0185 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/interpolation/TimeInterpolatableBuffer.yml @@ -0,0 +1,50 @@ +extra_includes: +- "frc/geometry/Pose3d.h" + +classes: + frc::TimeInterpolatableBuffer: + template_params: + - T + methods: + TimeInterpolatableBuffer: + overloads: + units::second_t, std::function: + units::second_t: + AddSample: + Clear: + Sample: + GetInternalBuffer: + overloads: + "": + '[const]': + ignore: true + +templates: + TimeInterpolatablePose2dBuffer: + qualname: frc::TimeInterpolatableBuffer + params: + - frc::Pose2d + TimeInterpolatablePose3dBuffer: + qualname: frc::TimeInterpolatableBuffer + params: + - frc::Pose3d + TimeInterpolatableRotation2dBuffer: + qualname: frc::TimeInterpolatableBuffer + params: + - frc::Rotation2d + TimeInterpolatableRotation3dBuffer: + qualname: frc::TimeInterpolatableBuffer + params: + - frc::Rotation3d + TimeInterpolatableTranslation2dBuffer: + qualname: frc::TimeInterpolatableBuffer + params: + - frc::Translation2d + TimeInterpolatableTranslation3dBuffer: + qualname: frc::TimeInterpolatableBuffer + params: + - frc::Translation3d + TimeInterpolatableFloatBuffer: + qualname: frc::TimeInterpolatableBuffer + params: + - double diff --git a/wpimath/src/main/python/semiwrap/kinematics/ChassisSpeeds.yml b/wpimath/src/main/python/semiwrap/kinematics/ChassisSpeeds.yml new file mode 100644 index 0000000000..d673289965 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/kinematics/ChassisSpeeds.yml @@ -0,0 +1,84 @@ +extra_includes: +- wpystruct.h + +classes: + frc::ChassisSpeeds: + force_no_default_constructor: true + attributes: + vx: + vy: + omega: + methods: + ToTwist2d: + Discretize: + overloads: + units::meters_per_second_t, units::meters_per_second_t, units::radians_per_second_t, units::second_t: + const ChassisSpeeds&, units::second_t: + operator+: + operator-: + overloads: + const ChassisSpeeds& [const]: + '[const]': + operator*: + operator/: + operator==: + ToRobotRelative: + ToFieldRelative: + +inline_code: | + cls_ChassisSpeeds + .def( + py::init< + units::meters_per_second_t, units::meters_per_second_t, + units::radians_per_second_t + >(), + py::arg("vx") = 0, py::arg("vy") = 0, py::arg("omega") = 0 + ) + .def_static("fromFeet", [](units::feet_per_second_t vx, units::feet_per_second_t vy, units::radians_per_second_t omega){ + return ChassisSpeeds{vx, vy, omega}; + }, py::arg("vx") = 0, py::arg("vy") = 0, py::arg("omega") = 0) + .def_property("vx_fps", + [](ChassisSpeeds * self) -> units::feet_per_second_t { + return self->vx; + }, + [](ChassisSpeeds * self, units::feet_per_second_t vx) { + self->vx = vx; + } + ) + .def_property("vy_fps", + [](ChassisSpeeds * self) -> units::feet_per_second_t { + return self->vy; + }, + [](ChassisSpeeds * self, units::feet_per_second_t vy) { + self->vy = vy; + } + ) + .def_property("omega_dps", + [](ChassisSpeeds * self) -> units::degrees_per_second_t { + return self->omega; + }, + [](ChassisSpeeds * self, units::degrees_per_second_t omega) { + self->omega = omega; + } + ) + .def("__len__", [](const ChassisSpeeds &self) { return 3; }) + .def("__getitem__", [](const ChassisSpeeds &self, int index) { + switch (index) { + case 0: + return self.vx(); + case 1: + return self.vy(); + case 2: + return self.omega(); + default: + throw std::out_of_range("ChassisSpeeds index out of range"); + } + }) + .def("__repr__", [](const ChassisSpeeds &cs) -> std::string { + return "ChassisSpeeds(vx=" + std::to_string(cs.vx()) + ", " + "vy=" + std::to_string(cs.vy()) + ", " + "omega=" + std::to_string(cs.omega()) + ")"; + }) + ; + + SetupWPyStruct(cls_ChassisSpeeds); diff --git a/wpimath/src/main/python/semiwrap/kinematics/DifferentialDriveKinematics.yml b/wpimath/src/main/python/semiwrap/kinematics/DifferentialDriveKinematics.yml new file mode 100644 index 0000000000..24b8c460d2 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/kinematics/DifferentialDriveKinematics.yml @@ -0,0 +1,19 @@ +extra_includes: +- wpystruct.h + +classes: + frc::DifferentialDriveKinematics: + attributes: + trackwidth: + methods: + DifferentialDriveKinematics: + ToChassisSpeeds: + ToWheelSpeeds: + ToTwist2d: + overloads: + const units::meter_t, const units::meter_t [const]: + const DifferentialDriveWheelPositions&, const DifferentialDriveWheelPositions& [const]: + Interpolate: + +inline_code: | + SetupWPyStruct(cls_DifferentialDriveKinematics); diff --git a/wpimath/src/main/python/semiwrap/kinematics/DifferentialDriveOdometry.yml b/wpimath/src/main/python/semiwrap/kinematics/DifferentialDriveOdometry.yml new file mode 100644 index 0000000000..687e53a0aa --- /dev/null +++ b/wpimath/src/main/python/semiwrap/kinematics/DifferentialDriveOdometry.yml @@ -0,0 +1,7 @@ +classes: + frc::DifferentialDriveOdometry: + force_no_trampoline: true + methods: + DifferentialDriveOdometry: + ResetPosition: + Update: diff --git a/wpimath/src/main/python/semiwrap/kinematics/DifferentialDriveOdometry3d.yml b/wpimath/src/main/python/semiwrap/kinematics/DifferentialDriveOdometry3d.yml new file mode 100644 index 0000000000..9048eb7f16 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/kinematics/DifferentialDriveOdometry3d.yml @@ -0,0 +1,7 @@ +classes: + frc::DifferentialDriveOdometry3d: + force_no_trampoline: true + methods: + DifferentialDriveOdometry3d: + ResetPosition: + Update: diff --git a/wpimath/src/main/python/semiwrap/kinematics/DifferentialDriveWheelPositions.yml b/wpimath/src/main/python/semiwrap/kinematics/DifferentialDriveWheelPositions.yml new file mode 100644 index 0000000000..a50e54f79b --- /dev/null +++ b/wpimath/src/main/python/semiwrap/kinematics/DifferentialDriveWheelPositions.yml @@ -0,0 +1,8 @@ +classes: + frc::DifferentialDriveWheelPositions: + attributes: + left: + right: + methods: + operator==: + Interpolate: diff --git a/wpimath/src/main/python/semiwrap/kinematics/DifferentialDriveWheelSpeeds.yml b/wpimath/src/main/python/semiwrap/kinematics/DifferentialDriveWheelSpeeds.yml new file mode 100644 index 0000000000..7b4040c477 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/kinematics/DifferentialDriveWheelSpeeds.yml @@ -0,0 +1,53 @@ +extra_includes: +- wpystruct.h + +classes: + frc::DifferentialDriveWheelSpeeds: + force_no_default_constructor: true + attributes: + left: + right: + methods: + Desaturate: + operator+: + operator-: + overloads: + const DifferentialDriveWheelSpeeds& [const]: + '[const]': + operator*: + operator/: + + +inline_code: | + cls_DifferentialDriveWheelSpeeds + .def( + py::init(), + py::arg("left") = 0, py::arg("right") = 0 + ) + .def_static("fromFeet", [](units::feet_per_second_t left, units::feet_per_second_t right){ + return DifferentialDriveWheelSpeeds{left, right}; + }, py::arg("left"), py::arg("right")) + .def_property("left_fps", + [](DifferentialDriveWheelSpeeds * self) -> units::feet_per_second_t { + return self->left; + }, + [](DifferentialDriveWheelSpeeds * self, units::feet_per_second_t left) { + self->left = left; + } + ) + .def_property("right_fps", + [](DifferentialDriveWheelSpeeds * self) -> units::feet_per_second_t { + return self->right; + }, + [](DifferentialDriveWheelSpeeds * self, units::feet_per_second_t right) { + self->right = right; + } + ) + .def("__repr__", [](const DifferentialDriveWheelSpeeds &dds) -> std::string { + return "DifferentialDriveWheelSpeeds(left=" + std::to_string(dds.left()) + ", " + "right=" + std::to_string(dds.right()) + ")"; + }) + ; + + SetupWPyStruct(cls_DifferentialDriveWheelSpeeds); + diff --git a/wpimath/src/main/python/semiwrap/kinematics/Kinematics.yml b/wpimath/src/main/python/semiwrap/kinematics/Kinematics.yml new file mode 100644 index 0000000000..286c65756e --- /dev/null +++ b/wpimath/src/main/python/semiwrap/kinematics/Kinematics.yml @@ -0,0 +1,53 @@ +extra_includes: +- frc/kinematics/DifferentialDriveWheelPositions.h +- frc/kinematics/DifferentialDriveWheelSpeeds.h +- frc/kinematics/MecanumDriveWheelPositions.h +- frc/kinematics/MecanumDriveWheelSpeeds.h +- frc/kinematics/SwerveDriveKinematics.h + + +classes: + frc::Kinematics: + force_type_casters: + - wpi::array + template_params: + - WheelSpeeds + - WheelPositions + methods: + ToChassisSpeeds: + ToWheelSpeeds: + ToTwist2d: + Interpolate: + + +templates: + DifferentialDriveKinematicsBase: + qualname: frc::Kinematics + params: + - frc::DifferentialDriveWheelSpeeds + - frc::DifferentialDriveWheelPositions + MecanumDriveKinematicsBase: + qualname: frc::Kinematics + params: + - frc::MecanumDriveWheelSpeeds + - frc::MecanumDriveWheelPositions + SwerveDrive2KinematicsBase: + qualname: frc::Kinematics + params: + - wpi::array + - wpi::array + SwerveDrive3KinematicsBase: + qualname: frc::Kinematics + params: + - wpi::array + - wpi::array + SwerveDrive4KinematicsBase: + qualname: frc::Kinematics + params: + - wpi::array + - wpi::array + SwerveDrive6KinematicsBase: + qualname: frc::Kinematics + params: + - wpi::array + - wpi::array diff --git a/wpimath/src/main/python/semiwrap/kinematics/MecanumDriveKinematics.yml b/wpimath/src/main/python/semiwrap/kinematics/MecanumDriveKinematics.yml new file mode 100644 index 0000000000..17fcfe2532 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/kinematics/MecanumDriveKinematics.yml @@ -0,0 +1,50 @@ +extra_includes: +- wpystruct.h + +classes: + frc::MecanumDriveKinematics: + methods: + MecanumDriveKinematics: + ToWheelSpeeds: + doc: | + Performs inverse kinematics to return the wheel speeds from a desired + chassis velocity. This method is often used to convert joystick values into + wheel speeds. + + This function also supports variable centers of rotation. During normal + operations, the center of rotation is usually the same as the physical + center of the robot; therefore, the argument is defaulted to that use case. + However, if you wish to change the center of rotation for evasive + maneuvers, vision alignment, or for any other use case, you can do so. + + :param chassisSpeeds: The desired chassis speed. + :param centerOfRotation: The center of rotation. For example, if you set the + center of rotation at one corner of the robot and + provide a chassis speed that only has a dtheta + component, the robot will rotate around that + corner. + + :returns: The wheel speeds. Use caution because they are not normalized. + Sometimes, a user input may cause one of the wheel speeds to go + above the attainable max velocity. Use the + :meth:`MecanumDriveWheelSpeeds.normalize` method to rectify + this issue. In addition, you can use Python unpacking syntax + to directly assign the wheel speeds to variables:: + + fl, fr, bl, br = kinematics.toWheelSpeeds(chassisSpeeds) + overloads: + const ChassisSpeeds&, const Translation2d& [const]: + const ChassisSpeeds& [const]: + ToChassisSpeeds: + ToTwist2d: + overloads: + const MecanumDriveWheelPositions&, const MecanumDriveWheelPositions& [const]: + const MecanumDriveWheelPositions& [const]: + GetFrontLeft: + GetFrontRight: + GetRearLeft: + GetRearRight: + Interpolate: + +inline_code: | + SetupWPyStruct(cls_MecanumDriveKinematics); diff --git a/wpimath/src/main/python/semiwrap/kinematics/MecanumDriveOdometry.yml b/wpimath/src/main/python/semiwrap/kinematics/MecanumDriveOdometry.yml new file mode 100644 index 0000000000..929e3e2570 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/kinematics/MecanumDriveOdometry.yml @@ -0,0 +1,5 @@ +classes: + frc::MecanumDriveOdometry: + force_no_trampoline: true + methods: + MecanumDriveOdometry: diff --git a/wpimath/src/main/python/semiwrap/kinematics/MecanumDriveOdometry3d.yml b/wpimath/src/main/python/semiwrap/kinematics/MecanumDriveOdometry3d.yml new file mode 100644 index 0000000000..a53f0d75eb --- /dev/null +++ b/wpimath/src/main/python/semiwrap/kinematics/MecanumDriveOdometry3d.yml @@ -0,0 +1,5 @@ +classes: + frc::MecanumDriveOdometry3d: + force_no_trampoline: true + methods: + MecanumDriveOdometry3d: diff --git a/wpimath/src/main/python/semiwrap/kinematics/MecanumDriveWheelPositions.yml b/wpimath/src/main/python/semiwrap/kinematics/MecanumDriveWheelPositions.yml new file mode 100644 index 0000000000..82be5f55ef --- /dev/null +++ b/wpimath/src/main/python/semiwrap/kinematics/MecanumDriveWheelPositions.yml @@ -0,0 +1,10 @@ +classes: + frc::MecanumDriveWheelPositions: + attributes: + frontLeft: + frontRight: + rearLeft: + rearRight: + methods: + operator==: + Interpolate: diff --git a/wpimath/src/main/python/semiwrap/kinematics/MecanumDriveWheelSpeeds.yml b/wpimath/src/main/python/semiwrap/kinematics/MecanumDriveWheelSpeeds.yml new file mode 100644 index 0000000000..fff03df0d8 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/kinematics/MecanumDriveWheelSpeeds.yml @@ -0,0 +1,81 @@ +extra_includes: +- wpystruct.h + +classes: + frc::MecanumDriveWheelSpeeds: + force_no_default_constructor: true + attributes: + frontLeft: + frontRight: + rearLeft: + rearRight: + methods: + Desaturate: + operator+: + operator-: + overloads: + const MecanumDriveWheelSpeeds& [const]: + '[const]': + operator*: + operator/: + +inline_code: | + cls_MecanumDriveWheelSpeeds + .def( + py::init< + units::meters_per_second_t, units::meters_per_second_t, + units::meters_per_second_t, units::meters_per_second_t + >(), + py::arg("frontLeft") = 0, py::arg("frontRight") = 0, + py::arg("rearLeft") = 0, py::arg("rearRight") = 0 + ) + .def_static("fromFeet", []( + units::feet_per_second_t frontLeft, + units::feet_per_second_t frontRight, + units::feet_per_second_t rearLeft, + units::feet_per_second_t rearRight + ){ + return MecanumDriveWheelSpeeds{frontLeft, frontRight, rearLeft, rearRight}; + }, py::arg("frontLeft"), py::arg("frontRight"), + py::arg("rearLeft"), py::arg("rearRight")) + .def_property("frontLeft_fps", + [](MecanumDriveWheelSpeeds * self) -> units::feet_per_second_t { + return self->frontLeft; + }, + [](MecanumDriveWheelSpeeds * self, units::feet_per_second_t fps) { + self->frontLeft = fps; + } + ) + .def_property("frontRight_fps", + [](MecanumDriveWheelSpeeds * self) -> units::feet_per_second_t { + return self->frontRight; + }, + [](MecanumDriveWheelSpeeds * self, units::feet_per_second_t fps) { + self->frontRight = fps; + } + ) + .def_property("rearLeft_fps", + [](MecanumDriveWheelSpeeds * self) -> units::feet_per_second_t { + return self->rearLeft; + }, + [](MecanumDriveWheelSpeeds * self, units::feet_per_second_t fps) { + self->rearLeft = fps; + } + ) + .def_property("rearRight_fps", + [](MecanumDriveWheelSpeeds * self) -> units::feet_per_second_t { + return self->rearRight; + }, + [](MecanumDriveWheelSpeeds * self, units::feet_per_second_t fps) { + self->rearRight = fps; + } + ) + .def("__repr__", [](const MecanumDriveWheelSpeeds &ms) -> std::string { + return "MecanumDriveWheelSpeeds(frontLeft=" + std::to_string(ms.frontLeft()) + ", " + "frontRight=" + std::to_string(ms.frontRight()) + ", " + "rearLeft=" + std::to_string(ms.rearLeft()) + ", " + "rearRight=" + std::to_string(ms.rearRight()) + ")"; + }) + ; + + SetupWPyStruct(cls_MecanumDriveWheelSpeeds); diff --git a/wpimath/src/main/python/semiwrap/kinematics/Odometry.yml b/wpimath/src/main/python/semiwrap/kinematics/Odometry.yml new file mode 100644 index 0000000000..d11548e0c6 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/kinematics/Odometry.yml @@ -0,0 +1,52 @@ +extra_includes: +- frc/kinematics/DifferentialDriveWheelPositions.h +- frc/kinematics/DifferentialDriveWheelSpeeds.h +- frc/kinematics/MecanumDriveWheelPositions.h +- frc/kinematics/MecanumDriveWheelSpeeds.h +- frc/kinematics/SwerveDriveKinematics.h + +classes: + frc::Odometry: + template_params: + - WheelSpeeds + - WheelPositions + methods: + Odometry: + ResetPosition: + ResetPose: + ResetTranslation: + ResetRotation: + GetPose: + Update: + +templates: + DifferentialDriveOdometryBase: + qualname: frc::Odometry + params: + - frc::DifferentialDriveWheelSpeeds + - frc::DifferentialDriveWheelPositions + MecanumDriveOdometryBase: + qualname: frc::Odometry + params: + - frc::MecanumDriveWheelSpeeds + - frc::MecanumDriveWheelPositions + SwerveDrive2OdometryBase: + qualname: frc::Odometry + params: + - wpi::array + - wpi::array + SwerveDrive3OdometryBase: + qualname: frc::Odometry + params: + - wpi::array + - wpi::array + SwerveDrive4OdometryBase: + qualname: frc::Odometry + params: + - wpi::array + - wpi::array + SwerveDrive6OdometryBase: + qualname: frc::Odometry + params: + - wpi::array + - wpi::array diff --git a/wpimath/src/main/python/semiwrap/kinematics/Odometry3d.yml b/wpimath/src/main/python/semiwrap/kinematics/Odometry3d.yml new file mode 100644 index 0000000000..8c5d9eefa8 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/kinematics/Odometry3d.yml @@ -0,0 +1,53 @@ +extra_includes: +- frc/kinematics/DifferentialDriveWheelPositions.h +- frc/kinematics/DifferentialDriveWheelSpeeds.h +- frc/kinematics/MecanumDriveWheelPositions.h +- frc/kinematics/MecanumDriveWheelSpeeds.h +- frc/kinematics/SwerveDriveKinematics.h + +classes: + frc::Odometry3d: + template_params: + - WheelSpeeds + - WheelPositions + methods: + Odometry3d: + ResetPosition: + ResetPose: + ResetTranslation: + ResetRotation: + GetPose: + Update: + + +templates: + DifferentialDriveOdometry3dBase: + qualname: frc::Odometry3d + params: + - frc::DifferentialDriveWheelSpeeds + - frc::DifferentialDriveWheelPositions + MecanumDriveOdometry3dBase: + qualname: frc::Odometry3d + params: + - frc::MecanumDriveWheelSpeeds + - frc::MecanumDriveWheelPositions + SwerveDrive2Odometry3dBase: + qualname: frc::Odometry3d + params: + - wpi::array + - wpi::array + SwerveDrive3Odometry3dBase: + qualname: frc::Odometry3d + params: + - wpi::array + - wpi::array + SwerveDrive4Odometry3dBase: + qualname: frc::Odometry3d + params: + - wpi::array + - wpi::array + SwerveDrive6Odometry3dBase: + qualname: frc::Odometry3d + params: + - wpi::array + - wpi::array diff --git a/wpimath/src/main/python/semiwrap/kinematics/SwerveDriveKinematics.yml b/wpimath/src/main/python/semiwrap/kinematics/SwerveDriveKinematics.yml new file mode 100644 index 0000000000..aa14d190f0 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/kinematics/SwerveDriveKinematics.yml @@ -0,0 +1,108 @@ +classes: + frc::SwerveDriveKinematics: + force_type_casters: + - wpi::array + template_params: + - size_t NumModules + methods: + SwerveDriveKinematics: + overloads: + ModuleTranslations&&...: + ignore: true + const wpi::array&: + ignore: true + + ResetHeadings: + overloads: + ModuleHeadings&&...: + ignore: true + wpi::array: + + ToSwerveModuleStates: + doc: | + Performs inverse kinematics to return the module states from a desired + chassis velocity. This method is often used to convert joystick values into + module speeds and angles. + + This function also supports variable centers of rotation. During normal + operations, the center of rotation is usually the same as the physical + center of the robot; therefore, the argument is defaulted to that use case. + However, if you wish to change the center of rotation for evasive + maneuvers, vision alignment, or for any other use case, you can do so. + + :param chassisSpeeds: The desired chassis speed. + :param centerOfRotation: The center of rotation. For example, if you set the + center of rotation at one corner of the robot and provide a chassis speed + that only has a dtheta component, the robot will rotate around that corner. + + :returns: An array containing the module states. Use caution because these + module states are not normalized. Sometimes, a user input may cause one of + the module speeds to go above the attainable max velocity. Use the + :meth:`desaturateWheelSpeeds` function to rectify this issue. + In addition, you can use Python unpacking syntax + to directly assign the module states to variables:: + + fl, fr, bl, br = kinematics.toSwerveModuleStates(chassisSpeeds) + ToWheelSpeeds: + ToChassisSpeeds: + overloads: + ModuleStates&&... [const]: + ignore: true + const wpi::array& [const]: + ToTwist2d: + overloads: + ModuleDeltas&&... [const]: + ignore: true + wpi::array [const]: + const wpi::array&, const wpi::array& [const]: + DesaturateWheelSpeeds: + overloads: + wpi::array*, units::meters_per_second_t: + cpp_code: | + [](wpi::array moduleStates, units::meters_per_second_t attainableMaxSpeed) { + frc::SwerveDriveKinematics::DesaturateWheelSpeeds(&moduleStates, attainableMaxSpeed); + return moduleStates; + } + ? wpi::array*, ChassisSpeeds, units::meters_per_second_t, units::meters_per_second_t, units::radians_per_second_t + : cpp_code: | + [](wpi::array moduleStates, + ChassisSpeeds currentChassisSpeed, + units::meters_per_second_t attainableMaxModuleSpeed, + units::meters_per_second_t attainableMaxRobotTranslationSpeed, + units::radians_per_second_t attainableMaxRobotRotationSpeed) { + frc::SwerveDriveKinematics::DesaturateWheelSpeeds(&moduleStates, currentChassisSpeed, attainableMaxModuleSpeed, attainableMaxRobotTranslationSpeed, attainableMaxRobotRotationSpeed); + return moduleStates; + } + + Interpolate: + GetModules: + + template_inline_code: | + if constexpr (NumModules == 2) { + cls_SwerveDriveKinematics.def(py::init()); + } else if constexpr (NumModules == 3) { + cls_SwerveDriveKinematics.def(py::init()); + } else if constexpr (NumModules == 4) { + cls_SwerveDriveKinematics.def(py::init()); + } else if constexpr (NumModules == 6) { + cls_SwerveDriveKinematics.def(py::init()); + } + + +templates: + SwerveDrive2Kinematics: + qualname: frc::SwerveDriveKinematics + params: + - 2 + SwerveDrive3Kinematics: + qualname: frc::SwerveDriveKinematics + params: + - 3 + SwerveDrive4Kinematics: + qualname: frc::SwerveDriveKinematics + params: + - 4 + SwerveDrive6Kinematics: + qualname: frc::SwerveDriveKinematics + params: + - 6 diff --git a/wpimath/src/main/python/semiwrap/kinematics/SwerveDriveOdometry.yml b/wpimath/src/main/python/semiwrap/kinematics/SwerveDriveOdometry.yml new file mode 100644 index 0000000000..072f0cfac8 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/kinematics/SwerveDriveOdometry.yml @@ -0,0 +1,24 @@ +classes: + frc::SwerveDriveOdometry: + force_no_trampoline: true + template_params: + - size_t NumModules + methods: + SwerveDriveOdometry: +templates: + SwerveDrive2Odometry: + qualname: frc::SwerveDriveOdometry + params: + - 2 + SwerveDrive3Odometry: + qualname: frc::SwerveDriveOdometry + params: + - 3 + SwerveDrive4Odometry: + qualname: frc::SwerveDriveOdometry + params: + - 4 + SwerveDrive6Odometry: + qualname: frc::SwerveDriveOdometry + params: + - 6 diff --git a/wpimath/src/main/python/semiwrap/kinematics/SwerveDriveOdometry3d.yml b/wpimath/src/main/python/semiwrap/kinematics/SwerveDriveOdometry3d.yml new file mode 100644 index 0000000000..63752d4685 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/kinematics/SwerveDriveOdometry3d.yml @@ -0,0 +1,25 @@ +classes: + frc::SwerveDriveOdometry3d: + force_no_trampoline: true + template_params: + - size_t NumModules + methods: + SwerveDriveOdometry3d: + +templates: + SwerveDrive2Odometry3d: + qualname: frc::SwerveDriveOdometry3d + params: + - 2 + SwerveDrive3Odometry3d: + qualname: frc::SwerveDriveOdometry3d + params: + - 3 + SwerveDrive4Odometry3d: + qualname: frc::SwerveDriveOdometry3d + params: + - 4 + SwerveDrive6Odometry3d: + qualname: frc::SwerveDriveOdometry3d + params: + - 6 diff --git a/wpimath/src/main/python/semiwrap/kinematics/SwerveModulePosition.yml b/wpimath/src/main/python/semiwrap/kinematics/SwerveModulePosition.yml new file mode 100644 index 0000000000..aaa7130feb --- /dev/null +++ b/wpimath/src/main/python/semiwrap/kinematics/SwerveModulePosition.yml @@ -0,0 +1,34 @@ +extra_includes: +- wpystruct.h + +classes: + frc::SwerveModulePosition: + force_no_default_constructor: true + attributes: + distance: + angle: + methods: + Interpolate: + operator==: + inline_code: | + .def( + py::init< + units::meter_t, frc::Rotation2d + >(), + py::arg("distance") = 0, py::arg("angle") = frc::Rotation2d() + ) + .def_property("distance_ft", + [](SwerveModulePosition * self) -> units::foot_t { + return self->distance; + }, + [](SwerveModulePosition * self, units::foot_t distance) { + self->distance = distance; + } + ) + .def("__repr__", [](const SwerveModulePosition &ss) -> std::string { + return "SwerveModulePosition(distance=" + std::to_string(ss.distance()) + ", " + "angle=" + std::to_string(ss.angle.Radians()()) + ")"; + }) + +inline_code: | + SetupWPyStruct(cls_SwerveModulePosition); diff --git a/wpimath/src/main/python/semiwrap/kinematics/SwerveModuleState.yml b/wpimath/src/main/python/semiwrap/kinematics/SwerveModuleState.yml new file mode 100644 index 0000000000..9ba68ee960 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/kinematics/SwerveModuleState.yml @@ -0,0 +1,41 @@ +extra_includes: +- wpystruct.h + +classes: + frc::SwerveModuleState: + force_no_default_constructor: true + attributes: + speed: + angle: + methods: + operator==: + Optimize: + overloads: + const Rotation2d&: + const SwerveModuleState&, const Rotation2d&: + ignore: true + CosineScale: + +inline_code: | + cls_SwerveModuleState + .def( + py::init< + units::meters_per_second_t, frc::Rotation2d + >(), + py::arg("speed") = 0, py::arg("angle") = frc::Rotation2d() + ) + .def_property("speed_fps", + [](SwerveModuleState * self) -> units::feet_per_second_t { + return self->speed; + }, + [](SwerveModuleState * self, units::feet_per_second_t speed) { + self->speed = speed; + } + ) + .def("__repr__", [](const SwerveModuleState &ss) -> std::string { + return "SwerveModuleState(speed=" + std::to_string(ss.speed()) + ", " + "angle=" + std::to_string(ss.angle.Radians()()) + ")"; + }) + ; + + SetupWPyStruct(cls_SwerveModuleState); diff --git a/wpimath/src/main/python/semiwrap/spline/CubicHermiteSpline.yml b/wpimath/src/main/python/semiwrap/spline/CubicHermiteSpline.yml new file mode 100644 index 0000000000..8947e658a1 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/spline/CubicHermiteSpline.yml @@ -0,0 +1,10 @@ +classes: + frc::CubicHermiteSpline: + force_no_trampoline: true + methods: + CubicHermiteSpline: + Coefficients: + # otherwise we have to depend on numpy + ignore: true + GetInitialControlVector: + GetFinalControlVector: diff --git a/wpimath/src/main/python/semiwrap/spline/QuinticHermiteSpline.yml b/wpimath/src/main/python/semiwrap/spline/QuinticHermiteSpline.yml new file mode 100644 index 0000000000..054929423f --- /dev/null +++ b/wpimath/src/main/python/semiwrap/spline/QuinticHermiteSpline.yml @@ -0,0 +1,9 @@ +classes: + frc::QuinticHermiteSpline: + force_no_trampoline: true + methods: + QuinticHermiteSpline: + Coefficients: + ignore: true + GetInitialControlVector: + GetFinalControlVector: diff --git a/wpimath/src/main/python/semiwrap/spline/Spline.yml b/wpimath/src/main/python/semiwrap/spline/Spline.yml new file mode 100644 index 0000000000..8804b137a7 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/spline/Spline.yml @@ -0,0 +1,49 @@ +extra_includes: +- pybind11/stl.h + +classes: + frc::Spline: + is_polymorphic: true + force_type_casters: + - units::curvature_t + - wpi::array + template_params: + - int Degree + methods: + Spline: + GetPoint: + Coefficients: + ignore_pure: true + GetInitialControlVector: + ignore_pure: true + GetFinalControlVector: + ignore_pure: true + ToVector: + ignore: true + FromVector: + ignore: true + template_inline_code: | + cls_ControlVector + .def( + py::init< + wpi::array, + wpi::array>(), + py::arg("x"), + py::arg("y") + ); + frc::Spline::ControlVector: + force_no_default_constructor: true + attributes: + x: + y: + +templates: + Spline3: + qualname: frc::Spline + params: + - 3 + + Spline5: + qualname: frc::Spline + params: + - 5 diff --git a/wpimath/src/main/python/semiwrap/spline/SplineHelper.yml b/wpimath/src/main/python/semiwrap/spline/SplineHelper.yml new file mode 100644 index 0000000000..8ec6394658 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/spline/SplineHelper.yml @@ -0,0 +1,8 @@ +classes: + frc::SplineHelper: + methods: + CubicControlVectorsFromWaypoints: + QuinticSplinesFromWaypoints: + CubicSplinesFromControlVectors: + QuinticSplinesFromControlVectors: + OptimizeCurvature: diff --git a/wpimath/src/main/python/semiwrap/spline/SplineParameterizer.yml b/wpimath/src/main/python/semiwrap/spline/SplineParameterizer.yml new file mode 100644 index 0000000000..a4971fa5b3 --- /dev/null +++ b/wpimath/src/main/python/semiwrap/spline/SplineParameterizer.yml @@ -0,0 +1,13 @@ +classes: + frc::SplineParameterizer: + force_no_default_constructor: true + force_type_casters: + - units::curvature_t + methods: + Parameterize: + template_impls: + - ['3'] + - ['5'] + + frc::SplineParameterizer::MalformedSplineException: + ignore: true diff --git a/wpimath/src/main/python/tools/create_units.py b/wpimath/src/main/python/tools/create_units.py new file mode 100755 index 0000000000..1f902719db --- /dev/null +++ b/wpimath/src/main/python/tools/create_units.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 + +import io +import inspect +import pathlib +import re +import subprocess +import sys +import toml + +out = {} + +for f in sorted(pathlib.Path(sys.argv[1]).glob("*.h")): + if f.name == "base.h": + continue + + names = [] + + with open(f) as fp: + last_line = None + for line in fp.readlines(): + line = line.strip() + if last_line is not None: + line = last_line + line + last_line = None + elif line.endswith("(") or line.endswith(","): + last_line = line + continue + + m = re.match(r"UNIT_ADD\(\w+, (\w+), (\w+),", line) + if m: + names.append((m.group(1), m.group(2))) + else: + m = re.match(r"UNIT_ADD_WITH_METRIC_PREFIXES\(\w+, (\w+), (\w+),", line) + if m: + names.append((m.group(1), m.group(2))) + for i in ("nano", "micro", "milli", "kilo"): + names.append((f"{i}{m.group(1)}", f"{i}{m.group(2)}")) + + out_name = f"units_{f.name[:-2]}_type_caster.h" + + if names: + if True: + ofp = io.StringIO() + + ofp.write("#pragma once\n") + ofp.write("\n") + ofp.write(f"#include \n") + ofp.write("\n") + + ofp.write("\nnamespace pybind11 { namespace detail {\n") + + for single, double in names: + ofp.write( + inspect.cleandoc( + f""" + + template <> struct handle_type_name {{ + static constexpr auto name = _("{double}"); + }}; + + template <> struct handle_type_name {{ + static constexpr auto name = _("{double}"); + }}; + + """ + ) + ) + ofp.write("\n\n") + + ofp.write("\n}\n}\n\n") + + ofp.write(f'#include "_units_base_type_caster.h"\n') + ofp.write("\n") + + ofp.seek(0) + + content = subprocess.check_output( + ["clang-format"], input=ofp.getvalue(), encoding="utf-8" + ) + + with open(out_name, "w+") as fp: + fp.write(content) + else: + out[out_name] = sorted( + [f"units::{single}_t" for single, _ in names] + + [f"units::{single}" for single, _ in names] + + [f"units::{double}" for _, double in names] + ) + + +print(toml.dumps(dict(sup=out))) diff --git a/wpimath/src/main/python/wpimath/__init__.py b/wpimath/src/main/python/wpimath/__init__.py new file mode 100644 index 0000000000..f85ba7db0b --- /dev/null +++ b/wpimath/src/main/python/wpimath/__init__.py @@ -0,0 +1,25 @@ +from . import _init__wpimath # noqa: F401 + +# Needed for stubgen +from . import geometry + +# autogenerated by 'semiwrap create-imports wpimath wpimath._wpimath' +from ._wpimath import ( + angleModulus, + applyDeadband, + floorDiv, + floorMod, + inputModulus, + objectToRobotPose, + slewRateLimit, +) + +__all__ = [ + "angleModulus", + "applyDeadband", + "floorDiv", + "floorMod", + "inputModulus", + "objectToRobotPose", + "slewRateLimit", +] diff --git a/wpimath/src/main/python/wpimath/_controls/__init__.py b/wpimath/src/main/python/wpimath/_controls/__init__.py new file mode 100644 index 0000000000..d3c258e60c --- /dev/null +++ b/wpimath/src/main/python/wpimath/_controls/__init__.py @@ -0,0 +1 @@ +from . import _init__controls diff --git a/wpimath/src/main/python/wpimath/_controls/controls.cpp b/wpimath/src/main/python/wpimath/_controls/controls.cpp new file mode 100644 index 0000000000..1c5864fe0e --- /dev/null +++ b/wpimath/src/main/python/wpimath/_controls/controls.cpp @@ -0,0 +1,7 @@ + +#include "semiwrap_init.wpimath._controls._controls.hpp" + +SEMIWRAP_PYBIND11_MODULE(m) +{ + initWrapper(m); +} diff --git a/wpimath/src/main/python/wpimath/_impl/__init__.py b/wpimath/src/main/python/wpimath/_impl/__init__.py new file mode 100644 index 0000000000..612b22393b --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/__init__.py @@ -0,0 +1 @@ +from . import _init_wpimath_cpp diff --git a/wpimath/src/main/python/wpimath/_impl/src/PyTrajectoryConstraint.h b/wpimath/src/main/python/wpimath/_impl/src/PyTrajectoryConstraint.h new file mode 100644 index 0000000000..8d91ed04c6 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/PyTrajectoryConstraint.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include + +namespace frc { + +struct PyTrajectoryConstraint : public TrajectoryConstraint { + + PyTrajectoryConstraint() {} + + units::meters_per_second_t + MaxVelocity(const Pose2d &pose, units::curvature_t curvature, + units::meters_per_second_t velocity) const override { + return m_constraint->MaxVelocity(pose, curvature, velocity); + } + + MinMax MinMaxAcceleration(const Pose2d &pose, units::curvature_t curvature, + units::meters_per_second_t speed) const override { + return m_constraint->MinMaxAcceleration(pose, curvature, speed); + } + + std::shared_ptr m_constraint; +}; + +}; // namespace frc + +namespace pybind11 { +namespace detail { + +template <> struct type_caster { + using value_conv = make_caster>; + + PYBIND11_TYPE_CASTER(frc::PyTrajectoryConstraint, _("wpimath._controls._controls.constraint.TrajectoryConstraint")); + + bool load(handle src, bool convert) { + value_conv conv; + if (!conv.load(src, convert)) { + return false; + } + + value.m_constraint = + cast_op>(std::move(conv)); + return true; + } + + static handle cast(const frc::PyTrajectoryConstraint &src, + return_value_policy policy, handle parent) { + return value_conv::cast(src.m_constraint, policy, parent); + } +}; + +}; // namespace detail +}; // namespace pybind11 diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/_units_base_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/_units_base_type_caster.h new file mode 100644 index 0000000000..d539405512 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/_units_base_type_caster.h @@ -0,0 +1,55 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { + +/* + Units type caster assumptions + + When going from C++ to Python (eg, return values), the units that a user + gets as a float are in whatever the unit was for C++. + + units::foot_t getFeet(); // converted to float, value is feet + + When going from Python to C++, the units a user uses are once again + whatever the C++ function specifies: + + void setFeet(units::foot_t ft); // must pass a float, it's in feet + void setMeters(units::meter_t m); // must pass a float, it's in meters + + Unfortunately, with this type caster and robotpy-build there are mismatch + issues with implicit conversions when default values are used that don't + match the actual value: + + foo(units::second_t tm = 10_ms); // if not careful, pybind11 will + // store as 10 seconds +*/ +template class S> +struct type_caster> { + using value_type = units::unit_t; + + // TODO: there should be a way to include the type with this + PYBIND11_TYPE_CASTER(value_type, handle_type_name::name); + + // Python -> C++ + bool load(handle src, bool convert) { + if (!src) + return false; + if (!convert && !PyFloat_Check(src.ptr())) + return false; + auto cvted = PyFloat_AsDouble(src.ptr()); + value = value_type(cvted); + return !(cvted == -1 && PyErr_Occurred()); + } + + // C++ -> Python + static handle cast(const value_type &src, return_value_policy /* policy */, + handle /* parent */) { + return PyFloat_FromDouble(src.template to()); + } +}; + +} // namespace detail +} // namespace pybind11 \ No newline at end of file diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/frc_eigen.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/frc_eigen.h new file mode 100644 index 0000000000..d1c219aa1b --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/frc_eigen.h @@ -0,0 +1,4 @@ +#pragma once + +// the FRC types are just aliases for Eigen types, so use that +#include diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_acceleration_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_acceleration_type_caster.h new file mode 100644 index 0000000000..503fec4142 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_acceleration_type_caster.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.meters_per_second_squared"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.meters_per_second_squared"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.feet_per_second_squared"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.feet_per_second_squared"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.standard_gravity"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.standard_gravity"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_angle_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_angle_type_caster.h new file mode 100644 index 0000000000..11e5456d22 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_angle_type_caster.h @@ -0,0 +1,98 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.radians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.radians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanoradians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanoradians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microradians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microradians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliradians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliradians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kiloradians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kiloradians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.degrees"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.degrees"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.arcminutes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.arcminutes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.arcseconds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.arcseconds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliarcseconds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliarcseconds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.turns"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.turns"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.gradians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.gradians"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_angular_acceleration_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_angular_acceleration_type_caster.h new file mode 100644 index 0000000000..6ccff80ec1 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_angular_acceleration_type_caster.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.radians_per_second_squared"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.radians_per_second_squared"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.degrees_per_second_squared"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.degrees_per_second_squared"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.turns_per_second_squared"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.turns_per_second_squared"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_angular_velocity_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_angular_velocity_type_caster.h new file mode 100644 index 0000000000..f441ec17c6 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_angular_velocity_type_caster.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.radians_per_second"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.radians_per_second"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.degrees_per_second"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.degrees_per_second"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.turns_per_second"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.turns_per_second"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.revolutions_per_minute"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.revolutions_per_minute"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliarcseconds_per_year"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliarcseconds_per_year"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_area_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_area_type_caster.h new file mode 100644 index 0000000000..e50d135655 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_area_type_caster.h @@ -0,0 +1,66 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.square_meters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.square_meters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.square_feet"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.square_feet"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.square_inches"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.square_inches"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.square_miles"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.square_miles"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.square_kilometers"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.square_kilometers"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.hectares"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.hectares"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.acres"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.acres"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_capacitance_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_capacitance_type_caster.h new file mode 100644 index 0000000000..2dfcb1be5e --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_capacitance_type_caster.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.farads"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.farads"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanofarads"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanofarads"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microfarads"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microfarads"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millifarads"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millifarads"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilofarads"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilofarads"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_charge_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_charge_type_caster.h new file mode 100644 index 0000000000..0ddb1ed318 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_charge_type_caster.h @@ -0,0 +1,90 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.coulombs"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.coulombs"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanocoulombs"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanocoulombs"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microcoulombs"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microcoulombs"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millicoulombs"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millicoulombs"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilocoulombs"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilocoulombs"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.ampere_hours"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.ampere_hours"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanoampere_hours"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanoampere_hours"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microampere_hours"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microampere_hours"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliampere_hours"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliampere_hours"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kiloampere_hours"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kiloampere_hours"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_compound_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_compound_type_caster.h new file mode 100644 index 0000000000..bafe8b19de --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_compound_type_caster.h @@ -0,0 +1,96 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace pybind11 { +namespace detail { + +template <> +struct handle_type_name>>> { + static constexpr auto name = _("wpimath.units.radians_per_meter"); +}; + +template <> +struct handle_type_name>>> { + static constexpr auto name = _("wpimath.units.radians_per_second_per_volt"); +}; + +template <> +struct handle_type_name>> { + static constexpr auto name = _("wpimath.units.units_per_second"); +}; + +template <> +struct handle_type_name< + units::unit_t>>> { + static constexpr auto name = _("wpimath.units.units_per_second_squared"); +}; + +using volt_seconds = units::compound_unit; +using volt_seconds_squared = units::compound_unit; + +template <> struct handle_type_name> { + static constexpr auto name = _("wpimath.units.volt_seconds"); +}; + +template <> struct handle_type_name> { + static constexpr auto name = _("wpimath.units.volt_seconds_squared"); +}; + +template <> +struct handle_type_name>>> { + static constexpr auto name = _("wpimath.units.volt_seconds_per_meter"); +}; +template <> +struct handle_type_name>>> { + static constexpr auto name = _("wpimath.units.volt_seconds_squared_per_meter"); +}; +template <> +struct handle_type_name>>> { + static constexpr auto name = _("wpimath.units.volt_seconds_per_feet"); +}; +template <> +struct handle_type_name>>> { + static constexpr auto name = _("wpimath.units.volt_seconds_squared_per_feet"); +}; +template <> +struct handle_type_name>>> { + static constexpr auto name = _("wpimath.units.volt_seconds_per_radian"); +}; +template <> +struct handle_type_name>>> { + static constexpr auto name = _("wpimath.units.volt_seconds_squared_per_radian"); +}; + +using unit_seconds = units::compound_unit; +using unit_seconds_squared = units::compound_unit; + +template <> +struct handle_type_name>>> { + static constexpr auto name = _("wpimath.units.unit_seconds_squared_per_unit"); +}; + +template <> +struct handle_type_name>>> { + static constexpr auto name = _("wpimath.units.meters_per_second_squared_per_volt"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_concentration_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_concentration_type_caster.h new file mode 100644 index 0000000000..fb2eb41709 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_concentration_type_caster.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.parts_per_million"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.parts_per_million"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.parts_per_billion"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.parts_per_billion"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.parts_per_trillion"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.parts_per_trillion"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.percent"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.percent"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_conductance_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_conductance_type_caster.h new file mode 100644 index 0000000000..331d44d601 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_conductance_type_caster.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.siemens"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.siemens"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanosiemens"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanosiemens"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microsiemens"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microsiemens"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millisiemens"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millisiemens"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilosiemens"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilosiemens"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_current_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_current_type_caster.h new file mode 100644 index 0000000000..a617364c4c --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_current_type_caster.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.amperes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.amperes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanoamperes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanoamperes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microamperes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microamperes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliamperes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliamperes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kiloamperes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kiloamperes"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_data_transfer_rate_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_data_transfer_rate_type_caster.h new file mode 100644 index 0000000000..86211701a8 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_data_transfer_rate_type_caster.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.exabytes_per_second"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.exabytes_per_second"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.exabits_per_second"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.exabits_per_second"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_data_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_data_type_caster.h new file mode 100644 index 0000000000..0d6c085e57 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_data_type_caster.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.exabytes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.exabytes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.exabits"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.exabits"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_density_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_density_type_caster.h new file mode 100644 index 0000000000..387d916ca1 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_density_type_caster.h @@ -0,0 +1,90 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilograms_per_cubic_meter"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilograms_per_cubic_meter"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.grams_per_milliliter"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.grams_per_milliliter"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilograms_per_liter"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilograms_per_liter"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.ounces_per_cubic_foot"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.ounces_per_cubic_foot"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.ounces_per_cubic_inch"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.ounces_per_cubic_inch"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.ounces_per_gallon"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.ounces_per_gallon"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.pounds_per_cubic_foot"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.pounds_per_cubic_foot"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.pounds_per_cubic_inch"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.pounds_per_cubic_inch"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.pounds_per_gallon"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.pounds_per_gallon"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.slugs_per_cubic_foot"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.slugs_per_cubic_foot"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_energy_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_energy_type_caster.h new file mode 100644 index 0000000000..e747cd80e2 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_energy_type_caster.h @@ -0,0 +1,146 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.joules"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.joules"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanojoules"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanojoules"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microjoules"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microjoules"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millijoules"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millijoules"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilojoules"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilojoules"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.calories"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.calories"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanocalories"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanocalories"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microcalories"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microcalories"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millicalories"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millicalories"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilocalories"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilocalories"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilowatt_hours"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilowatt_hours"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.watt_hours"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.watt_hours"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.british_thermal_units"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.british_thermal_units"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.british_thermal_units_iso"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.british_thermal_units_iso"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.british_thermal_units_59"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.british_thermal_units_59"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.therms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.therms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.foot_pounds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.foot_pounds"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_force_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_force_type_caster.h new file mode 100644 index 0000000000..2b1f692d2c --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_force_type_caster.h @@ -0,0 +1,82 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.newtons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.newtons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanonewtons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanonewtons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.micronewtons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.micronewtons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millinewtons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millinewtons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilonewtons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilonewtons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.pounds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.pounds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.dynes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.dynes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kiloponds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kiloponds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.poundals"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.poundals"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_frequency_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_frequency_type_caster.h new file mode 100644 index 0000000000..3618eb71a0 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_frequency_type_caster.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.hertz"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.hertz"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanohertz"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanohertz"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microhertz"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microhertz"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millihertz"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millihertz"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilohertz"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilohertz"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_illuminance_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_illuminance_type_caster.h new file mode 100644 index 0000000000..d242178157 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_illuminance_type_caster.h @@ -0,0 +1,74 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.luxes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.luxes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanoluxes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanoluxes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microluxes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microluxes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliluxes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliluxes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kiloluxes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kiloluxes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.footcandles"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.footcandles"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.lumens_per_square_inch"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.lumens_per_square_inch"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.phots"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.phots"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_impedance_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_impedance_type_caster.h new file mode 100644 index 0000000000..b4af8ad029 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_impedance_type_caster.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.ohms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.ohms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanoohms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanoohms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microohms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microohms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliohms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliohms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kiloohms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kiloohms"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_inductance_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_inductance_type_caster.h new file mode 100644 index 0000000000..59700cf085 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_inductance_type_caster.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.henries"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.henries"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanohenries"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanohenries"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microhenries"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microhenries"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millihenries"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millihenries"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilohenries"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilohenries"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_length_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_length_type_caster.h new file mode 100644 index 0000000000..9f0cee4022 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_length_type_caster.h @@ -0,0 +1,194 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.meters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.meters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanometers"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanometers"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.micrometers"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.micrometers"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millimeters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millimeters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.centimeters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.centimeters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilometers"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilometers"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.feet"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.feet"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.mils"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.mils"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.inches"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.inches"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.miles"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.miles"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nauticalMiles"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nauticalMiles"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.astronicalUnits"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.astronicalUnits"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.lightyears"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.lightyears"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.parsecs"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.parsecs"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.angstroms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.angstroms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cubits"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cubits"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.fathoms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.fathoms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.chains"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.chains"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.furlongs"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.furlongs"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.hands"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.hands"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.leagues"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.leagues"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nauticalLeagues"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nauticalLeagues"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.yards"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.yards"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_luminous_flux_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_luminous_flux_type_caster.h new file mode 100644 index 0000000000..bfb2209bfc --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_luminous_flux_type_caster.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.lumens"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.lumens"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanolumens"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanolumens"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microlumens"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microlumens"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millilumens"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millilumens"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilolumens"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilolumens"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_luminous_intensity_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_luminous_intensity_type_caster.h new file mode 100644 index 0000000000..4d4a382b7e --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_luminous_intensity_type_caster.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.candelas"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.candelas"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanocandelas"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanocandelas"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microcandelas"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microcandelas"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millicandelas"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millicandelas"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilocandelas"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilocandelas"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_magnetic_field_strength_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_magnetic_field_strength_type_caster.h new file mode 100644 index 0000000000..f52feb6cd3 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_magnetic_field_strength_type_caster.h @@ -0,0 +1,58 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.teslas"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.teslas"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanoteslas"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanoteslas"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microteslas"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microteslas"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliteslas"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliteslas"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kiloteslas"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kiloteslas"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.gauss"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.gauss"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_magnetic_flux_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_magnetic_flux_type_caster.h new file mode 100644 index 0000000000..6d0b9a7b30 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_magnetic_flux_type_caster.h @@ -0,0 +1,58 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.webers"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.webers"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanowebers"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanowebers"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microwebers"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microwebers"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliwebers"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliwebers"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilowebers"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilowebers"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.maxwells"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.maxwells"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_mass_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_mass_type_caster.h new file mode 100644 index 0000000000..27f091234b --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_mass_type_caster.h @@ -0,0 +1,114 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.grams"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.grams"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanograms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanograms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.micrograms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.micrograms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milligrams"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milligrams"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilograms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilograms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.metric_tons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.metric_tons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.pounds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.pounds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.long_tons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.long_tons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.short_tons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.short_tons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.stone"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.stone"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.ounces"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.ounces"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.carats"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.carats"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.slugs"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.slugs"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_misc_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_misc_type_caster.h new file mode 100644 index 0000000000..9bd4dd775c --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_misc_type_caster.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { + +template <> struct handle_type_name { + static constexpr auto name = _("float"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("float"); +}; + +// template <> struct handle_type_name { +// static constexpr auto name = _("float"); +// }; + +// template <> struct handle_type_name { +// static constexpr auto name = _("float"); +// }; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" \ No newline at end of file diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_moment_of_inertia_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_moment_of_inertia_type_caster.h new file mode 100644 index 0000000000..ff9e370788 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_moment_of_inertia_type_caster.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilogram_square_meters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilogram_square_meters"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_power_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_power_type_caster.h new file mode 100644 index 0000000000..6082e37cfa --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_power_type_caster.h @@ -0,0 +1,58 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.watts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.watts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanowatts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanowatts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microwatts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microwatts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliwatts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliwatts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilowatts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilowatts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.horsepower"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.horsepower"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_pressure_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_pressure_type_caster.h new file mode 100644 index 0000000000..c12ffb1d43 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_pressure_type_caster.h @@ -0,0 +1,90 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.pascals"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.pascals"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanopascals"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanopascals"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.micropascals"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.micropascals"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millipascals"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millipascals"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilopascals"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilopascals"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.bars"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.bars"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.mbars"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.mbars"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.atmospheres"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.atmospheres"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.pounds_per_square_inch"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.pounds_per_square_inch"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.torrs"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.torrs"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_radiation_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_radiation_type_caster.h new file mode 100644 index 0000000000..0e8c268dc7 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_radiation_type_caster.h @@ -0,0 +1,154 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.becquerels"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.becquerels"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanobecquerels"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanobecquerels"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microbecquerels"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microbecquerels"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millibecquerels"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millibecquerels"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilobecquerels"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilobecquerels"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.grays"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.grays"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanograys"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanograys"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.micrograys"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.micrograys"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milligrays"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milligrays"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilograys"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilograys"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.sieverts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.sieverts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanosieverts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanosieverts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microsieverts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microsieverts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millisieverts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millisieverts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilosieverts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilosieverts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.curies"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.curies"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.rutherfords"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.rutherfords"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.rads"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.rads"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_solid_angle_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_solid_angle_type_caster.h new file mode 100644 index 0000000000..9e32b50068 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_solid_angle_type_caster.h @@ -0,0 +1,66 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.steradians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.steradians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanosteradians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanosteradians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microsteradians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microsteradians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millisteradians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millisteradians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilosteradians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilosteradians"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.degrees_squared"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.degrees_squared"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.spats"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.spats"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_substance_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_substance_type_caster.h new file mode 100644 index 0000000000..08e29242e7 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_substance_type_caster.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.moles"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.moles"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_temperature_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_temperature_type_caster.h new file mode 100644 index 0000000000..3d068c7eb2 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_temperature_type_caster.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kelvin"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kelvin"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.celsius"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.celsius"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.fahrenheit"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.fahrenheit"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.reaumur"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.reaumur"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.rankine"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.rankine"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_time_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_time_type_caster.h new file mode 100644 index 0000000000..4edfa26aef --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_time_type_caster.h @@ -0,0 +1,106 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.seconds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.seconds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanoseconds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanoseconds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microseconds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microseconds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliseconds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliseconds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kiloseconds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kiloseconds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.minutes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.minutes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.hours"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.hours"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.days"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.days"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.weeks"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.weeks"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.years"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.years"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.julian_years"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.julian_years"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.gregorian_years"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.gregorian_years"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_torque_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_torque_type_caster.h new file mode 100644 index 0000000000..7954423a23 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_torque_type_caster.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.newton_meters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.newton_meters"); +}; + +// template <> struct handle_type_name { +// static constexpr auto name = _("wpimath.units.foot_pounds"); +// }; + +// template <> struct handle_type_name { +// static constexpr auto name = _("wpimath.units.foot_pounds"); +// }; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.foot_poundals"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.foot_poundals"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.inch_pounds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.inch_pounds"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.meter_kilograms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.meter_kilograms"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_velocity_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_velocity_type_caster.h new file mode 100644 index 0000000000..7c1e2e5642 --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_velocity_type_caster.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.meters_per_second"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.meters_per_second"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.feet_per_second"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.feet_per_second"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.miles_per_hour"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.miles_per_hour"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilometers_per_hour"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilometers_per_hour"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.knots"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.knots"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_voltage_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_voltage_type_caster.h new file mode 100644 index 0000000000..67faeec08e --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_voltage_type_caster.h @@ -0,0 +1,66 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.volts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.volts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanovolts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanovolts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microvolts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microvolts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millivolts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.millivolts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilovolts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kilovolts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.statvolts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.statvolts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.abvolts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.abvolts"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_volume_type_caster.h b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_volume_type_caster.h new file mode 100644 index 0000000000..c294c3b31d --- /dev/null +++ b/wpimath/src/main/python/wpimath/_impl/src/type_casters/units_volume_type_caster.h @@ -0,0 +1,274 @@ +#pragma once + +#include + +namespace pybind11 { +namespace detail { +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cubic_meters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cubic_meters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cubic_millimeters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cubic_millimeters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cubic_kilometers"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cubic_kilometers"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.liters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.liters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanoliters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.nanoliters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microliters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.microliters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliliters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.milliliters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kiloliters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.kiloliters"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cubic_inches"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cubic_inches"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cubic_feet"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cubic_feet"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cubic_yards"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cubic_yards"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cubic_miles"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cubic_miles"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.gallons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.gallons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.quarts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.quarts"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.pints"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.pints"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cups"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cups"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.fluid_ounces"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.fluid_ounces"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.barrels"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.barrels"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.bushels"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.bushels"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cords"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cords"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cubic_fathoms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.cubic_fathoms"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.tablespoons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.tablespoons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.teaspoons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.teaspoons"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.pinches"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.pinches"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.dashes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.dashes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.drops"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.drops"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.fifths"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.fifths"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.drams"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.drams"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.gills"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.gills"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.pecks"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.pecks"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.sacks"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.sacks"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.shots"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.shots"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.strikes"); +}; + +template <> struct handle_type_name { + static constexpr auto name = _("wpimath.units.strikes"); +}; + +} // namespace detail +} // namespace pybind11 + +#include "_units_base_type_caster.h" diff --git a/wpimath/src/main/python/wpimath/controller/__init__.py b/wpimath/src/main/python/wpimath/controller/__init__.py new file mode 100644 index 0000000000..53070f020f --- /dev/null +++ b/wpimath/src/main/python/wpimath/controller/__init__.py @@ -0,0 +1,60 @@ +# autogenerated by 'semiwrap create-imports wpimath.controller wpimath._controls._controls.controller' +from .._controls._controls.controller import ( + ArmFeedforward, + BangBangController, + ControlAffinePlantInversionFeedforward_1_1, + ControlAffinePlantInversionFeedforward_2_1, + ControlAffinePlantInversionFeedforward_2_2, + DifferentialDriveAccelerationLimiter, + DifferentialDriveWheelVoltages, + ElevatorFeedforward, + HolonomicDriveController, + ImplicitModelFollower_1_1, + ImplicitModelFollower_2_1, + ImplicitModelFollower_2_2, + LTVDifferentialDriveController, + LTVUnicycleController, + LinearPlantInversionFeedforward_1_1, + LinearPlantInversionFeedforward_2_1, + LinearPlantInversionFeedforward_2_2, + LinearPlantInversionFeedforward_3_2, + LinearQuadraticRegulator_1_1, + LinearQuadraticRegulator_2_1, + LinearQuadraticRegulator_2_2, + LinearQuadraticRegulator_3_2, + PIDController, + ProfiledPIDController, + ProfiledPIDControllerRadians, + SimpleMotorFeedforwardMeters, + SimpleMotorFeedforwardRadians, +) + +__all__ = [ + "ArmFeedforward", + "BangBangController", + "ControlAffinePlantInversionFeedforward_1_1", + "ControlAffinePlantInversionFeedforward_2_1", + "ControlAffinePlantInversionFeedforward_2_2", + "DifferentialDriveAccelerationLimiter", + "DifferentialDriveWheelVoltages", + "ElevatorFeedforward", + "HolonomicDriveController", + "ImplicitModelFollower_1_1", + "ImplicitModelFollower_2_1", + "ImplicitModelFollower_2_2", + "LTVDifferentialDriveController", + "LTVUnicycleController", + "LinearPlantInversionFeedforward_1_1", + "LinearPlantInversionFeedforward_2_1", + "LinearPlantInversionFeedforward_2_2", + "LinearPlantInversionFeedforward_3_2", + "LinearQuadraticRegulator_1_1", + "LinearQuadraticRegulator_2_1", + "LinearQuadraticRegulator_2_2", + "LinearQuadraticRegulator_3_2", + "PIDController", + "ProfiledPIDController", + "ProfiledPIDControllerRadians", + "SimpleMotorFeedforwardMeters", + "SimpleMotorFeedforwardRadians", +] diff --git a/wpimath/src/main/python/wpimath/estimator/__init__.py b/wpimath/src/main/python/wpimath/estimator/__init__.py new file mode 100644 index 0000000000..dd9b3ca8ef --- /dev/null +++ b/wpimath/src/main/python/wpimath/estimator/__init__.py @@ -0,0 +1,72 @@ +# autogenerated by 'semiwrap create-imports wpimath.estimator wpimath._controls._controls.estimator' +from .._controls._controls.estimator import ( + DifferentialDrivePoseEstimator, + DifferentialDrivePoseEstimator3d, + DifferentialDrivePoseEstimator3dBase, + DifferentialDrivePoseEstimatorBase, + ExtendedKalmanFilter_1_1_1, + ExtendedKalmanFilter_2_1_1, + ExtendedKalmanFilter_2_1_2, + ExtendedKalmanFilter_2_2_2, + KalmanFilter_1_1_1, + KalmanFilter_2_1_1, + KalmanFilter_2_1_2, + KalmanFilter_2_2_2, + KalmanFilter_3_2_3, + MecanumDrivePoseEstimator, + MecanumDrivePoseEstimator3d, + MecanumDrivePoseEstimator3dBase, + MecanumDrivePoseEstimatorBase, + SwerveDrive2PoseEstimator, + SwerveDrive2PoseEstimator3d, + SwerveDrive2PoseEstimator3dBase, + SwerveDrive2PoseEstimatorBase, + SwerveDrive3PoseEstimator, + SwerveDrive3PoseEstimator3d, + SwerveDrive3PoseEstimator3dBase, + SwerveDrive3PoseEstimatorBase, + SwerveDrive4PoseEstimator, + SwerveDrive4PoseEstimator3d, + SwerveDrive4PoseEstimator3dBase, + SwerveDrive4PoseEstimatorBase, + SwerveDrive6PoseEstimator, + SwerveDrive6PoseEstimator3d, + SwerveDrive6PoseEstimator3dBase, + SwerveDrive6PoseEstimatorBase, +) + +__all__ = [ + "DifferentialDrivePoseEstimator", + "DifferentialDrivePoseEstimator3d", + "DifferentialDrivePoseEstimator3dBase", + "DifferentialDrivePoseEstimatorBase", + "ExtendedKalmanFilter_1_1_1", + "ExtendedKalmanFilter_2_1_1", + "ExtendedKalmanFilter_2_1_2", + "ExtendedKalmanFilter_2_2_2", + "KalmanFilter_1_1_1", + "KalmanFilter_2_1_1", + "KalmanFilter_2_1_2", + "KalmanFilter_2_2_2", + "KalmanFilter_3_2_3", + "MecanumDrivePoseEstimator", + "MecanumDrivePoseEstimator3d", + "MecanumDrivePoseEstimator3dBase", + "MecanumDrivePoseEstimatorBase", + "SwerveDrive2PoseEstimator", + "SwerveDrive2PoseEstimator3d", + "SwerveDrive2PoseEstimator3dBase", + "SwerveDrive2PoseEstimatorBase", + "SwerveDrive3PoseEstimator", + "SwerveDrive3PoseEstimator3d", + "SwerveDrive3PoseEstimator3dBase", + "SwerveDrive3PoseEstimatorBase", + "SwerveDrive4PoseEstimator", + "SwerveDrive4PoseEstimator3d", + "SwerveDrive4PoseEstimator3dBase", + "SwerveDrive4PoseEstimatorBase", + "SwerveDrive6PoseEstimator", + "SwerveDrive6PoseEstimator3d", + "SwerveDrive6PoseEstimator3dBase", + "SwerveDrive6PoseEstimatorBase", +] diff --git a/wpimath/src/main/python/wpimath/filter/__init__.py b/wpimath/src/main/python/wpimath/filter/__init__.py new file mode 100644 index 0000000000..c9386e49d0 --- /dev/null +++ b/wpimath/src/main/python/wpimath/filter/__init__.py @@ -0,0 +1,6 @@ +from . import _init__filter + +# autogenerated by 'semiwrap create-imports wpimath.filter wpimath.filter._filter' +from ._filter import Debouncer, LinearFilter, MedianFilter, SlewRateLimiter + +__all__ = ["Debouncer", "LinearFilter", "MedianFilter", "SlewRateLimiter"] diff --git a/wpimath/src/main/python/wpimath/filter/filter.cpp b/wpimath/src/main/python/wpimath/filter/filter.cpp new file mode 100644 index 0000000000..1f541d1e98 --- /dev/null +++ b/wpimath/src/main/python/wpimath/filter/filter.cpp @@ -0,0 +1,7 @@ + +#include "semiwrap_init.wpimath.filter._filter.hpp" + +SEMIWRAP_PYBIND11_MODULE(m) +{ + initWrapper(m); +} diff --git a/wpimath/src/main/python/wpimath/geometry/__init__.py b/wpimath/src/main/python/wpimath/geometry/__init__.py new file mode 100644 index 0000000000..894909d4b6 --- /dev/null +++ b/wpimath/src/main/python/wpimath/geometry/__init__.py @@ -0,0 +1,40 @@ +from . import _init__geometry + +# autogenerated by 'semiwrap create-imports wpimath.geometry wpimath.geometry._geometry' +from ._geometry import ( + CoordinateAxis, + CoordinateSystem, + Ellipse2d, + Pose2d, + Pose3d, + Quaternion, + Rectangle2d, + Rotation2d, + Rotation3d, + Transform2d, + Transform3d, + Translation2d, + Translation3d, + Twist2d, + Twist3d, + rotationVectorToMatrix, +) + +__all__ = [ + "CoordinateAxis", + "CoordinateSystem", + "Ellipse2d", + "Pose2d", + "Pose3d", + "Quaternion", + "Rectangle2d", + "Rotation2d", + "Rotation3d", + "Transform2d", + "Transform3d", + "Translation2d", + "Translation3d", + "Twist2d", + "Twist3d", + "rotationVectorToMatrix", +] diff --git a/wpimath/src/main/python/wpimath/geometry/geometry.cpp b/wpimath/src/main/python/wpimath/geometry/geometry.cpp new file mode 100644 index 0000000000..3ae0f54301 --- /dev/null +++ b/wpimath/src/main/python/wpimath/geometry/geometry.cpp @@ -0,0 +1,7 @@ + +#include "semiwrap_init.wpimath.geometry._geometry.hpp" + +SEMIWRAP_PYBIND11_MODULE(m) +{ + initWrapper(m); +} diff --git a/wpimath/src/main/python/wpimath/geometry/include/rpy/geometryToString.h b/wpimath/src/main/python/wpimath/geometry/include/rpy/geometryToString.h new file mode 100644 index 0000000000..4ba07ad5b9 --- /dev/null +++ b/wpimath/src/main/python/wpimath/geometry/include/rpy/geometryToString.h @@ -0,0 +1,99 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace rpy { + +inline std::string toString(const frc::Rotation2d &self) { + return "Rotation2d(" + std::to_string(self.Radians()()) + ")"; +} + +inline std::string toString(const frc::Rotation3d &self) { + return "Rotation3d(" + "x=" + + std::to_string(self.X()()) + + ", " + "y=" + + std::to_string(self.Y()()) + + ", " + "z=" + + std::to_string(self.Z()()) + ")"; +} + +inline std::string toString(const frc::Translation2d &self) { + return "Translation2d(" + "x=" + + std::to_string(self.X()()) + + ", " + "y=" + + std::to_string(self.Y()()) + ")"; +} + +inline std::string toString(const frc::Translation3d &self) { + return "Translation3d(" + "x=" + + std::to_string(self.X()()) + + ", " + "y=" + + std::to_string(self.Y()()) + + ", " + "z=" + + std::to_string(self.Z()()) + ")"; +} + +inline std::string toString(const frc::Quaternion &self) { + return "Quaternion(" + "w=" + + std::to_string(self.W()) + + "," + "x=" + + std::to_string(self.X()) + + ", " + "y=" + + std::to_string(self.Y()) + + ", " + "z=" + + std::to_string(self.Z()) + ")"; +} + +inline std::string toString(const frc::Transform2d &self) { + return "Transform2d(" + rpy::toString(self.Translation()) + ", " + + rpy::toString(self.Rotation()) + ")"; +} + +inline std::string toString(const frc::Transform3d &self) { + return "Transform3d(" + rpy::toString(self.Translation()) + ", " + + rpy::toString(self.Rotation()) + ")"; +} + +inline std::string toString(const frc::Pose2d &self) { + return "Pose2d(" + rpy::toString(self.Translation()) + ", " + + rpy::toString(self.Rotation()) + ")"; +} + +inline std::string toString(const frc::Pose3d &self) { + return "Pose3d(" + rpy::toString(self.Translation()) + ", " + + rpy::toString(self.Rotation()) + ")"; +} + +inline std::string toString(const frc::Rectangle2d &self) { + return "Rectangle2d(center=" + rpy::toString(self.Center()) + + ", xWidth=" + std::to_string(self.XWidth()()) + + ", yWidth=" + std::to_string(self.YWidth()()) + ")"; +} + +inline std::string toString(const frc::Ellipse2d &self) { + return "Ellipse2d(center=" + rpy::toString(self.Center()) + + ", xSemiAxis=" + std::to_string(self.XSemiAxis()()) + + ", ySemiAxis=" + std::to_string(self.YSemiAxis()()) + ")"; +} + + +} // namespace rpy diff --git a/wpimath/src/main/python/wpimath/interpolation/__init__.py b/wpimath/src/main/python/wpimath/interpolation/__init__.py new file mode 100644 index 0000000000..c71091b338 --- /dev/null +++ b/wpimath/src/main/python/wpimath/interpolation/__init__.py @@ -0,0 +1,22 @@ +from . import _init__interpolation # noqa: F401 + +# autogenerated by 'robotpy-build create-imports wpimath.interpolation wpimath.interpolation._interpolation' +from ._interpolation import ( + TimeInterpolatableFloatBuffer, + TimeInterpolatablePose2dBuffer, + TimeInterpolatablePose3dBuffer, + TimeInterpolatableRotation2dBuffer, + TimeInterpolatableRotation3dBuffer, + TimeInterpolatableTranslation2dBuffer, + TimeInterpolatableTranslation3dBuffer, +) + +__all__ = [ + "TimeInterpolatableFloatBuffer", + "TimeInterpolatablePose2dBuffer", + "TimeInterpolatablePose3dBuffer", + "TimeInterpolatableRotation2dBuffer", + "TimeInterpolatableRotation3dBuffer", + "TimeInterpolatableTranslation2dBuffer", + "TimeInterpolatableTranslation3dBuffer", +] diff --git a/wpimath/src/main/python/wpimath/interpolation/interpolation.cpp b/wpimath/src/main/python/wpimath/interpolation/interpolation.cpp new file mode 100644 index 0000000000..d7e3226cc0 --- /dev/null +++ b/wpimath/src/main/python/wpimath/interpolation/interpolation.cpp @@ -0,0 +1,7 @@ + +#include "semiwrap_init.wpimath.interpolation._interpolation.hpp" + +SEMIWRAP_PYBIND11_MODULE(m) +{ + initWrapper(m); +} diff --git a/wpimath/src/main/python/wpimath/kinematics/__init__.py b/wpimath/src/main/python/wpimath/kinematics/__init__.py new file mode 100644 index 0000000000..4327debb3d --- /dev/null +++ b/wpimath/src/main/python/wpimath/kinematics/__init__.py @@ -0,0 +1,70 @@ +from . import _init__kinematics + +# autogenerated by 'robotpy-build create-imports wpimath.kinematics' +from ._kinematics import ( + ChassisSpeeds, + DifferentialDriveKinematics, + DifferentialDriveKinematicsBase, + DifferentialDriveOdometry, + DifferentialDriveOdometryBase, + DifferentialDriveWheelPositions, + DifferentialDriveWheelSpeeds, + MecanumDriveKinematics, + MecanumDriveKinematicsBase, + MecanumDriveOdometry, + MecanumDriveOdometryBase, + MecanumDriveWheelPositions, + MecanumDriveWheelSpeeds, + SwerveDrive2Kinematics, + SwerveDrive2KinematicsBase, + SwerveDrive2Odometry, + SwerveDrive2OdometryBase, + SwerveDrive3Kinematics, + SwerveDrive3KinematicsBase, + SwerveDrive3Odometry, + SwerveDrive3OdometryBase, + SwerveDrive4Kinematics, + SwerveDrive4KinematicsBase, + SwerveDrive4Odometry, + SwerveDrive4OdometryBase, + SwerveDrive6Kinematics, + SwerveDrive6KinematicsBase, + SwerveDrive6Odometry, + SwerveDrive6OdometryBase, + SwerveModulePosition, + SwerveModuleState, +) + +__all__ = [ + "ChassisSpeeds", + "DifferentialDriveKinematics", + "DifferentialDriveKinematicsBase", + "DifferentialDriveOdometry", + "DifferentialDriveOdometryBase", + "DifferentialDriveWheelPositions", + "DifferentialDriveWheelSpeeds", + "MecanumDriveKinematics", + "MecanumDriveKinematicsBase", + "MecanumDriveOdometry", + "MecanumDriveOdometryBase", + "MecanumDriveWheelPositions", + "MecanumDriveWheelSpeeds", + "SwerveDrive2Kinematics", + "SwerveDrive2KinematicsBase", + "SwerveDrive2Odometry", + "SwerveDrive2OdometryBase", + "SwerveDrive3Kinematics", + "SwerveDrive3KinematicsBase", + "SwerveDrive3Odometry", + "SwerveDrive3OdometryBase", + "SwerveDrive4Kinematics", + "SwerveDrive4KinematicsBase", + "SwerveDrive4Odometry", + "SwerveDrive4OdometryBase", + "SwerveDrive6Kinematics", + "SwerveDrive6KinematicsBase", + "SwerveDrive6Odometry", + "SwerveDrive6OdometryBase", + "SwerveModulePosition", + "SwerveModuleState", +] diff --git a/wpimath/src/main/python/wpimath/kinematics/kinematics.cpp b/wpimath/src/main/python/wpimath/kinematics/kinematics.cpp new file mode 100644 index 0000000000..6b35ec1de3 --- /dev/null +++ b/wpimath/src/main/python/wpimath/kinematics/kinematics.cpp @@ -0,0 +1,7 @@ + +#include "semiwrap_init.wpimath.kinematics._kinematics.hpp" + +SEMIWRAP_PYBIND11_MODULE(m) +{ + initWrapper(m); +} diff --git a/wpimath/src/main/python/wpimath/optimization/__init__.py b/wpimath/src/main/python/wpimath/optimization/__init__.py new file mode 100644 index 0000000000..8f7a6c6502 --- /dev/null +++ b/wpimath/src/main/python/wpimath/optimization/__init__.py @@ -0,0 +1,4 @@ +# autogenerated by 'semiwrap create-imports wpimath.optimization wpimath._controls._controls.optimization' +from .._controls._controls.optimization import SimulatedAnnealing + +__all__ = ["SimulatedAnnealing"] diff --git a/wpimath/src/main/python/wpimath/path/__init__.py b/wpimath/src/main/python/wpimath/path/__init__.py new file mode 100644 index 0000000000..8e82c2640b --- /dev/null +++ b/wpimath/src/main/python/wpimath/path/__init__.py @@ -0,0 +1,4 @@ +# autogenerated by 'semiwrap create-imports wpimath.path wpimath._controls._controls.path' +from .._controls._controls.path import TravelingSalesman + +__all__ = ["TravelingSalesman"] diff --git a/wpimath/src/main/python/wpimath/py.typed b/wpimath/src/main/python/wpimath/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/wpimath/src/main/python/wpimath/spline/__init__.py b/wpimath/src/main/python/wpimath/spline/__init__.py new file mode 100644 index 0000000000..37f1a65ec7 --- /dev/null +++ b/wpimath/src/main/python/wpimath/spline/__init__.py @@ -0,0 +1,20 @@ +from . import _init__spline + +# autogenerated by 'semiwrap create-imports wpimath.spline wpimath.spline._spline' +from ._spline import ( + CubicHermiteSpline, + QuinticHermiteSpline, + Spline3, + Spline5, + SplineHelper, + SplineParameterizer, +) + +__all__ = [ + "CubicHermiteSpline", + "QuinticHermiteSpline", + "Spline3", + "Spline5", + "SplineHelper", + "SplineParameterizer", +] diff --git a/wpimath/src/main/python/wpimath/spline/spline.cpp b/wpimath/src/main/python/wpimath/spline/spline.cpp new file mode 100644 index 0000000000..b3cd6d33ae --- /dev/null +++ b/wpimath/src/main/python/wpimath/spline/spline.cpp @@ -0,0 +1,7 @@ + +#include "semiwrap_init.wpimath.spline._spline.hpp" + +SEMIWRAP_PYBIND11_MODULE(m) +{ + initWrapper(m); +} diff --git a/wpimath/src/main/python/wpimath/src/wpimath.cpp b/wpimath/src/main/python/wpimath/src/wpimath.cpp new file mode 100644 index 0000000000..15fff17025 --- /dev/null +++ b/wpimath/src/main/python/wpimath/src/wpimath.cpp @@ -0,0 +1,4 @@ + +#include "semiwrap_init.wpimath._wpimath.hpp" + +SEMIWRAP_PYBIND11_MODULE(m) { initWrapper(m); } diff --git a/wpimath/src/main/python/wpimath/system/__init__.py b/wpimath/src/main/python/wpimath/system/__init__.py new file mode 100644 index 0000000000..500033b153 --- /dev/null +++ b/wpimath/src/main/python/wpimath/system/__init__.py @@ -0,0 +1,40 @@ +# autogenerated by 'semiwrap create-imports wpimath.system wpimath._controls._controls.system' +from .._controls._controls.system import ( + LinearSystemLoop_1_1_1, + LinearSystemLoop_2_1_1, + LinearSystemLoop_2_1_2, + LinearSystemLoop_2_2_2, + LinearSystemLoop_3_2_3, + LinearSystem_1_1_1, + LinearSystem_1_1_2, + LinearSystem_1_1_3, + LinearSystem_2_1_1, + LinearSystem_2_1_2, + LinearSystem_2_1_3, + LinearSystem_2_2_1, + LinearSystem_2_2_2, + LinearSystem_2_2_3, + LinearSystem_3_2_1, + LinearSystem_3_2_2, + LinearSystem_3_2_3, +) + +__all__ = [ + "LinearSystemLoop_1_1_1", + "LinearSystemLoop_2_1_1", + "LinearSystemLoop_2_1_2", + "LinearSystemLoop_2_2_2", + "LinearSystemLoop_3_2_3", + "LinearSystem_1_1_1", + "LinearSystem_1_1_2", + "LinearSystem_1_1_3", + "LinearSystem_2_1_1", + "LinearSystem_2_1_2", + "LinearSystem_2_1_3", + "LinearSystem_2_2_1", + "LinearSystem_2_2_2", + "LinearSystem_2_2_3", + "LinearSystem_3_2_1", + "LinearSystem_3_2_2", + "LinearSystem_3_2_3", +] diff --git a/wpimath/src/main/python/wpimath/system/plant/__init__.py b/wpimath/src/main/python/wpimath/system/plant/__init__.py new file mode 100644 index 0000000000..a509aa3ca5 --- /dev/null +++ b/wpimath/src/main/python/wpimath/system/plant/__init__.py @@ -0,0 +1,3 @@ +from ..._controls._controls.plant import DCMotor, LinearSystemId + +__all__ = ["DCMotor", "LinearSystemId"] diff --git a/wpimath/src/main/python/wpimath/trajectory/__init__.py b/wpimath/src/main/python/wpimath/trajectory/__init__.py new file mode 100644 index 0000000000..6a0916bbb2 --- /dev/null +++ b/wpimath/src/main/python/wpimath/trajectory/__init__.py @@ -0,0 +1,20 @@ +# autogenerated by 'semiwrap create-imports wpimath.trajectory wpimath._controls._controls.trajectory' +from .._controls._controls.trajectory import ( + ExponentialProfileMeterVolts, + Trajectory, + TrajectoryConfig, + TrajectoryGenerator, + TrajectoryParameterizer, + TrapezoidProfile, + TrapezoidProfileRadians, +) + +__all__ = [ + "ExponentialProfileMeterVolts", + "Trajectory", + "TrajectoryConfig", + "TrajectoryGenerator", + "TrajectoryParameterizer", + "TrapezoidProfile", + "TrapezoidProfileRadians", +] diff --git a/wpimath/src/main/python/wpimath/trajectory/constraint/__init__.py b/wpimath/src/main/python/wpimath/trajectory/constraint/__init__.py new file mode 100644 index 0000000000..392ba9d8c1 --- /dev/null +++ b/wpimath/src/main/python/wpimath/trajectory/constraint/__init__.py @@ -0,0 +1,30 @@ +# autogenerated by 'semiwrap create-imports wpimath.trajectory.constraint wpimath._controls._controls.constraint' +from ..._controls._controls.constraint import ( + CentripetalAccelerationConstraint, + DifferentialDriveKinematicsConstraint, + DifferentialDriveVoltageConstraint, + EllipticalRegionConstraint, + MaxVelocityConstraint, + MecanumDriveKinematicsConstraint, + RectangularRegionConstraint, + SwerveDrive2KinematicsConstraint, + SwerveDrive3KinematicsConstraint, + SwerveDrive4KinematicsConstraint, + SwerveDrive6KinematicsConstraint, + TrajectoryConstraint, +) + +__all__ = [ + "CentripetalAccelerationConstraint", + "DifferentialDriveKinematicsConstraint", + "DifferentialDriveVoltageConstraint", + "EllipticalRegionConstraint", + "MaxVelocityConstraint", + "MecanumDriveKinematicsConstraint", + "RectangularRegionConstraint", + "SwerveDrive2KinematicsConstraint", + "SwerveDrive3KinematicsConstraint", + "SwerveDrive4KinematicsConstraint", + "SwerveDrive6KinematicsConstraint", + "TrajectoryConstraint", +] diff --git a/wpimath/src/main/python/wpimath/units.py b/wpimath/src/main/python/wpimath/units.py new file mode 100644 index 0000000000..540b7afb95 --- /dev/null +++ b/wpimath/src/main/python/wpimath/units.py @@ -0,0 +1,549 @@ +# Copyright (c) FIRST and other WPILib contributors. +# Open Source Software; you can modify and/or share it under the terms of +# the WPILib BSD license file in the root directory of this project. + +from __future__ import annotations + +import math +import typing + +kInchesPerFoot = 12.0 +kMetersPerInch = 0.0254 +kSecondsPerMinute = 60 +kMillisecondsPerSecond = 1000 +kKilogramsPerLb = 0.453592 + + +def metersToFeet(m: meters) -> feet: + """Converts given meters to feet. + + :param m: The meters to convert to feet. + + :returns: Feet converted from meters. + """ + return metersToInches(m) / kInchesPerFoot + + +def feetToMeters(ft: feet) -> meters: + """Converts given feet to meters. + + :param ft: The feet to convert to meters. + + :returns: Meters converted from feet. + """ + return inchesToMeters(ft * kInchesPerFoot) + + +def metersToInches(m: meters) -> inches: + """Converts given meters to inches. + + :param m: The meters to convert to inches. + + :returns: Inches converted from meters. + """ + return m / kMetersPerInch + + +def inchesToMeters(i: inches) -> meters: + """Converts given inches to meters. + + :param i: The inches to convert to meters. + + :returns: Meters converted from inches. + """ + return i * kMetersPerInch + + +# Converts given degrees to radians. +degreesToRadians: typing.Callable[[degrees], radians] = math.radians + +# Converts given radians to degrees. +radiansToDegrees: typing.Callable[[radians], degrees] = math.degrees + + +def radiansToRotations(rad: radians) -> float: + """Converts given radians to rotations. + + :param rad: The radians to convert. + + :returns: rotations Converted from radians. + """ + return rad / math.tau + + +def degreesToRotations(deg: degrees) -> float: + """Converts given degrees to rotations. + + :param deg: The degrees to convert. + + :returns: rotations Converted from degrees. + """ + return deg / 360.0 + + +def rotationsToDegrees(rotations: float) -> degrees: + """Converts given rotations to degrees. + + :param rotations: The rotations to convert. + + :returns: degrees Converted from rotations. + """ + return rotations * 360.0 + + +def rotationsToRadians(rotations: float) -> float: + """Converts given rotations to radians. + + :param rotations: The rotations to convert. + + :returns: radians Converted from rotations. + """ + return rotations * math.tau + + +def rotationsPerMinuteToRadiansPerSecond( + rpm: revolutions_per_minute, +) -> radians_per_second: + """Converts rotations per minute to radians per second. + + :param rpm: The rotations per minute to convert to radians per second. + + :returns: Radians per second converted from rotations per minute. + """ + return (rpm / kSecondsPerMinute) * math.tau + + +def radiansPerSecondToRotationsPerMinute( + rps: radians_per_second, +) -> revolutions_per_minute: + """Converts radians per second to rotations per minute. + + :param rps: The radians per second to convert to from rotations per minute. + + :returns: Rotations per minute converted from radians per second. + """ + return (rps * kSecondsPerMinute) / math.tau + + +def millisecondsToSeconds(ms: milliseconds) -> seconds: + """Converts given milliseconds to seconds. + + :param ms: The milliseconds to convert to seconds. + + :returns: Seconds converted from milliseconds. + """ + return ms / kMillisecondsPerSecond + + +def secondsToMilliseconds(s: seconds) -> milliseconds: + """Converts given seconds to milliseconds. + + :param s: The seconds to convert to milliseconds. + + :returns: Milliseconds converted from seconds. + """ + return s * kMillisecondsPerSecond + + +def kilogramsToLbs(kg: kilograms) -> pounds: + """Converts kilograms into lbs (pound-mass). + + :param kg: The kilograms to convert to lbs (pound-mass). + + :returns: Lbs (pound-mass) converted from kilograms. + """ + return kg / kKilogramsPerLb + + +def lbsToKilograms(lbs: pounds) -> kilograms: + """Converts lbs (pound-mass) into kilograms. + + :param lbs: The lbs (pound-mass) to convert to kilograms. + + :returns: Kilograms converted from lbs (pound-mass). + """ + return lbs * kKilogramsPerLb + + +# +# type aliases to make type checkers happier +# - None of these types do any enforcement of unit related things +# + +# acceleration +meters_per_second_squared = float +feet_per_second_squared = float +standard_gravity = float + +# angle +radians = float +nanoradians = float +microradians = float +milliradians = float +kiloradians = float +degrees = float +arcminutes = float +arcseconds = float +milliarcseconds = float +turns = float +gradians = float + +# angular acceleration +radians_per_second_squared = float +degrees_per_second_squared = float +turns_per_second_squared = float + +# angular velocity +radians_per_second = float +degrees_per_second = float +turns_per_second = float +revolutions_per_minute = float +milliarcseconds_per_year = float + +# area +square_meters = float +square_feet = float +square_inches = float +square_miles = float +square_kilometers = float +hectares = float +acres = float + +# capacitance +farads = float +nanofarads = float +microfarads = float +millifarads = float +kilofarads = float + +# compound types +radians_per_meter = float +radians_per_second_per_volt = float +units_per_second = float +units_per_second_squared = float +volt_seconds = float +volt_seconds_squared = float +volt_seconds_per_meter = float +volt_seconds_squared_per_meter = float +volt_seconds_per_feet = float +volt_seconds_squared_per_feet = float +volt_seconds_per_radian = float +volt_seconds_squared_per_radian = float +unit_seconds_squared_per_unit = float +meters_per_second_squared_per_volt = float + +# charge +coulombs = float +nanocoulombs = float +microcoulombs = float +millicoulombs = float +kilocoulombs = float +ampere_hours = float +nanoampere_hours = float +microampere_hours = float +milliampere_hours = float +kiloampere_hours = float + +# concentration +parts_per_million = float +parts_per_billion = float +parts_per_trillion = float +percent = float + +# conductance +siemens = float +nanosiemens = float +microsiemens = float +millisiemens = float +kilosiemens = float + +# current +amperes = float +nanoamperes = float +microamperes = float +milliamperes = float +kiloamperes = float + +# data +exabytes = float +exabits = float + +# data transfer +exabytes_per_second = float +exabits_per_second = float + +# density +kilograms_per_cubic_meter = float +grams_per_milliliter = float +kilograms_per_liter = float +ounces_per_cubic_foot = float +ounces_per_cubic_inch = float +ounces_per_gallon = float +pounds_per_cubic_foot = float +pounds_per_cubic_inch = float +pounds_per_gallon = float +slugs_per_cubic_foot = float + +# energy +joules = float +nanojoules = float +microjoules = float +millijoules = float +kilojoules = float +calories = float +nanocalories = float +microcalories = float +millicalories = float +kilocalories = float +kilowatt_hours = float +watt_hours = float +british_thermal_units = float +british_thermal_units_iso = float +british_thermal_units_59 = float +therms = float +foot_pounds = float + +# force +newtons = float +nanonewtons = float +micronewtons = float +millinewtons = float +kilonewtons = float +pounds = float +dynes = float +kiloponds = float +poundals = float + +# frequency +hertz = float +nanohertz = float +microhertz = float +millihertz = float +kilohertz = float + +# illuminance +luxes = float +nanoluxes = float +microluxes = float +milliluxes = float +kiloluxes = float +footcandles = float +lumens_per_square_inch = float +phots = float + +# inductance +henries = float +nanohenries = float +microhenries = float +millihenries = float +kilohenries = float + +# length +meters = float +nanometers = float +micrometers = float +millimeters = float +centimeters = float +kilometers = float +feet = float +mils = float +inches = float +miles = float +nauticalMiles = float +astronicalUnits = float +lightyears = float +parsecs = float +angstroms = float +cubits = float +fathoms = float +chains = float +furlongs = float +hands = float +leagues = float +nauticalLeagues = float +yards = float + +# luminous flux +lumens = float +nanolumens = float +microlumens = float +millilumens = float +kilolumens = float + +# luminous intensity +candelas = float +nanocandelas = float +microcandelas = float +millicandelas = float +kilocandelas = float + +# magnetic flux +webers = float +nanowebers = float +microwebers = float +milliwebers = float +kilowebers = float +maxwells = float + +# magnetic strength +teslas = float +nanoteslas = float +microteslas = float +milliteslas = float +kiloteslas = float +gauss = float + +# mass +grams = float +nanograms = float +micrograms = float +milligrams = float +kilograms = float +metric_tons = float +pounds = float +long_tons = float +short_tons = float +stone = float +ounces = float +carats = float +slugs = float + +# moment of inertia +kilogram_square_meters = float + +# power +watts = float +nanowatts = float +microwatts = float +milliwatts = float +kilowatts = float +horsepower = float + +# pressure +pascals = float +nanopascals = float +micropascals = float +millipascals = float +kilopascals = float +bars = float +mbars = float +atmospheres = float +pounds_per_square_inch = float +torrs = float + +# radiation +becquerels = float +nanobecquerels = float +microbecquerels = float +millibecquerels = float +kilobecquerels = float +grays = float +nanograys = float +micrograys = float +milligrays = float +kilograys = float +sieverts = float +nanosieverts = float +microsieverts = float +millisieverts = float +kilosieverts = float +curies = float +rutherfords = float +rads = float + +# resistance +ohms = float +nanoohms = float +microohms = float +milliohms = float +kiloohms = float + +# solid angle +steradians = float +nanosteradians = float +microsteradians = float +millisteradians = float +kilosteradians = float +degrees_squared = float +spats = float + +# substance +moles = float + +# temperature +kelvin = float +celsius = float +fahrenheit = float +reaumur = float +rankine = float + +# time +seconds = float +nanoseconds = float +microseconds = float +milliseconds = float +kiloseconds = float +minutes = float +hours = float +days = float +weeks = float +years = float +julian_years = float +gregorian_years = float + +# torque +newton_meters = float +foot_poundals = float +inch_pounds = float +meter_kilograms = float + +# velocity +meters_per_second = float +feet_per_second = float +miles_per_hour = float +kilometers_per_hour = float +knots = float + +# voltage +volts = float +nanovolts = float +microvolts = float +millivolts = float +kilovolts = float +statvolts = float +abvolts = float + +# volume +cubic_meters = float +cubic_millimeters = float +cubic_kilometers = float +liters = float +nanoliters = float +microliters = float +milliliters = float +kiloliters = float +cubic_inches = float +cubic_feet = float +cubic_yards = float +cubic_miles = float +gallons = float +quarts = float +pints = float +cups = float +fluid_ounces = float +barrels = float +bushels = float +cords = float +cubic_fathoms = float +tablespoons = float +teaspoons = float +pinches = float +dashes = float +drops = float +fifths = float +drams = float +gills = float +pecks = float +sacks = float +shots = float +strikes = float diff --git a/wpimath/src/test/python/conftest.py b/wpimath/src/test/python/conftest.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/wpimath/src/test/python/conftest.py @@ -0,0 +1 @@ + diff --git a/wpimath/src/test/python/cpp/pyproject.toml b/wpimath/src/test/python/cpp/pyproject.toml new file mode 100644 index 0000000000..679f4bdf0f --- /dev/null +++ b/wpimath/src/test/python/cpp/pyproject.toml @@ -0,0 +1,27 @@ +[build-system] +build-backend = "hatchling.build" +requires = [ + "semiwrap", "hatch-meson", "hatchling", +] + +[project] +name = "wpimath_test" +version = "0.1" +description = "Test project for verifying robotpy-build behavior" +authors = [ + {name = "RobotPy Development Team", email = "robotpy@googlegroups.com"}, +] +license = "BSD-3-Clause" + +[tool.hatch.build.hooks.semiwrap] +[tool.hatch.build.hooks.meson] + +[tool.semiwrap] + +[tool.semiwrap.extension_modules."wpimath_test._wpimath_test"] +name = "wpimath_test" +depends = ["wpimath"] +includes = ["wpimath_test/include"] + +[tool.semiwrap.extension_modules."wpimath_test._wpimath_test".headers] +module = "module.h" diff --git a/wpimath/src/test/python/cpp/semiwrap/module.yml b/wpimath/src/test/python/cpp/semiwrap/module.yml new file mode 100644 index 0000000000..b9157d3737 --- /dev/null +++ b/wpimath/src/test/python/cpp/semiwrap/module.yml @@ -0,0 +1,19 @@ +--- + +classes: + SomeClass: + attributes: + kDefaultPeriod: + five_ft: + methods: + checkDefaultByName: + checkDefaultByNum1: + param_override: + period: + default: 50_ms + checkDefaultByNum2: + param_override: + period: + default: 0.050_s + ms2s: + ft2m: diff --git a/wpimath/src/test/python/cpp/wpimath_test/__init__.py b/wpimath/src/test/python/cpp/wpimath_test/__init__.py new file mode 100644 index 0000000000..5dff80ff06 --- /dev/null +++ b/wpimath/src/test/python/cpp/wpimath_test/__init__.py @@ -0,0 +1,8 @@ +from . import _init__wpimath_test + +# autogenerated by 'robotpy-build create-imports wpimath_test' +from ._wpimath_test import SomeClass + +__all__ = ["SomeClass"] + +del _init__wpimath_test diff --git a/wpimath/src/test/python/cpp/wpimath_test/include/module.h b/wpimath/src/test/python/cpp/wpimath_test/include/module.h new file mode 100644 index 0000000000..a4afe39d39 --- /dev/null +++ b/wpimath/src/test/python/cpp/wpimath_test/include/module.h @@ -0,0 +1,24 @@ + +#pragma once + +#include +#include + +struct SomeClass { + static constexpr auto s_constant = 2_s; + static constexpr auto ms_constant1 = 20_ms; + static constexpr units::second_t ms_constant2 = 50_ms; + static constexpr units::millisecond_t ms_constant3 = 0.20_s; + + bool checkDefaultByName1(units::second_t period = ms_constant1); + bool checkDefaultByName2(units::second_t period = ms_constant2); + bool checkDefaultByNum1(units::second_t period = 50_ms); + bool checkDefaultByNum2(units::second_t period = 0.5_s); + + units::second_t ms2s(units::millisecond_t ms); + units::millisecond_t s2ms(units::second_t s); + + static constexpr units::foot_t five_ft = 5_ft; + + units::meter_t ft2m(units::foot_t f); +}; \ No newline at end of file diff --git a/wpimath/src/test/python/cpp/wpimath_test/src/module.cpp b/wpimath/src/test/python/cpp/wpimath_test/src/module.cpp new file mode 100644 index 0000000000..c101790b32 --- /dev/null +++ b/wpimath/src/test/python/cpp/wpimath_test/src/module.cpp @@ -0,0 +1,53 @@ + +#include "semiwrap_init.wpimath_test._wpimath_test.hpp" +#include +#include + +SEMIWRAP_PYBIND11_MODULE(m) +{ + initWrapper(m); +} + +bool SomeClass::checkDefaultByName1(units::second_t period) +{ + if (period != SomeClass::ms_constant1) { + throw std::runtime_error(units::to_string(period)); + } + return true; +} + +bool SomeClass::checkDefaultByName2(units::second_t period) +{ + if (period != SomeClass::ms_constant2) { + throw std::runtime_error(units::to_string(period)); + } + return true; +} + +bool SomeClass::checkDefaultByNum1(units::second_t period) +{ + if (period != 50_ms) { + throw std::runtime_error(units::to_string(period)); + } + return true; +} + +bool SomeClass::checkDefaultByNum2(units::second_t period) +{ + if (period != 50_ms) { + throw std::runtime_error(units::to_string(period)); + } + return true; +} + +units::meter_t SomeClass::ft2m(units::foot_t f) { + return f; +} + +units::second_t SomeClass::ms2s(units::millisecond_t ms) { + return ms; +} + +units::millisecond_t SomeClass::s2ms(units::second_t s) { + return s; +} diff --git a/wpimath/src/test/python/geometry/test_rotation2d.py b/wpimath/src/test/python/geometry/test_rotation2d.py new file mode 100644 index 0000000000..8457d3877b --- /dev/null +++ b/wpimath/src/test/python/geometry/test_rotation2d.py @@ -0,0 +1,82 @@ +import importlib.util +import math + +import pytest + +from wpimath.geometry import Rotation2d + + +@pytest.mark.parametrize( + "radians,degrees", + [ + (math.pi / 3, 60.0), + (math.pi / 4, 45.0), + ], +) +def test_radians_to_degrees(radians: float, degrees: float): + rot = Rotation2d(radians) + assert math.isclose(degrees, rot.degrees()) + + +@pytest.mark.parametrize( + "degrees,radians", + [ + (45.0, math.pi / 4), + (30.0, math.pi / 6), + ], +) +def test_degrees_to_radians(degrees: float, radians: float): + rot = Rotation2d.fromDegrees(degrees) + assert math.isclose(radians, rot.radians()) + + +def test_rotate_by_from_zero() -> None: + zero = Rotation2d() + rotated = zero + Rotation2d.fromDegrees(90) + + assert math.isclose(math.pi / 2, rotated.radians()) + assert math.isclose(90.0, rotated.degrees()) + + +def test_rotate_by_non_zero() -> None: + rot = Rotation2d.fromDegrees(90.0) + rot += Rotation2d.fromDegrees(30.0) + + assert math.isclose(120.0, rot.degrees()) + + +def test_minus() -> None: + rot1 = Rotation2d.fromDegrees(70) + rot2 = Rotation2d.fromDegrees(30) + + assert math.isclose(40.0, (rot1 - rot2).degrees()) + + +def test_unary_minus() -> None: + rot = Rotation2d.fromDegrees(20) + assert math.isclose(-20.0, (-rot).degrees()) + + +def test_equality() -> None: + rot1 = Rotation2d.fromDegrees(43.0) + rot2 = Rotation2d.fromDegrees(43.0) + assert rot1 == rot2 + + rot1 = Rotation2d.fromDegrees(-180.0) + rot2 = Rotation2d.fromDegrees(180.0) + assert rot1 == rot2 + + +def test_inequality() -> None: + rot1 = Rotation2d.fromDegrees(43.0) + rot2 = Rotation2d.fromDegrees(43.5) + assert rot1 != rot2 + + +@pytest.mark.skipif( + importlib.util.find_spec("numpy") is None, reason="numpy is not available" +) +def test_to_matrix() -> None: + before = Rotation2d.fromDegrees(20.0) + after = Rotation2d.fromMatrix(before.toMatrix()) + assert before == after diff --git a/wpimath/src/test/python/kinematics/test_chassis_speeds.py b/wpimath/src/test/python/kinematics/test_chassis_speeds.py new file mode 100644 index 0000000000..3b5fb19081 --- /dev/null +++ b/wpimath/src/test/python/kinematics/test_chassis_speeds.py @@ -0,0 +1,72 @@ +import math + +from wpimath.geometry import Rotation2d +from wpimath.kinematics import ChassisSpeeds + + +def test_plus() -> None: + left = ChassisSpeeds(vx=1.0, vy=0.5, omega=0.75) + right = ChassisSpeeds(vx=2.0, vy=1.5, omega=0.25) + + result = left + right + + assert math.isclose(3.0, result.vx) + assert math.isclose(2.0, result.vy) + assert math.isclose(1.0, result.omega) + + +def test_minus() -> None: + left = ChassisSpeeds(vx=1.0, vy=0.5, omega=0.75) + right = ChassisSpeeds(vx=2.0, vy=0.5, omega=0.25) + + result = left - right + + assert math.isclose(-1.0, result.vx) + assert math.isclose(0, result.vy) + assert math.isclose(0.5, result.omega) + + +def test_neg() -> None: + speeds = ChassisSpeeds(vx=1.0, vy=0.5, omega=0.75) + + result = -speeds + + assert math.isclose(-1.0, result.vx) + assert math.isclose(-0.5, result.vy) + assert math.isclose(-0.75, result.omega) + + +def test_multiplication() -> None: + speeds = ChassisSpeeds(vx=1.0, vy=0.5, omega=0.75) + + result = speeds * 2 + + assert math.isclose(2.0, result.vx) + assert math.isclose(1.0, result.vy) + assert math.isclose(1.5, result.omega) + + +def test_division() -> None: + speeds = ChassisSpeeds(vx=1.0, vy=0.5, omega=0.75) + + result = speeds / 2 + + assert math.isclose(0.5, result.vx) + assert math.isclose(0.25, result.vy) + assert math.isclose(0.375, result.omega) + + +def test_unpack() -> None: + speeds = ChassisSpeeds(1, 1, 0.5) + + vx, vy, omega = speeds + + assert math.isclose(1, vx) + assert math.isclose(1, vy) + assert math.isclose(0.5, omega) + + +def test_repr() -> None: + speeds = ChassisSpeeds(2.0, 1.0, 0.0) + + assert repr(speeds) == "ChassisSpeeds(vx=2.000000, vy=1.000000, omega=0.000000)" diff --git a/wpimath/src/test/python/kinematics/test_swerve.py b/wpimath/src/test/python/kinematics/test_swerve.py new file mode 100644 index 0000000000..b431ff8b45 --- /dev/null +++ b/wpimath/src/test/python/kinematics/test_swerve.py @@ -0,0 +1,75 @@ +import pytest + +from wpimath.geometry import Pose2d, Rotation2d, Translation2d +from wpimath.kinematics import ( + ChassisSpeeds, + SwerveDrive4Kinematics, + SwerveDrive4Odometry, + SwerveModulePosition, + SwerveModuleState, +) + + +@pytest.fixture() +def s4(): + fl = Translation2d(12, 12) + fr = Translation2d(12, -12) + bl = Translation2d(-12, 12) + br = Translation2d(-12, -12) + return SwerveDrive4Kinematics(fl, fr, bl, br) + + +def test_swerve4_straightline(s4: SwerveDrive4Kinematics): + chassisSpeeds = ChassisSpeeds(5, 0, 0) + + fl, fr, bl, br = s4.toSwerveModuleStates(chassisSpeeds) + assert fl.speed == pytest.approx(5.0) + assert fr.speed == pytest.approx(5.0) + assert bl.speed == pytest.approx(5.0) + assert br.speed == pytest.approx(5.0) + + assert fl.angle.radians() == pytest.approx(0.0) + assert fr.angle.radians() == pytest.approx(0.0) + assert bl.angle.radians() == pytest.approx(0.0) + assert br.angle.radians() == pytest.approx(0.0) + + +def test_swerve4_normalize(): + s1 = SwerveModuleState(5) + s2 = SwerveModuleState(6) + s3 = SwerveModuleState(4) + s4 = SwerveModuleState(7) + + states = SwerveDrive4Kinematics.desaturateWheelSpeeds((s1, s2, s3, s4), 5.5) + + kFactor = 5.5 / 7.0 + + assert states[0].speed == pytest.approx(5.0 * kFactor) + assert states[1].speed == pytest.approx(6.0 * kFactor) + assert states[2].speed == pytest.approx(4.0 * kFactor) + assert states[3].speed == pytest.approx(7.0 * kFactor) + + +def test_swerve4_odometry(s4: SwerveDrive4Kinematics): + zero = SwerveModulePosition() + odometry = SwerveDrive4Odometry(s4, Rotation2d(0), (zero, zero, zero, zero)) + odometry.resetPosition(Rotation2d(0), (zero, zero, zero, zero), Pose2d()) + + position = SwerveModulePosition(0.5) + + odometry.update( + Rotation2d(0), + ( + zero, + zero, + zero, + zero, + ), + ) + + pose = odometry.update(Rotation2d(0), (position, position, position, position)) + + print(pose) + assert pose.x == pytest.approx(0.5) + assert pose.y == pytest.approx(0.0) + assert pose.rotation().degrees() == pytest.approx(0) diff --git a/wpimath/src/test/python/test_controller.py b/wpimath/src/test/python/test_controller.py new file mode 100644 index 0000000000..806897ca42 --- /dev/null +++ b/wpimath/src/test/python/test_controller.py @@ -0,0 +1,5 @@ +import wpimath.controller + + +def test_todo(): + pass diff --git a/wpimath/src/test/python/test_estimator.py b/wpimath/src/test/python/test_estimator.py new file mode 100644 index 0000000000..cc040bb8fc --- /dev/null +++ b/wpimath/src/test/python/test_estimator.py @@ -0,0 +1,5 @@ +import wpimath.estimator + + +def test_todo(): + pass diff --git a/wpimath/src/test/python/test_interpolation.py b/wpimath/src/test/python/test_interpolation.py new file mode 100644 index 0000000000..8e8bd5547a --- /dev/null +++ b/wpimath/src/test/python/test_interpolation.py @@ -0,0 +1,50 @@ +import math + +from wpimath.geometry import Pose2d, Rotation2d +from wpimath.interpolation import ( + TimeInterpolatableFloatBuffer, + TimeInterpolatablePose2dBuffer, + TimeInterpolatableRotation2dBuffer, +) + + +def test_float(): + buffer = TimeInterpolatableFloatBuffer(10) + + buffer.addSample(0, 0) + assert buffer.sample(0) == 0 + buffer.addSample(1, 1) + assert buffer.sample(0.5) == 0.5 + assert buffer.sample(1) == 1 + buffer.addSample(3, 2) + assert buffer.sample(2) == 1.5 + + buffer.addSample(10.5, 2) + assert buffer.sample(0) == 1 + + +def test_rotation2d(): + buffer = TimeInterpolatableRotation2dBuffer(10) + + buffer.addSample(0, Rotation2d(0)) + assert buffer.sample(0) == Rotation2d(0) + buffer.addSample(1, Rotation2d(1)) + assert buffer.sample(0.5) == Rotation2d(0.5) + assert buffer.sample(1) == Rotation2d(1) + buffer.addSample(3, Rotation2d(2)) + assert buffer.sample(2) == Rotation2d(1.5) + + buffer.addSample(10.5, Rotation2d(2)) + assert buffer.sample(0) == Rotation2d(1) + + +def test_pose2d(): + buffer = TimeInterpolatablePose2dBuffer(10) + # We expect to be at (1 - 1/sqrt(2), 1/sqrt(2), 45deg) at t=0.5 + buffer.addSample(0, Pose2d(0, 0, Rotation2d.fromDegrees(90))) + buffer.addSample(1, Pose2d(1, 1, Rotation2d(0))) + sample = buffer.sample(0.5) + assert sample is not None + assert math.isclose(sample.X(), 1 - 1 / 2**0.5) + assert math.isclose(sample.Y(), 1 / 2**0.5) + assert math.isclose(sample.rotation().degrees(), 45) diff --git a/wpimath/src/test/python/test_optimization_simulated_annealing.py b/wpimath/src/test/python/test_optimization_simulated_annealing.py new file mode 100644 index 0000000000..0adf396646 --- /dev/null +++ b/wpimath/src/test/python/test_optimization_simulated_annealing.py @@ -0,0 +1,42 @@ +# Copyright (c) FIRST and other WPILib contributors. +# Open Source Software; you can modify and/or share it under the terms of +# the WPILib BSD license file in the root directory of this project. + +import math +import random + +from wpimath.optimization import SimulatedAnnealing + + +def clamp(v, minval, maxval): + return max(min(v, maxval), minval) + + +def test_double_function_optimization_heartbeat(): + def function(x): + return -(x + math.sin(x)) * math.exp(-x * x) + 1 + + step_size = 10.0 + + simulated_annealing = SimulatedAnnealing( + 2.0, lambda x: clamp(x + (random.random() - 0.5) * step_size, -3, 3), function + ) + + solution = simulated_annealing.solve(-1.0, 5000) + + assert math.isclose(solution, 0.68, abs_tol=1e-1) + + +def test_double_function_optimization_multimodal(): + def function(x): + return math.sin(x) + math.sin((10.0 / 3.0) * x) + + step_size = 10.0 + + simulated_annealing = SimulatedAnnealing( + 2.0, lambda x: clamp(x + (random.random() - 0.5) * step_size, 0, 7), function + ) + + solution = simulated_annealing.solve(-1.0, 5000) + + assert math.isclose(solution, 5.146, abs_tol=1e-1) diff --git a/wpimath/src/test/python/test_spline.py b/wpimath/src/test/python/test_spline.py new file mode 100644 index 0000000000..d262b0d67e --- /dev/null +++ b/wpimath/src/test/python/test_spline.py @@ -0,0 +1,5 @@ +import wpimath.spline + + +def test_todo(): + pass diff --git a/wpimath/src/test/python/test_system.py b/wpimath/src/test/python/test_system.py new file mode 100644 index 0000000000..2fe2976a7c --- /dev/null +++ b/wpimath/src/test/python/test_system.py @@ -0,0 +1,6 @@ +import wpimath.system +import wpimath.system.plant + + +def test_todo(): + pass diff --git a/wpimath/src/test/python/test_trajectory.py b/wpimath/src/test/python/test_trajectory.py new file mode 100644 index 0000000000..1b16ab80ba --- /dev/null +++ b/wpimath/src/test/python/test_trajectory.py @@ -0,0 +1,145 @@ +import math + +from wpimath.geometry import ( + Ellipse2d, + Pose2d, + Rectangle2d, + Rotation2d, + Transform2d, + Translation2d, +) +from wpimath.spline import CubicHermiteSpline, SplineHelper +from wpimath.trajectory import ( + Trajectory, + TrajectoryConfig, + TrajectoryGenerator, + TrajectoryParameterizer, +) +from wpimath.trajectory.constraint import ( + EllipticalRegionConstraint, + MaxVelocityConstraint, + RectangularRegionConstraint, + TrajectoryConstraint, +) + + +def getTestTrajectory(config: TrajectoryConfig) -> Trajectory: + # 2018 cross scale auto waypoints + sideStart = Pose2d.fromFeet(1.54, 23.23, Rotation2d.fromDegrees(180)) + crossScale = Pose2d.fromFeet(23.7, 6.8, Rotation2d.fromDegrees(-160)) + + config.setReversed(True) + + vector = [ + ( + sideStart + Transform2d(Translation2d.fromFeet(-13, 0), Rotation2d()) + ).translation(), + ( + sideStart + + Transform2d( + Translation2d.fromFeet(-19.5, 5.0), Rotation2d.fromDegrees(-90) + ) + ).translation(), + ] + + return TrajectoryGenerator.generateTrajectory(sideStart, vector, crossScale, config) + + +# +# EllipticalRegionConstraint tests +# + + +def test_elliptical_region_constraint(): + maxVelocity = 2 + ellipse = Ellipse2d.fromFeet(Pose2d.fromFeet(5, 2.5, math.radians(180)), 5, 2.5) + + config = TrajectoryConfig.fromFps(13, 13) + maxVelConstraint = MaxVelocityConstraint.fromFps(maxVelocity) + regionConstraint = EllipticalRegionConstraint(ellipse, maxVelConstraint) + + config.addConstraint(regionConstraint) + + trajectory = getTestTrajectory(config) + + exceededConstraintOutsideRegion = False + for point in trajectory.states(): + if ellipse.contains(point.pose.translation()): + assert abs(point.velocity_fps) < maxVelocity + 0.05 + elif abs(point.velocity_fps) >= maxVelocity + 0.05: + exceededConstraintOutsideRegion = True + + # translation = point.pose.translation() + # if translation.x_feet < 10 and translation.y_feet < 5: + # assert abs(point.velocity_fps) < maxVelocity + 0.05 + # elif abs(point.velocity_fps) >= maxVelocity + 0.05: + # exceededConstraintOutsideRegion = True + + assert exceededConstraintOutsideRegion + + +# +# RectangularRegionConstraint tests +# + + +def test_rectangular_region_constraint(): + maxVelocity = 2 + rectangle = Rectangle2d(Translation2d.fromFeet(1, 1), Translation2d.fromFeet(5, 27)) + + config = TrajectoryConfig.fromFps(13, 13) + maxVelConstraint = MaxVelocityConstraint.fromFps(maxVelocity) + regionConstraint = RectangularRegionConstraint(rectangle, maxVelConstraint) + + config.addConstraint(regionConstraint) + + trajectory = getTestTrajectory(config) + + exceededConstraintOutsideRegion = False + for point in trajectory.states(): + if rectangle.contains(point.pose.translation()): + assert abs(point.velocity_fps) < maxVelocity + 0.05 + elif abs(point.velocity_fps) >= maxVelocity + 0.05: + exceededConstraintOutsideRegion = True + + assert exceededConstraintOutsideRegion + + +# +# TrajectoryConstraint +# + + +def test_trajectory_constraint_min_max(): + min_max = TrajectoryConstraint.MinMax() + _, _ = min_max + + min_max = TrajectoryConstraint.MinMax(minAcceleration=0, maxAcceleration=1) + assert ( + repr(min_max) + == "TrajectoryConstraint.MinMax(minAcceleration=0.0, maxAcceleration=1.0)" + ) + + +# +# TrajectoryParameterizer +# + + +def test_trajectory_parameterizer(): + start = Pose2d(1, 1, 0) + end = Pose2d(2, 2, math.pi / 2) + + # generate the spline from start and end poses + vec1, vec2 = SplineHelper.cubicControlVectorsFromWaypoints(start, [], end) + spline = CubicHermiteSpline(vec1.x, vec2.x, vec1.y, vec2.y) + + # sample the pose and curvature along the spline + points: list[tuple[Pose2d, float]] = [] + for i in range(100): + points.append(spline.getPoint(i / 100)) + + trajectory = TrajectoryParameterizer.timeParameterizeTrajectory( + points, [], 0, 0, 4, 3, False + ) + assert trajectory is not None diff --git a/wpimath/src/test/python/test_trajectory_exponential_profile.py b/wpimath/src/test/python/test_trajectory_exponential_profile.py new file mode 100644 index 0000000000..0bb90cf15b --- /dev/null +++ b/wpimath/src/test/python/test_trajectory_exponential_profile.py @@ -0,0 +1,319 @@ +# Copyright (c) FIRST and other WPILib contributors. +# Open Source Software; you can modify and/or share it under the terms of +# the WPILib BSD license file in the root directory of this project. + +import pytest + +from wpimath.controller import SimpleMotorFeedforwardMeters +from wpimath.trajectory import ExponentialProfileMeterVolts + +kDt = 0.01 +feedforward = SimpleMotorFeedforwardMeters(0, 2.5629, 0.43277, kDt) +constraints = ExponentialProfileMeterVolts.Constraints.fromCharacteristics( + 12, 2.5629, 0.43277 +) + + +def assert_near(val1, val2, eps): + assert ( + abs(val1 - val2) <= eps + ), f"Difference between {val1} and {val2} is greater than {eps}" + + +def assert_near_state(val1, val2, eps): + assert_near(val1.position, val2.position, eps) + assert_near(val1.position, val2.position, eps) + + +def check_dynamics( + profile: ExponentialProfileMeterVolts, + current: ExponentialProfileMeterVolts.State, + goal: ExponentialProfileMeterVolts.State, +): + next_state = profile.calculate(kDt, current, goal) + + signal = feedforward.calculate(current.velocity, next_state.velocity) + + assert abs(signal) < constraints.maxInput + 1e-9 + + return next_state + + +@pytest.fixture +def profile(): + return ExponentialProfileMeterVolts(constraints) + + +def test_reaches_goal(profile): + goal = ExponentialProfileMeterVolts.State(10, 0) + state = ExponentialProfileMeterVolts.State(0, 0) + + for _ in range(450): + state = check_dynamics(profile, state, goal) + + assert state == goal + + +def test_pos_continuous_under_vel_change(profile): + goal = ExponentialProfileMeterVolts.State(10, 0) + state = ExponentialProfileMeterVolts.State(0, 0) + + for i in range(300): + if i == 150: + profile = ExponentialProfileMeterVolts( + ExponentialProfileMeterVolts.Constraints.fromStateSpace( + 9, constraints.A, constraints.B + ) + ) + + state = check_dynamics(profile, state, goal) + + assert state == goal + + +def test_pos_continuous_under_vel_change_backward(profile): + goal = ExponentialProfileMeterVolts.State(-10, 0) + state = ExponentialProfileMeterVolts.State(0, 0) + + for i in range(300): + if i == 150: + profile = ExponentialProfileMeterVolts( + ExponentialProfileMeterVolts.Constraints.fromStateSpace( + 9, constraints.A, constraints.B + ) + ) + + state = check_dynamics(profile, state, goal) + + assert state == goal + + +def test_backwards(profile): + goal = ExponentialProfileMeterVolts.State(-10, 0) + state = ExponentialProfileMeterVolts.State(0, 0) + + for _ in range(400): + state = check_dynamics(profile, state, goal) + + assert state == goal + + +def test_switch_goal_in_middle(profile): + goal = ExponentialProfileMeterVolts.State(-10, 0) + state = ExponentialProfileMeterVolts.State(0, 0) + + for _ in range(50): + state = check_dynamics(profile, state, goal) + + assert state != goal + + goal = ExponentialProfileMeterVolts.State(0.0, 0.0) + for _ in range(100): + state = check_dynamics(profile, state, goal) + + assert state == goal + + +def test_top_speed(profile): + goal = ExponentialProfileMeterVolts.State(40, 0) + state = ExponentialProfileMeterVolts.State(0, 0) + + max_speed = 0 + for _ in range(900): + state = check_dynamics(profile, state, goal) + max_speed = max(max_speed, state.velocity) + + assert_near(constraints.maxVelocity(), max_speed, 10e-5) + assert state == goal + + +def test_top_speed_backward(profile): + goal = ExponentialProfileMeterVolts.State(-40, 0) + state = ExponentialProfileMeterVolts.State(0, 0) + + max_speed = 0 + for _ in range(900): + state = check_dynamics(profile, state, goal) + max_speed = min(max_speed, state.velocity) + + assert_near(-constraints.maxVelocity(), max_speed, 10e-5) + assert state == goal + + +def test_large_initial_velocity(profile): + goal = ExponentialProfileMeterVolts.State(40, 0) + state = ExponentialProfileMeterVolts.State(0, 8) + + for _ in range(900): + state = check_dynamics(profile, state, goal) + + assert state == goal + + +def test_large_negative_initial_velocity(profile): + goal = ExponentialProfileMeterVolts.State(-40, 0) + state = ExponentialProfileMeterVolts.State(0, -8) + + for _ in range(900): + state = check_dynamics(profile, state, goal) + + assert state == goal + + +class ETestCase: + def __init__(self, initial, goal, inflection_point): + self.initial = initial + self.goal = goal + self.inflection_point = inflection_point + + +def test_heuristic(profile): + test_cases = [ + ETestCase( + ExponentialProfileMeterVolts.State(0.0, -4), + ExponentialProfileMeterVolts.State(0.75, -4), + ExponentialProfileMeterVolts.State(1.3758, 4.4304), + ), + ETestCase( + ExponentialProfileMeterVolts.State(0.0, -4), + ExponentialProfileMeterVolts.State(1.4103, 4), + ExponentialProfileMeterVolts.State(1.3758, 4.4304), + ), + ETestCase( + ExponentialProfileMeterVolts.State(0.6603, 4), + ExponentialProfileMeterVolts.State(0.75, -4), + ExponentialProfileMeterVolts.State(1.3758, 4.4304), + ), + ETestCase( + ExponentialProfileMeterVolts.State(0.6603, 4), + ExponentialProfileMeterVolts.State(1.4103, 4), + ExponentialProfileMeterVolts.State(1.3758, 4.4304), + ), + ETestCase( + ExponentialProfileMeterVolts.State(0.0, -4), + ExponentialProfileMeterVolts.State(0.5, -2), + ExponentialProfileMeterVolts.State(0.4367, 3.7217), + ), + ETestCase( + ExponentialProfileMeterVolts.State(0.0, -4), + ExponentialProfileMeterVolts.State(0.546, 2), + ExponentialProfileMeterVolts.State(0.4367, 3.7217), + ), + ETestCase( + ExponentialProfileMeterVolts.State(0.6603, 4), + ExponentialProfileMeterVolts.State(0.5, -2), + ExponentialProfileMeterVolts.State(0.5560, -2.9616), + ), + ETestCase( + ExponentialProfileMeterVolts.State(0.6603, 4), + ExponentialProfileMeterVolts.State(0.546, 2), + ExponentialProfileMeterVolts.State(0.5560, -2.9616), + ), + ETestCase( + ExponentialProfileMeterVolts.State(0.0, -4), + ExponentialProfileMeterVolts.State(-0.75, -4), + ExponentialProfileMeterVolts.State(-0.7156, -4.4304), + ), + ETestCase( + ExponentialProfileMeterVolts.State(0.0, -4), + ExponentialProfileMeterVolts.State(-0.0897, 4), + ExponentialProfileMeterVolts.State(-0.7156, -4.4304), + ), + ETestCase( + ExponentialProfileMeterVolts.State(0.6603, 4), + ExponentialProfileMeterVolts.State(-0.75, -4), + ExponentialProfileMeterVolts.State(-0.7156, -4.4304), + ), + ETestCase( + ExponentialProfileMeterVolts.State(0.6603, 4), + ExponentialProfileMeterVolts.State(-0.0897, 4), + ExponentialProfileMeterVolts.State(-0.7156, -4.4304), + ), + ETestCase( + ExponentialProfileMeterVolts.State(0.0, -4), + ExponentialProfileMeterVolts.State(-0.5, -4.5), + ExponentialProfileMeterVolts.State(1.095, 4.314), + ), + ETestCase( + ExponentialProfileMeterVolts.State(0.0, -4), + ExponentialProfileMeterVolts.State(1.0795, 4.5), + ExponentialProfileMeterVolts.State(-0.5122, -4.351), + ), + ETestCase( + ExponentialProfileMeterVolts.State(0.6603, 4), + ExponentialProfileMeterVolts.State(-0.5, -4.5), + ExponentialProfileMeterVolts.State(1.095, 4.314), + ), + ETestCase( + ExponentialProfileMeterVolts.State(0.6603, 4), + ExponentialProfileMeterVolts.State(1.0795, 4.5), + ExponentialProfileMeterVolts.State(-0.5122, -4.351), + ), + ETestCase( + ExponentialProfileMeterVolts.State(0.0, -8), + ExponentialProfileMeterVolts.State(0, 0), + ExponentialProfileMeterVolts.State(-0.1384, 3.342), + ), + ETestCase( + ExponentialProfileMeterVolts.State(0.0, -8), + ExponentialProfileMeterVolts.State(-1, 0), + ExponentialProfileMeterVolts.State(-0.562, -6.792), + ), + ETestCase( + ExponentialProfileMeterVolts.State(0.0, 8), + ExponentialProfileMeterVolts.State(1, 0), + ExponentialProfileMeterVolts.State(0.562, 6.792), + ), + ETestCase( + ExponentialProfileMeterVolts.State(0.0, 8), + ExponentialProfileMeterVolts.State(-1, 0), + ExponentialProfileMeterVolts.State(-0.785, -4.346), + ), + ] + + for test_case in test_cases: + state = profile.calculateInflectionPoint(test_case.initial, test_case.goal) + assert_near_state(test_case.inflection_point, state, 1e-3) + + +def test_timing_to_current(profile): + goal = ExponentialProfileMeterVolts.State(2, 0) + state = ExponentialProfileMeterVolts.State(0, 0) + + for _ in range(400): + state = check_dynamics(profile, state, goal) + assert_near(profile.timeLeftUntil(state, state), 0, 2e-2) + + +def test_timing_to_goal(profile): + goal = ExponentialProfileMeterVolts.State(2, 0) + state = ExponentialProfileMeterVolts.State(0, 0) + + predicted_time_left = profile.timeLeftUntil(state, goal) + reached_goal = False + for _ in range(400): + state = check_dynamics(profile, state, goal) + + if not reached_goal and state == goal: + # Expected value using for loop index is just an approximation since + # the time left in the profile doesn't increase linearly at the + # endpoints + assert_near(predicted_time_left, _ / 100.0, 0.25) + reached_goal = True + + +def test_timing_to_negative_goal(profile): + goal = ExponentialProfileMeterVolts.State(-2, 0) + state = ExponentialProfileMeterVolts.State(0, 0) + + predicted_time_left = profile.timeLeftUntil(state, goal) + reached_goal = False + for _ in range(400): + state = check_dynamics(profile, state, goal) + + if not reached_goal and state == goal: + # Expected value using for loop index is just an approximation since + # the time left in the profile doesn't increase linearly at the + # endpoints + assert_near(predicted_time_left, _ / 100.0, 0.25) + reached_goal = True diff --git a/wpimath/src/test/python/test_trapezoid_profile.py b/wpimath/src/test/python/test_trapezoid_profile.py new file mode 100644 index 0000000000..e28c55c95b --- /dev/null +++ b/wpimath/src/test/python/test_trapezoid_profile.py @@ -0,0 +1,24 @@ +import pytest + +from wpimath import trajectory + +trapezoid_profile_types = [ + trajectory.TrapezoidProfile, + trajectory.TrapezoidProfileRadians, +] + + +@pytest.mark.parametrize("TrapezoidProfile", trapezoid_profile_types) +def test_constraints_repr(TrapezoidProfile): + expected_qualname = f"{TrapezoidProfile.__name__}.Constraints" + constraints = TrapezoidProfile.Constraints() + + assert repr(constraints).startswith(f"{expected_qualname}(maxVelocity=0.") + + +@pytest.mark.parametrize("TrapezoidProfile", trapezoid_profile_types) +def test_state_repr(TrapezoidProfile): + expected_qualname = f"{TrapezoidProfile.__name__}.State" + constraints = TrapezoidProfile.State() + + assert repr(constraints).startswith(f"{expected_qualname}(position=0.") diff --git a/wpimath/src/test/python/test_units.py b/wpimath/src/test/python/test_units.py new file mode 100644 index 0000000000..2fe73f2938 --- /dev/null +++ b/wpimath/src/test/python/test_units.py @@ -0,0 +1,71 @@ +import pytest +import wpimath_test + + +def test_units_attributes(): + assert wpimath_test.SomeClass.s_constant == 2 + assert wpimath_test.SomeClass.ms_constant1 == 20 # the unit is ms, not seconds + assert wpimath_test.SomeClass.ms_constant2 == 0.050 + assert wpimath_test.SomeClass.ms_constant3 == 200 + + +def test_units_check_default_by_name1(): + sc = wpimath_test.SomeClass() + + assert sc.checkDefaultByName1(0.020) == True + assert sc.checkDefaultByName1() == True + + with pytest.raises(RuntimeError): + sc.checkDefaultByName1(100) + + +def test_units_check_default_by_name2(): + sc = wpimath_test.SomeClass() + + assert sc.checkDefaultByName2(0.050) == True + assert sc.checkDefaultByName2() == True + + with pytest.raises(RuntimeError): + sc.checkDefaultByName2(100) + + +def test_units_check_default_by_num1(): + sc = wpimath_test.SomeClass() + + assert sc.checkDefaultByNum1(0.050) == True + assert sc.checkDefaultByNum1() == True + + with pytest.raises(RuntimeError): + sc.checkDefaultByNum1(100) + + +def test_units_check_default_by_num2(): + sc = wpimath_test.SomeClass() + + assert sc.checkDefaultByNum2(0.050) == True + assert sc.checkDefaultByNum2() == True + + with pytest.raises(RuntimeError): + sc.checkDefaultByNum2(100) + + +def test_units_ft(): + assert wpimath_test.SomeClass.five_ft == 5.0 + + +def test_units_ft2m(): + sc = wpimath_test.SomeClass() + + assert sc.ft2m(3) == 0.9144 + + +def test_units_ms2s(): + sc = wpimath_test.SomeClass() + + assert sc.ms2s(20) == 0.020 + + +def test_units_s2ms(): + sc = wpimath_test.SomeClass() + + assert sc.s2ms(0.2) == 200.0 diff --git a/wpimath/src/test/python/trajectory/test_trapezoidal_profile.py b/wpimath/src/test/python/trajectory/test_trapezoidal_profile.py new file mode 100644 index 0000000000..c779611356 --- /dev/null +++ b/wpimath/src/test/python/trajectory/test_trapezoidal_profile.py @@ -0,0 +1,176 @@ +# Copyright (c) FIRST and other WPILib contributors. +# Open Source Software; you can modify and/or share it under the terms of +# the WPILib BSD license file in the root directory of this project. + + +import math + +from wpimath.trajectory import TrapezoidProfile + +kDt = 0.01 # 10 ms + + +def test_reaches_goal(): + constraints = TrapezoidProfile.Constraints(1.75, 0.75) + goal = TrapezoidProfile.State(3.0, 0.0) + state = TrapezoidProfile.State() + + profile = TrapezoidProfile(constraints) + for _ in range(450): + state = profile.calculate(kDt, state, goal) + assert state == goal + + +def test_pos_continuous_under_vel_change(): + constraints = TrapezoidProfile.Constraints(1.75, 0.75) + goal = TrapezoidProfile.State(12.0, 0.0) + profile = TrapezoidProfile(constraints) + state = profile.calculate(kDt, TrapezoidProfile.State(), goal) + + last_pos = state.position + for i in range(1600): + if i == 400: + constraints = TrapezoidProfile.Constraints(0.75, 0.75) + profile = TrapezoidProfile(constraints) + + state = profile.calculate(kDt, state, goal) + estimated_vel = (state.position - last_pos) / kDt + + if i >= 400: + if estimated_vel <= constraints.maxVelocity: + assert estimated_vel <= constraints.maxVelocity + else: + assert math.isclose( + estimated_vel, constraints.maxVelocity, abs_tol=1e-4 + ) + assert state.velocity <= constraints.maxVelocity + + last_pos = state.position + + assert state == goal + + +def test_backwards(): + constraints = TrapezoidProfile.Constraints(0.75, 0.75) + goal = TrapezoidProfile.State(-2.0, 0.0) + state = TrapezoidProfile.State() + profile = TrapezoidProfile(constraints) + + for _ in range(400): + state = profile.calculate(kDt, state, goal) + assert state == goal + + +def test_switch_goal_in_middle(): + constraints = TrapezoidProfile.Constraints(0.75, 0.75) + goal = TrapezoidProfile.State(-2.0, 0.0) + state = TrapezoidProfile.State() + profile = TrapezoidProfile(constraints) + + for _ in range(200): + state = profile.calculate(kDt, state, goal) + assert state != goal + + goal = TrapezoidProfile.State(0.0, 0.0) + profile = TrapezoidProfile(constraints) + for _ in range(550): + state = profile.calculate(kDt, state, goal) + assert state == goal + + +def test_top_speed(): + constraints = TrapezoidProfile.Constraints(0.75, 0.75) + goal = TrapezoidProfile.State(4.0, 0.0) + state = TrapezoidProfile.State() + profile = TrapezoidProfile(constraints) + + for _ in range(200): + state = profile.calculate(kDt, state, goal) + assert math.isclose(constraints.maxVelocity, state.velocity, abs_tol=1e-4) + + profile = TrapezoidProfile(constraints) + for _ in range(2000): + state = profile.calculate(kDt, state, goal) + assert state == goal + + +def test_timing_to_current(): + constraints = TrapezoidProfile.Constraints(0.75, 0.75) + goal = TrapezoidProfile.State(2.0, 0.0) + state = TrapezoidProfile.State() + profile = TrapezoidProfile(constraints) + + for _ in range(400): + state = profile.calculate(kDt, state, goal) + assert math.isclose(profile.timeLeftUntil(state.position), 0.0, abs_tol=0.02) + + +def test_timing_to_goal(): + constraints = TrapezoidProfile.Constraints(0.75, 0.75) + goal = TrapezoidProfile.State(2.0, 0.0) + profile = TrapezoidProfile(constraints) + + state = profile.calculate(kDt, goal, TrapezoidProfile.State()) + predicted_time_left = profile.timeLeftUntil(goal.position) + + reached_goal = False + for i in range(400): + state = profile.calculate(kDt, state, goal) + if not reached_goal and state == goal: + assert math.isclose(predicted_time_left, i * kDt, abs_tol=0.25) + reached_goal = True + + +def test_timing_before_goal(): + constraints = TrapezoidProfile.Constraints(0.75, 0.75) + goal = TrapezoidProfile.State(2.0, 0.0) + profile = TrapezoidProfile(constraints) + + state = profile.calculate(kDt, goal, TrapezoidProfile.State()) + predicted_time_left = profile.timeLeftUntil(1.0) + + reached_goal = False + for i in range(400): + state = profile.calculate(kDt, state, goal) + if not reached_goal and abs(state.velocity - 1.0) < 1e-4: + assert math.isclose(predicted_time_left, i * kDt, abs_tol=0.02) + reached_goal = True + + +def test_timing_to_negative_goal(): + constraints = TrapezoidProfile.Constraints(0.75, 0.75) + goal = TrapezoidProfile.State(-2.0, 0.0) + profile = TrapezoidProfile(constraints) + + state = profile.calculate(kDt, goal, TrapezoidProfile.State()) + predicted_time_left = profile.timeLeftUntil(goal.position) + + reached_goal = False + for i in range(400): + state = profile.calculate(kDt, state, goal) + if not reached_goal and state == goal: + assert math.isclose(predicted_time_left, i * kDt, abs_tol=0.25) + reached_goal = True + + +def test_timing_before_negative_goal(): + constraints = TrapezoidProfile.Constraints(0.75, 0.75) + goal = TrapezoidProfile.State(-2.0, 0.0) + profile = TrapezoidProfile(constraints) + + state = profile.calculate(kDt, goal, TrapezoidProfile.State()) + predicted_time_left = profile.timeLeftUntil(-1.0) + + reached_goal = False + for i in range(400): + state = profile.calculate(kDt, state, goal) + if not reached_goal and abs(state.velocity + 1.0) < 1e-4: + assert math.isclose(predicted_time_left, i * kDt, abs_tol=0.02) + reached_goal = True + + +def test_initialization_of_current_state(): + constraints = TrapezoidProfile.Constraints(1.0, 1.0) + profile = TrapezoidProfile(constraints) + assert math.isclose(profile.timeLeftUntil(0.0), 0.0, abs_tol=1e-10) + assert math.isclose(profile.totalTime(), 0.0, abs_tol=1e-10) diff --git a/wpinet/src/test/python/run_tests.py b/wpinet/src/test/python/run_tests.py deleted file mode 100755 index 2ffc6595d2..0000000000 --- a/wpinet/src/test/python/run_tests.py +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env python3 - -import os -from os.path import abspath, dirname -import sys -import subprocess - -if __name__ == "__main__": - root = abspath(dirname(__file__)) - os.chdir(root) - - subprocess.check_call([sys.executable, "-m", "pytest"]) diff --git a/wpiutil/src/test/python/run_tests.py b/wpiutil/src/test/python/run_tests.py deleted file mode 100755 index 80c5f49df8..0000000000 --- a/wpiutil/src/test/python/run_tests.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python3 - -import os -from os.path import abspath, dirname -import sys -import subprocess - -if __name__ == "__main__": - root = abspath(dirname(__file__)) - os.chdir(root) - - subprocess.check_call( - [ - sys.executable, - "-m", - "pip", - "--disable-pip-version-check", - "install", - "-v", - "--force-reinstall", - "--no-build-isolation", - "./cpp", - ], - ) - - subprocess.check_call([sys.executable, "-m", "pytest"]) diff --git a/xrpVendordep/src/main/python/README.md b/xrpVendordep/src/main/python/README.md new file mode 100644 index 0000000000..b34966d307 --- /dev/null +++ b/xrpVendordep/src/main/python/README.md @@ -0,0 +1,4 @@ +robotpy-xrp +============ + +RobotPy support for the WPILib XRP vendor library. diff --git a/xrpVendordep/src/main/python/native-pyproject.toml b/xrpVendordep/src/main/python/native-pyproject.toml new file mode 100644 index 0000000000..eff30dae59 --- /dev/null +++ b/xrpVendordep/src/main/python/native-pyproject.toml @@ -0,0 +1,39 @@ +[build-system] +build-backend = "hatchling.build" +requires = [ + "hatchling", + "hatch-nativelib~=0.2.0", + "hatch-robotpy~=0.2.1", + "robotpy-native-wpilib==2027.0.0a2", +] + +[project] +name = "robotpy-native-xrp" +version = "2027.0.0a2" +description = "WPILib XRP vendor library" +license = "BSD-3-Clause" + +dependencies = [ + "robotpy-native-wpilib==2027.0.0a2", +] + +[tool.hatch.build.targets.wheel] +packages = ["src/native"] + +[[tool.hatch.build.hooks.robotpy.maven_lib_download]] +artifact_id = "xrpVendordep-cpp" +group_id = "edu.wpi.first.xrpVendordep" +repo_url = "https://frcmaven.wpi.edu/artifactory/release-2027" +version = "2027.0.0-alpha-2" + +extract_to = "src/native/xrp" +libs = ["xrpVendordep"] + +[[tool.hatch.build.hooks.nativelib.pcfile]] +pcfile = "src/native/xrp/robotpy-native-xrp.pc" +name = "xrp" + +includedir = "src/native/xrp/include" +libdir = "src/native/xrp/lib" +shared_libraries = ["xrpVendordep"] +requires = ["robotpy-native-wpilib"] diff --git a/xrpVendordep/src/main/python/pyproject.toml b/xrpVendordep/src/main/python/pyproject.toml new file mode 100644 index 0000000000..cbc3e18a29 --- /dev/null +++ b/xrpVendordep/src/main/python/pyproject.toml @@ -0,0 +1,66 @@ +[build-system] +build-backend = "hatchling.build" +requires = [ + "semiwrap~=0.1.7", + "hatch-meson~=0.1.0b2", + "hatch-robotpy~=0.2.1", + "hatchling", + "robotpy-native-xrp==2027.0.0a2", + "wpilib==2027.0.0a2", +] + + +[project] +name = "robotpy-xrp" +version = "2027.0.0a2" +description = "Binary wrapper for WPILib XRP Vendor library" +authors = [ + {name = "RobotPy Development Team", email = "robotpy@googlegroups.com"}, +] +license = "BSD-3-Clause" +dependencies = [ + "robotpy-native-xrp==2027.0.0a2", + "wpilib==2027.0.0a2" +] + +[project.entry-points.robotpysimext] +xrp = "xrp.extension" + + +[tool.hatch.build.hooks.robotpy] +version_file = "xrp/version.py" + +[[tool.hatch.build.hooks.robotpy.maven_lib_download]] +artifact_id = "halsim_xrp" +group_id = "edu.wpi.first.halsim" +repo_url = "https://frcmaven.wpi.edu/artifactory/release-2027" +version = "2027.0.0-alpha-2" +use_headers = false + +extract_to = "xrp/extension" +libs = ["halsim_xrp"] + +[tool.hatch.build.hooks.semiwrap] + +[tool.hatch.build.hooks.meson] + +[tool.hatch.build.targets.wheel] +packages = ["xrp"] + + +[tool.semiwrap] +update_init = ["xrp"] + +[tool.semiwrap.extension_modules."xrp._xrp"] +name = "xrp" +wraps = ["robotpy-native-xrp"] +depends = ["wpilib", "wpimath_geometry"] + +[tool.semiwrap.extension_modules."xrp._xrp".headers] +# frc/xrp +XRPGyro = "frc/xrp/XRPGyro.h" +XRPMotor = "frc/xrp/XRPMotor.h" +XRPOnBoardIO = "frc/xrp/XRPOnBoardIO.h" +XRPRangefinder = "frc/xrp/XRPRangefinder.h" +XRPReflectanceSensor = "frc/xrp/XRPReflectanceSensor.h" +XRPServo = "frc/xrp/XRPServo.h" diff --git a/xrpVendordep/src/main/python/semiwrap/XRPGyro.yml b/xrpVendordep/src/main/python/semiwrap/XRPGyro.yml new file mode 100644 index 0000000000..e5726f2529 --- /dev/null +++ b/xrpVendordep/src/main/python/semiwrap/XRPGyro.yml @@ -0,0 +1,14 @@ +classes: + frc::XRPGyro: + methods: + XRPGyro: + GetAngle: + GetRate: + GetRateX: + GetRateY: + GetRateZ: + GetAngleX: + GetAngleY: + GetAngleZ: + Reset: + GetRotation2d: diff --git a/xrpVendordep/src/main/python/semiwrap/XRPMotor.yml b/xrpVendordep/src/main/python/semiwrap/XRPMotor.yml new file mode 100644 index 0000000000..a534dc9996 --- /dev/null +++ b/xrpVendordep/src/main/python/semiwrap/XRPMotor.yml @@ -0,0 +1,11 @@ +classes: + frc::XRPMotor: + methods: + XRPMotor: + Set: + Get: + SetInverted: + GetInverted: + Disable: + StopMotor: + GetDescription: diff --git a/xrpVendordep/src/main/python/semiwrap/XRPOnBoardIO.yml b/xrpVendordep/src/main/python/semiwrap/XRPOnBoardIO.yml new file mode 100644 index 0000000000..3ea7645c37 --- /dev/null +++ b/xrpVendordep/src/main/python/semiwrap/XRPOnBoardIO.yml @@ -0,0 +1,10 @@ +classes: + frc::XRPOnBoardIO: + attributes: + kMessageInterval: + m_nextMessageTime: + methods: + XRPOnBoardIO: + GetUserButtonPressed: + SetLed: + GetLed: diff --git a/xrpVendordep/src/main/python/semiwrap/XRPRangefinder.yml b/xrpVendordep/src/main/python/semiwrap/XRPRangefinder.yml new file mode 100644 index 0000000000..1a7a05365f --- /dev/null +++ b/xrpVendordep/src/main/python/semiwrap/XRPRangefinder.yml @@ -0,0 +1,4 @@ +classes: + frc::XRPRangefinder: + methods: + GetDistance: diff --git a/xrpVendordep/src/main/python/semiwrap/XRPReflectanceSensor.yml b/xrpVendordep/src/main/python/semiwrap/XRPReflectanceSensor.yml new file mode 100644 index 0000000000..5052c03650 --- /dev/null +++ b/xrpVendordep/src/main/python/semiwrap/XRPReflectanceSensor.yml @@ -0,0 +1,5 @@ +classes: + frc::XRPReflectanceSensor: + methods: + GetLeftReflectanceValue: + GetRightReflectanceValue: diff --git a/xrpVendordep/src/main/python/semiwrap/XRPServo.yml b/xrpVendordep/src/main/python/semiwrap/XRPServo.yml new file mode 100644 index 0000000000..5fc8e1bf62 --- /dev/null +++ b/xrpVendordep/src/main/python/semiwrap/XRPServo.yml @@ -0,0 +1,8 @@ +classes: + frc::XRPServo: + methods: + XRPServo: + SetAngle: + GetAngle: + SetPosition: + GetPosition: diff --git a/xrpVendordep/src/main/python/xrp/__init__.py b/xrpVendordep/src/main/python/xrp/__init__.py new file mode 100644 index 0000000000..2f14efc2ed --- /dev/null +++ b/xrpVendordep/src/main/python/xrp/__init__.py @@ -0,0 +1,22 @@ +from . import _init__xrp + +# autogenerated by 'semiwrap create-imports xrp xrp._xrp' +from ._xrp import ( + XRPGyro, + XRPMotor, + XRPOnBoardIO, + XRPRangefinder, + XRPReflectanceSensor, + XRPServo, +) + +__all__ = [ + "XRPGyro", + "XRPMotor", + "XRPOnBoardIO", + "XRPRangefinder", + "XRPReflectanceSensor", + "XRPServo", +] + +del _init__xrp diff --git a/xrpVendordep/src/main/python/xrp/extension/__init__.py b/xrpVendordep/src/main/python/xrp/extension/__init__.py new file mode 100644 index 0000000000..fe11109567 --- /dev/null +++ b/xrpVendordep/src/main/python/xrp/extension/__init__.py @@ -0,0 +1,3 @@ +from .main import loadExtension + +__all__ = ["loadExtension"] diff --git a/xrpVendordep/src/main/python/xrp/extension/main.py b/xrpVendordep/src/main/python/xrp/extension/main.py new file mode 100644 index 0000000000..8e824be493 --- /dev/null +++ b/xrpVendordep/src/main/python/xrp/extension/main.py @@ -0,0 +1,23 @@ +import logging +import os +from os.path import abspath, dirname, join + +logger = logging.getLogger("xrp.extension") + + +def loadExtension(): + try: + import hal + except ImportError as e: + # really, should never happen... + raise ImportError("you must install robotpy-hal!") from e + + from ..version import version + + logger.info("WPILib XRP client %s", version) + + root = join(abspath(dirname(__file__)), "lib") + ext = join(root, os.listdir(root)[0]) + retval = hal.loadOneExtension(ext) + if retval != 0: + logger.warn("loading extension may have failed (error=%d)", retval) diff --git a/xrpVendordep/src/main/python/xrp/py.typed b/xrpVendordep/src/main/python/xrp/py.typed new file mode 100644 index 0000000000..e69de29bb2 diff --git a/xrpVendordep/src/main/python/xrp/src/main.cpp b/xrpVendordep/src/main/python/xrp/src/main.cpp new file mode 100644 index 0000000000..002441a180 --- /dev/null +++ b/xrpVendordep/src/main/python/xrp/src/main.cpp @@ -0,0 +1,6 @@ + +#include + +SEMIWRAP_PYBIND11_MODULE(m) { + initWrapper(m); +} \ No newline at end of file diff --git a/xrpVendordep/src/main/python/xrp/xrp.pc b/xrpVendordep/src/main/python/xrp/xrp.pc new file mode 100644 index 0000000000..a1896bdc49 --- /dev/null +++ b/xrpVendordep/src/main/python/xrp/xrp.pc @@ -0,0 +1,9 @@ +# automatically generated by semiwrap.cmd_gen_pkgconf +prefix=${pcfiledir} +pkgconf_pypi_initpy=xrp._init__xrp + +Name: xrp +Description: semiwrap pybind11 module +Version: +Cflags: -I${prefix} +Requires: robotpy-native-xrp wpilib wpimath_geometry diff --git a/xrpVendordep/src/test/python/test_xrp.py b/xrpVendordep/src/test/python/test_xrp.py new file mode 100644 index 0000000000..f1013a1d43 --- /dev/null +++ b/xrpVendordep/src/test/python/test_xrp.py @@ -0,0 +1,5 @@ +import xrp + + +def test_xrp(): + pass