Skip to content

Commit

Permalink
Added Pos2D
Browse files Browse the repository at this point in the history
  • Loading branch information
AtomicGamer9523 committed Dec 18, 2023
1 parent 2d9d702 commit 29fd5b7
Show file tree
Hide file tree
Showing 31 changed files with 1,965 additions and 317 deletions.
6 changes: 5 additions & 1 deletion arc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ license.workspace = true
path = "lib.rs"
crate-type = ["cdylib", "rlib"]

[dependencies.hardware]
[dependencies.arc_robot_hardware]
package = "arc-robot-hardware"
path = "../libs/robot/hardware"

[dependencies.libtrig]
package = "libtrig"
path = "../libs/helper/libtrig"

[dependencies.pyo3]
#!!! Important - DO NOT ENABLE extension-module FEATURE HERE!!!
version = "0.20.0"
Expand Down
22 changes: 18 additions & 4 deletions arc/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,20 @@ Additionally, it is easy to extend, so that you can add your own functionality t
"""

from .hardware.gamepad import Gamepad as _Gamepad
from typing import Callable as _Callable
import typing as _typing

type RunResult = bool | int | str | None
RunResult = _typing.Union[bool, int, str, None]
"""
This type is used to indicate whether or not an `Op` completed successfully.
If the `Op` completed successfully, then it should return `OK` (or `None`).
If the `Op` did not complete successfully, then it should return an object that
provides information about why it did not complete successfully:
- If it's a `bool`, then it should be False.
- If it's an `int`, then it should be the error code.
- If it's a `str`, then it should be the error message.
"""

class Op(object):
"""Represents an operation that can be run on the robot."""
Expand All @@ -25,11 +36,11 @@ class Op(object):
"""Whether or not the operation is running."""
...

def Auto(func: _Callable[[Op], RunResult]) -> _Callable[[Op], RunResult]:
def Auto(name: str) -> _typing.Callable[[_typing.Callable[[Op], RunResult]], _typing.Callable[[Op], RunResult]]:
"""Decorator for an autonomous operation."""
...

def Teleop(func: _Callable[[Op], RunResult]) -> _Callable[[Op], RunResult]:
def Teleop(name: str) -> _typing.Callable[[_typing.Callable[[Op], RunResult]], _typing.Callable[[Op], RunResult]]:
"""Decorator for a teleop operation."""
...

Expand All @@ -38,3 +49,6 @@ def sleep(seconds: float) -> None:
...

OK: RunResult = True
"""
A built-in `RunResult` that indicates that the `Op` completed successfully.
"""
129 changes: 83 additions & 46 deletions arc/__init__.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
//! The rust bindings for the root of the arc library
//!
//! This module contains the [`Op`] struct, which is the main struct that
//! is used to access the robot's hardware.
//!
//! [`Op`]: struct.Op.html
use crate::threadsafe::{self, ThreadSafe};
use pyo3::prelude::*;

/// Hardware submodule
#[path = "hardware/__init__.rs"]
pub mod _hardware;
pub mod hardware;

#[doc(hidden)]
fn make_err(e: &'static str) -> PyErr {
PyErr::new::<pyo3::exceptions::PyIOError, _>(e)
}
/// Math submodule
#[path = "math/__init__.rs"]
pub mod math;

/// The struct that actually contains the necessary data for the op mode
/// to run.
Expand All @@ -18,7 +24,8 @@ fn make_err(e: &'static str) -> PyErr {
#[derive(Debug)]
pub struct OpHolder {
running: threadsafe::ThreadSafeBool,
gamepad: _hardware::gamepad::Gamepad,
gamepad: hardware::gamepad::Gamepad,
start_time: std::time::Instant,
}

impl OpHolder {
Expand All @@ -34,19 +41,26 @@ impl OpHolder {
/// Returns a reference to the gamepad
///
/// This call aquires a lock on the data
pub fn gamepad(&self) -> &_hardware::gamepad::Gamepad {
pub fn gamepad(&self) -> &hardware::gamepad::Gamepad {
&self.gamepad
}
/// Stops the op mode
///
/// DO NOT CALL THIS FROM THE OP MODE THREAD
pub fn stop(&self) {
self.running.get_mut().unwrap().set(false);
/// DO NOT CALL THIS FROM THE PYTHON THREAD
pub fn stop(&self) -> core::result::Result<(), &'static str> {
self.running.get_mut()?.set(false);
Ok(())
}
/// Returns the running time of the op mode
///
/// This call does not aquire a lock on the data,
/// nor does it need to.
pub fn running_time(&self) -> core::time::Duration {
std::time::Instant::now() - self.start_time
}
}

unsafe impl Send for OpHolder {}
unsafe impl Sync for OpHolder {}
threadsafe::thread_safe!(OpHolder);

/// The struct that is used to access the data in the op mode
///
Expand All @@ -59,43 +73,60 @@ unsafe impl Sync for OpHolder {}
///
/// # Example
///
/// ```rust,no_run,ignore
/// let op = pylib::Op::new(gamepad_wrapper);
/// let op_wrapper = pylib::Op::wrap(&op);
/// ```rust
/// # use pyo3::prelude::*;
/// # use arc_pylib as pylib;
/// # use arc_pylib::arc_robot_hardware as hardware;
/// # use hardware::IO_OK;
/// # use pylib::PyWrappedComponent as _;
/// # use pylib::__init__::Op;
/// # use pylib::setup_wrapped_component;
/// # fn main() -> hardware::Result {
/// # let (gamepad, gamepad_wrapper) = setup_wrapped_component!(
/// # pylib::arc_robot_hardware::gamepad::impls::LogitechF310::default();
/// # pylib::__init__::hardware::gamepad::Gamepad
/// # );
/// let (op, op_wrapper) = setup_wrapped_component!(gamepad_wrapper; Op);
///
/// // IO Thread
/// op.get_mut()?
/// let time = op.get()?.running_time();
/// if time.as_secs() >= 30 { // If we want to stop after 30 seconds
/// op.get_mut()?.stop()?;
/// }
/// # IO_OK
/// # }
/// ```
#[pyclass]
#[derive(Debug, Clone)]
pub struct Op(ThreadSafe<OpHolder>);

impl Op {
/// This creates a new `ThreadSafe<OpHolder>` struct. NOT a `Op` struct.
///
/// You then need to wrap it in a `Op` struct using the [`Op::wrap()`] method.
pub fn new(gamepad: _hardware::gamepad::Gamepad) -> ThreadSafe<OpHolder> {
impl crate::PyWrappedComponent<hardware::gamepad::Gamepad> for Op {
type Holder = OpHolder;
fn new(gamepad: hardware::gamepad::Gamepad) -> ThreadSafe<Self::Holder> {
ThreadSafe::new(OpHolder {
start_time: std::time::Instant::now(),
running: true.into(),
gamepad,
})
}
/// Wraps a `ThreadSafe<OpHolder>` in a `Op` struct.
pub fn wrap(op: &ThreadSafe<OpHolder>) -> Self {
Self(op.clone())
fn wrap(gamepad: &ThreadSafe<Self::Holder>) -> Self {
Self(gamepad.clone())
}
}

impl Op {
/// Returns a new [`Gamepad`] struct that is a clone of the one in the op mode.
///
/// (It's an `Arc` so it's cheap to clone)
///
/// [`Gamepad`]: _hardware/gamepad/struct.Gamepad.html
pub fn get_gamepad(&self) -> threadsafe::TSResult<_hardware::gamepad::Gamepad> {
pub fn get_gamepad(&self) -> core::result::Result<hardware::gamepad::Gamepad, &'static str> {
self.0.get().map(|g| g.gamepad().clone())
}
/// Returns whether the op mode is running
///
/// This call aquires a lock on the data
pub fn is_running(&self) -> threadsafe::TSResult<bool> {
pub fn is_running(&self) -> core::result::Result<bool, &'static str> {
self.0.get().map(|g| g.running())
}
}
Expand All @@ -106,12 +137,12 @@ impl Op {
#[getter]
#[doc(hidden)]
fn running(&self) -> PyResult<bool> {
self.is_running().map_err(make_err)
self.is_running().map_err(crate::make_err)
}
#[getter]
#[doc(hidden)]
fn gamepad(&self) -> PyResult<_hardware::gamepad::Gamepad> {
self.get_gamepad().map_err(make_err)
fn gamepad(&self) -> PyResult<hardware::gamepad::Gamepad> {
self.get_gamepad().map_err(crate::make_err)
}
}

Expand All @@ -130,23 +161,24 @@ fn sleep(seconds: f64) -> PyResult<()> {
/// This annotation is used to mark a function as an autonomous function.
#[pyclass]
#[doc(hidden)]
struct Auto(Py<PyAny>);
struct Auto(String);
#[pymethods]
impl Auto {
#[new]
#[doc(hidden)]
fn __new__(wraps: Py<PyAny>) -> Self {
Self(wraps)
fn __new__(name: &str) -> Self {
Self(name.to_string())
}
#[doc(hidden)]
#[pyo3(signature = (*args, **kwargs))]
#[pyo3(signature = (*args, **_kwargs))]
fn __call__(
&self,
py: Python<'_>,
_py: Python<'_>,
args: &pyo3::types::PyTuple,
kwargs: Option<&pyo3::types::PyDict>,
_kwargs: Option<&pyo3::types::PyDict>,
) -> PyResult<Py<PyAny>> {
self.0.call(py, args, kwargs)
let func = args.get_item(0)?;
func.extract::<Py<PyAny>>()
}
}

Expand All @@ -155,23 +187,24 @@ impl Auto {
/// This annotation is used to mark a function as a teleop function.
#[pyclass]
#[doc(hidden)]
struct Teleop(Py<PyAny>);
struct Teleop(String);
#[pymethods]
impl Teleop {
#[new]
#[doc(hidden)]
fn __new__(wraps: Py<PyAny>) -> Self {
Self(wraps)
fn __new__(name: &str) -> Self {
Self(name.to_string())
}
#[doc(hidden)]
#[pyo3(signature = (*args, **kwargs))]
#[pyo3(signature = (*args, **_kwargs))]
fn __call__(
&self,
py: Python<'_>,
_py: Python<'_>,
args: &pyo3::types::PyTuple,
kwargs: Option<&pyo3::types::PyDict>,
_kwargs: Option<&pyo3::types::PyDict>,
) -> PyResult<Py<PyAny>> {
self.0.call(py, args, kwargs)
let func = args.get_item(0)?;
func.extract::<Py<PyAny>>()
}
}

Expand All @@ -180,15 +213,19 @@ impl Teleop {
/// This function is called by the Python interpreter when the module is imported.
#[pymodule]
#[doc(hidden)]
pub fn arc(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
pub fn arc(py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(sleep, m)?)?;
m.add_class::<Teleop>()?;
m.add_class::<Auto>()?;
m.add_class::<Op>()?;
m.add("OK", true)?;

// Modules
m.add_wrapped(pyo3::wrap_pymodule!(_hardware::hardware))?;
// Submodules
//
// Currently there is no better way to do this.
// See https://github.com/DranikiRobotics/arc/issues/3
crate::pymod!(hardware -> hardware::hardware_submodule, "arc.hardware", py, m);
crate::pymod!(math -> math::math_submodule, "arc.math", py, m);

Ok(())
}
32 changes: 18 additions & 14 deletions arc/hardware/__init__.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
use pyo3::prelude::*;

pub mod gamepad;

#[pymodule]
pub fn hardware(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
// Modules
m.add_wrapped(pyo3::wrap_pymodule!(gamepad::gamepad))?;

// Their classes
m.add_class::<gamepad::Gamepad>()?;

Ok(())
}
//! Hardware module for the arc crate
//!
//! Python identifier: `arc.hardware`
use pyo3::prelude::*;

pub mod gamepad;

/// The hardware module.
pub(crate) fn hardware_submodule(py: Python<'_>, m: &PyModule) -> PyResult<()> {
crate::pymod!(gamepad -> gamepad::gamepad_submodule, "arc.hardware.gamepad", py, m);

m.add_class::<gamepad::Gamepad>()?;
m.add_class::<gamepad::GamepadDpad>()?;
m.add_class::<gamepad::GamepadStick>()?;

Ok(())
}
Loading

0 comments on commit 29fd5b7

Please sign in to comment.