-
Notifications
You must be signed in to change notification settings - Fork 9
All components take a single port #215
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 15 commits
907d8f7
fb5642c
cfdf242
529b601
b799da8
2445705
dd91671
6c8cd86
9eda6cc
a40bc55
6d0a38b
8c35186
86e148f
25d14ee
30e08e7
d8d2533
605e441
02e9454
cd01138
555ce33
65f1563
c3e2d21
a6b695a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,7 +17,8 @@ | |
| # @author [email protected] (Alan Smith) | ||
|
|
||
| from typing import Protocol, Self | ||
| from component import Component, PortType, InvalidPortException | ||
| from component import Component, InvalidPortException | ||
| from port import Port, PortType | ||
|
|
||
| class DistanceCallable(Protocol): | ||
| def __call__(self, distance : float) -> None: | ||
|
|
@@ -30,11 +31,8 @@ def __call__(self, hue : int, saturation : int, value : int) -> None: | |
|
|
||
|
|
||
| class ColorRangeSensor(Component): | ||
| def __init__(self, ports : list[tuple[PortType, int]]): | ||
| portType, port = ports[0] | ||
| if portType != PortType.I2C_PORT: | ||
| raise InvalidPortException | ||
| self.port = port | ||
| def __init__(self, port : Port): | ||
| super().__init__( port, expectedType = PortType.I2C_PORT) | ||
|
|
||
| def get_manufacturer(self) -> str: | ||
| return "REV Robotics" | ||
|
|
@@ -54,17 +52,9 @@ def get_version(self) -> tuple[int, int, int]: | |
| def reset(self) -> None: | ||
| pass | ||
|
|
||
| def get_connection_port_type(self) -> list[PortType]: | ||
| return [PortType.I2C_PORT] | ||
|
|
||
| def periodic(self) -> None: | ||
| pass | ||
|
|
||
| # Alternative constructor to create an instance from an i2c port | ||
| @classmethod | ||
| def from_i2c_port(cls: type[Self], i2c_port: int) -> Self: | ||
| return cls([(PortType.I2C_PORT, i2c_port)]) | ||
|
|
||
|
|
||
| # Component specific methods | ||
|
|
||
| def get_color_rgb(self) -> tuple[int, int, int]: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,32 +17,26 @@ | |
| # @author [email protected] (Alan Smith) | ||
|
|
||
| from abc import ABC, abstractmethod | ||
| from enum import Enum | ||
| from typing import Protocol | ||
| from port import Port, PortType | ||
|
|
||
| class EmptyCallable(Protocol): | ||
| def __call__(self) -> None: | ||
| pass | ||
|
|
||
|
|
||
| class PortType(Enum): | ||
| CAN_PORT = 1 | ||
| SMART_IO_PORT = 2 | ||
| SMART_MOTOR_PORT = 3 | ||
| SERVO_PORT = 4 | ||
| I2C_PORT = 5 | ||
| USB_PORT = 6 | ||
|
|
||
|
|
||
| class InvalidPortException(Exception): | ||
| pass | ||
|
|
||
|
|
||
| # This is an abstract class | ||
| class Component(ABC): | ||
| @abstractmethod | ||
| def __init__(self, ports : list[tuple[PortType, int]]): | ||
| pass | ||
| def __init__(self, port : Port, expectedType : PortType): | ||
| """This has the port it is attached to, and the expected type of the port""" | ||
| if port.get_type != expectedType: | ||
| raise InvalidPortException | ||
|
|
||
| self.port = port | ||
|
|
||
| # Returns the manufacturer of the component | ||
| @abstractmethod | ||
|
|
@@ -87,11 +81,11 @@ def stop(self) -> None: | |
| def reset(self) -> None: | ||
| pass | ||
|
|
||
| # Returns a list (can be empty, one, or multiple) of the ports this connects to | ||
| # of the PortType enumeration | ||
| @abstractmethod | ||
| def get_connection_port_type(self) -> list[PortType]: | ||
| pass | ||
| # Returns the port this connects to of the PortType enumeration | ||
| def get_connection_port_type(self) -> PortType | None: | ||
| if self.port: | ||
| return self.port.get_type() | ||
| return None | ||
|
|
||
| # This is called periodically when an opmode is running. The component might use this | ||
| # to talk to hardware and then call callbacks | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,21 +21,15 @@ | |
| __author__ = "[email protected] (Liz Looney)" | ||
|
|
||
| from typing import Self | ||
| from component import Component, PortType, InvalidPortException | ||
| from component import Component, InvalidPortException | ||
| from port import Port, PortType | ||
| import expansion_hub | ||
| import wpimath | ||
|
|
||
| # TODO(lizlooney): Update port types. | ||
|
|
||
| class ExpansionHubMotor(Component): | ||
| def __init__(self, ports : list[tuple[PortType, int]]): | ||
| port_type, hub_number = ports[0] | ||
| if port_type != PortType.USB_PORT: | ||
| raise InvalidPortException | ||
| port_type, motor_number = ports[1] | ||
| if port_type != PortType.USB_PORT: | ||
| raise InvalidPortException | ||
| self.expansion_hub_motor = expansion_hub.ExpansionHubMotor(hub_number, motor_number) | ||
| def __init__(self, port : Port): | ||
| super().__init__(port, PortType.EXPANSION_HUB_MOTOR) | ||
| self.expansion_hub_motor = expansion_hub.ExpansionHubMotor(self.port.port1.location, self.port.port2.location) | ||
|
|
||
| def get_manufacturer(self) -> str: | ||
| return "REV Robotics" | ||
|
|
@@ -62,17 +56,9 @@ def stop(self) -> None: | |
| def reset(self) -> None: | ||
| pass | ||
|
|
||
| def get_connection_port_type(self) -> list[PortType]: | ||
| return [PortType.USB_PORT, PortType.USB_PORT] | ||
|
|
||
| def periodic(self) -> None: | ||
| pass | ||
|
|
||
| # Alternative constructor to create an instance from a hub number and a motor port. | ||
| @classmethod | ||
| def from_hub_number_and_motor_number(cls: type[Self], hub_number: int, motor_number: int) -> Self: | ||
| return cls([(PortType.USB_PORT, hub_number), (PortType.USB_PORT, motor_number)]) | ||
|
|
||
| # Component specific methods | ||
|
|
||
| # Methods from expansion_hub.ExpansionHubMotor | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| # @license | ||
| # Copyright 2025 Porpoiseful LLC | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # https://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| # @fileoverview This is a class to handle port types | ||
| # @author [email protected] (Alan Smith) | ||
| from enum import Enum | ||
| from typing import Self | ||
|
|
||
| class PortType(Enum): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a class comment that shows examples of how to use PortType?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am happy to write a comment here, but I couldn't come up with anything about how to use it that didn't seem like "this is the type of port". Suggestions? |
||
| CAN_PORT = 1 | ||
| SMART_IO_PORT = 2 | ||
| SMART_MOTOR_PORT = 3 | ||
| SERVO_PORT = 4 | ||
| I2C_PORT = 5 | ||
| USB_PORT = 6 | ||
| EXPANSION_HUB_MOTOR_PORT = 7 | ||
| EXPANSION_HUB_SERVO_PORT = 8 | ||
|
|
||
| BASE_COMPOUND = 256 | ||
| USB_HUB = BASE_COMPOUND + 1 | ||
| EXPANSION_HUB_MOTOR = BASE_COMPOUND + 2 | ||
alan412 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| EXPANSION_HUB_SERVO = BASE_COMPOUND + 3 | ||
|
|
||
| class Port: | ||
| def __init__(self, port_type : PortType = None, location : int = None, port1 : type[Self] = None, port2 : type[Self] = None): | ||
| """ | ||
| Create a port that can be either simple (type + location) or compound (two other ports). | ||
| Args: | ||
| port_type: PortType or CompoundPortType for this port | ||
| location: int location for simple ports | ||
| port1: First Port for compound ports | ||
| port2: Second Port for compound ports | ||
| """ | ||
| if port1 is not None and port2 is not None: | ||
| # Compound port | ||
| if port_type < PortType.BASE_COMPOUND: | ||
| raise ValueError("Port must be of a compound type") | ||
| self.is_compound = True | ||
| self.type = port_type | ||
| self.port1 = port1 | ||
| self.port2 = port2 | ||
| self.location = None | ||
| elif port_type is not None and location is not None: | ||
| # Simple port | ||
| if port_type > PortType.BASE_COMPOUND: | ||
| raise ValueError("Port must be of a simple type") | ||
| self.is_compound = False | ||
| self.type = port_type | ||
| self.location = location | ||
| self.port1 = None | ||
| self.port2 = None | ||
| else: | ||
| raise ValueError("Port must be either simple (type + location) or compound (port1 + port2)") | ||
|
|
||
| def get_all_ports(self): | ||
| """Return a list of all simple ports contained in this port.""" | ||
| if self.is_compound: | ||
| return self.port1.get_all_ports() + self.port2.get_all_ports() | ||
| else: | ||
| return [(self.type, self.location)] | ||
|
|
||
|
|
||
| def get_type(self): | ||
| """This returns the type if it is simple or the type of the last one in the chain if compound""" | ||
| if self.is_compound: | ||
| return self.port2.get_type() | ||
| else: | ||
| return self.type | ||
|
|
||
| def __str__(self): | ||
| if self.is_compound: | ||
| return f"CompoundPort({self.type}: {self.port1}, {self.port2})" | ||
| else: | ||
| return f"Port({self.type}, {self.location})" | ||
Uh oh!
There was an error while loading. Please reload this page.