10 Commits

Author SHA1 Message Date
Riley M.
8404999369 Merge pull request #31 from SHC-ASTRA/can-refactor
Refactor anchor & add direct CAN connector
2026-04-08 00:18:13 -05:00
ryleu
88574524cf clarify the mock connector usage in the README 2026-04-08 00:15:39 -05:00
ryleu
30bb32a66b remove extraneous slice 2026-04-08 00:09:04 -05:00
David
010d2da0b6 fix: string number 2026-04-07 23:45:40 -05:00
ryleu
0a257abf43 make the pad 3 -> logic consistent 2026-04-07 23:44:38 -05:00
ryleu
b09b55bee0 fix bug because apparently python has arrays 2026-04-07 22:19:55 -05:00
ryleu
ec7f272934 clean up code 2026-04-07 22:16:08 -05:00
ryleu
bc9183d59a make mock mcu use VicCAN messages 2026-04-07 21:52:52 -05:00
ryleu
410d3706ed update README with mock connector instructions 2026-04-02 19:49:07 -05:00
ryleu
89b3194914 update documentation and accept 3-value VicCAN messages 2026-04-02 19:43:10 -05:00
4 changed files with 29 additions and 17 deletions

View File

@@ -68,12 +68,25 @@ Anchor provides a mock connector meant for testing and scripting purposes. You c
$ ros2 launch anchor_pkg rover.launch.py connector:="mock" $ ros2 launch anchor_pkg rover.launch.py connector:="mock"
``` ```
You can see all data sent to it in a string format with this command: To see all data that would be sent over the CAN network (and thus to the microcontrollers), use this command:
```bash ```bash
$ ros2 topic echo /anchor/to_vic/debug $ ros2 topic echo /anchor/to_vic/debug
``` ```
To send data to the mock connector (as if you were a ROS2 node), use the normal relay topic:
```bash
$ ros2 topic pub /anchor/to_vic/relay astra_msgs/msg/VicCAN '{mcu_name: "core", command_id: 50, data: [0.0, 2.0, 0.0, 1.0]}'
```
To send data to the mock connector (as if you were a microcontroller), publish to the dedicated topic:
```bash
$ ros2 topic pub /anchor/from_vic/mock_mcu astra_msgs/msg/VicCAN '{mcu_name: "arm", command_id: 55, data: [0.0, 450.0, 900.0, 0.0]}'
```
### Testing Serial ### Testing Serial
You can fake the presence of a Serial device (i.e., MCU) by using the following command: You can fake the presence of a Serial device (i.e., MCU) by using the following command:

View File

@@ -37,11 +37,14 @@ class Anchor(Node):
Subscribers: Subscribers:
* /anchor/from_vic/mock_mcu * /anchor/from_vic/mock_mcu
- For testing without an actual MCU, publish strings here as if they came from an MCU - For testing without an actual MCU, publish ViCAN messages here as if they came from an MCU
* /anchor/to_vic/relay * /anchor/to_vic/relay
- Core, Arm, and Bio publish VicCAN messages to this topic to send to the MCU - Core, Arm, and Bio publish VicCAN messages to this topic to send to the MCU
* /anchor/to_vic/relay_string * /anchor/to_vic/relay_string
- Send raw strings to connectors. Does not work for connectors that require conversion (like CANConnector) - Send raw strings to connectors. Does not work for connectors that require conversion (like CANConnector)
* /anchor/relay
- Legacy method for talking to connectors. Takes String as input, but does not send the raw strings to connectors.
Instead, it converts them to VicCAN messages first.
""" """
connector: Connector connector: Connector
@@ -172,7 +175,7 @@ class Anchor(Node):
self.mock_mcu_sub_ = self.create_subscription( self.mock_mcu_sub_ = self.create_subscription(
String, String,
"/anchor/from_vic/mock_mcu", "/anchor/from_vic/mock_mcu",
self.on_mock_fromvic, self.relay_fromvic,
20, 20,
) )
self.tovic_string_sub_ = self.create_subscription( self.tovic_string_sub_ = self.create_subscription(
@@ -203,7 +206,9 @@ class Anchor(Node):
self.connector.write(msg) self.connector.write(msg)
self.tovic_debug_pub_.publish(msg) self.tovic_debug_pub_.publish(msg)
@deprecated("Use /anchor/to_vic/relay instead of /anchor/relay") @deprecated(
"Use /anchor/to_vic/relay or /anchor/to_vic/relay_string instead of /anchor/relay"
)
def write_connector_legacy(self, msg: String): def write_connector_legacy(self, msg: String):
"""Write to the connector by first attempting to convert String to VicCAN""" """Write to the connector by first attempting to convert String to VicCAN"""
# please do not reference this code. ~riley # please do not reference this code. ~riley
@@ -226,17 +231,6 @@ class Anchor(Node):
elif msg.mcu_name == "citadel" or msg.mcu_name == "digit": elif msg.mcu_name == "citadel" or msg.mcu_name == "digit":
self.fromvic_bio_pub_.publish(msg) self.fromvic_bio_pub_.publish(msg)
def on_mock_fromvic(self, msg: String):
"""Relay a message as if it came from the MCU"""
viccan = string_to_viccan(
msg.data,
"mock",
self.get_logger(),
self.get_clock().now().to_msg(),
)
if viccan:
self.relay_fromvic(viccan)
def main(args=None): def main(args=None):
try: try:

View File

@@ -257,7 +257,7 @@ class CANConnector(Connector):
) )
if self.can_channel and self.can_channel.startswith("v"): if self.can_channel and self.can_channel.startswith("v"):
self.logger.warn("likely using virtual CAN interface") self.logger.warn("CAN interface is likely virtual")
def read(self) -> tuple[VicCAN | None, str | None]: def read(self) -> tuple[VicCAN | None, str | None]:
if not self.can_bus: if not self.can_bus:
@@ -372,8 +372,10 @@ class CANConnector(Connector):
case 2: case 2:
data_type = 1 data_type = 1
data = struct.pack(">ff", *msg.data) data = struct.pack(">ff", *msg.data)
case 4: case 3 | 4: # 3 gets treated as 4
data_type = 2 data_type = 2
if data_len == 3:
msg.data.append(0)
data = struct.pack(">hhhh", *[int(x) for x in msg.data]) data = struct.pack(">hhhh", *[int(x) for x in msg.data])
case _: case _:
self.logger.error( self.logger.error(

View File

@@ -57,6 +57,9 @@ def string_to_viccan(
def viccan_to_string(viccan: VicCAN) -> str: def viccan_to_string(viccan: VicCAN) -> str:
"""Converts a ROS2 VicCAN message to the serial string VicCAN format.""" """Converts a ROS2 VicCAN message to the serial string VicCAN format."""
# make sure we accept 3 digits and treat it as 4
if len(viccan.data) == 3:
viccan.data.append(0)
# go from [ w, x, y, z ] -> ",w,x,y,z" & round to 7 digits max # go from [ w, x, y, z ] -> ",w,x,y,z" & round to 7 digits max
data = "".join([f",{round(val,7)}" for val in viccan.data]) data = "".join([f",{round(val,7)}" for val in viccan.data])
return f"can_relay_tovic,{viccan.mcu_name},{viccan.command_id}{data}\n" return f"can_relay_tovic,{viccan.mcu_name},{viccan.command_id}{data}\n"