Skip to content

Feat/refacto and telop#9

Merged
damnthonyy merged 7 commits into
developmentfrom
feat/refacto-and-telop
May 21, 2026
Merged

Feat/refacto and telop#9
damnthonyy merged 7 commits into
developmentfrom
feat/refacto-and-telop

Conversation

@KelianHalleray

Copy link
Copy Markdown
Contributor

Summary

Harden robot teleoperation reliability by introducing a deadman safety watchdog, fixing rosbridge WebSocket concurrency issues, and improving real robot teleop configuration for Yahboom M3 Pro.

Description

This PR strengthens the backend robot control pipeline for real-world operation by improving teleoperation safety and rosbridge communication reliability.

A teleop deadman watchdog was introduced to automatically stop the robot if control messages stop arriving (e.g. frontend freeze, browser crash, network interruption), preventing uncontrolled movement.

Additionally, the rosbridge client was refactored to eliminate concurrent WebSocket reads during latency measurement, ensuring stable telemetry delivery and preventing race conditions affecting battery, odometry, and ping diagnostics.

The real robot configuration was also updated with safer initial teleop limits for Yahboom M3 Pro testing.


What's Changed

Affected areas

  • Rosbridge communication reliability
  • Teleoperation runtime safety
  • Backend lifecycle management
  • Real robot teleop configuration
  • Unit/integration test coverage

Key files

src/robocoop_backend/robocoop_backend/adapters/rosbridge_client.py

Fixed concurrent WebSocket read issue by enforcing a single message consumer and pending ping future dispatch.

src/robocoop_backend/robocoop_backend/modules/robot/teleop.py

Added TeleopCommand.is_zero() helper for watchdog integration.

src/robocoop_backend/robocoop_backend/modules/robot/teleop_watchdog.py

Added deadman switch auto-stop watchdog for teleoperation safety.

src/robocoop_backend/robocoop_backend/app/backend_context.py

Integrated watchdog lifecycle management (startup / shutdown).

src/robocoop_backend/robocoop_backend/app/websocket_handler.py

Added watchdog notification on every valid teleop command.

src/robocoop_backend/robocoop_backend/tests/unit/adapters/test_rosbridge_client.py

Added rosbridge reliability and ping handling tests.

src/robocoop_backend/robocoop_backend/tests/unit/modules/test_teleop_watchdog.py

Added deadman watchdog unit tests.

src/robocoop_backend/robocoop_backend/tests/unit/app/test_websocket_handler.py

Added watchdog notification coverage.

src/robocoop_backend/robocoop_backend/tests/integration/test_context_lifecycle.py

Updated integration config expectations.

src/robocoop_bringup/config/common.params.yaml

Added deadman switch config defaults.

src/robocoop_bringup/config/real.params.yaml

Added safer Yahboom M3 Pro teleop limits.


Details

Rosbridge reliability hardening

Fixed a critical architecture issue where multiple coroutines could read from the same rosbridge WebSocket simultaneously.

Before:

  • listener task consumed telemetry messages
  • measure_ping() also read directly from the same socket
  • possible race conditions and lost messages

After:

  • single WebSocket consumer model
  • ping requests tracked via pending futures
  • pong responses resolved by the main listener
  • deterministic telemetry handling

This improves reliability for:

  • battery telemetry
  • odometry updates
  • ping diagnostics
  • reconnection behavior

Teleop deadman safety

Added an automatic emergency stop safeguard for teleoperation.

Behavior:

  • every valid teleop command refreshes watchdog timer
  • if no new command arrives within configured timeout
  • backend automatically sends zero velocity command

This protects against:

  • frontend crash
  • browser freeze
  • network interruption
  • dropped teleop streams

Default configuration:

deadman_timeout_s: 0.5
deadman_interval_s: 0.05

iGrec-a2n and others added 4 commits May 19, 2026 16:06
…ures

- Implemented RGB LED chenillard that runs on startup when connected to rosbridge.
- Updated README to include instructions for standalone LED testing and configuration options.
- Enhanced telemetry service to monitor linear velocity and critical battery levels, with corresponding audit events.
- Updated adapter factory to support new RGB functionality and ensure proper configuration handling.
- Added tests for new features, including telemetry checks for battery and velocity thresholds.
@KelianHalleray KelianHalleray self-assigned this May 21, 2026
@qodo-code-review

qodo-code-review Bot commented May 21, 2026

Copy link
Copy Markdown

Review Summary by Qodo

(Agentic_describe updated until commit d39fded)

Harden teleop reliability with deadman watchdog, fix rosbridge concurrency, add RGB LED effects

✨ Enhancement 🐞 Bug fix

Grey Divider

Walkthroughs

Description
• Refactored rosbridge WebSocket to eliminate concurrent reads during ping measurement, ensuring
  reliable telemetry delivery
• Added teleop deadman watchdog that auto-stops robot if control messages cease arriving (frontend
  crash, network interruption)
• Implemented RGB LED chenillard animation on /rgb topic with configurable hue sweep and LED
  indexing
• Enhanced telemetry pipeline with battery EMA filtering, critical threshold alerts, and linear
  velocity monitoring
• Extended teleop command parsing with axis clamping, legacy key support, and comprehensive
  validation
• Added health endpoint and structured command acknowledgment/error responses for WebSocket protocol
Diagram
flowchart LR
  A["WebSocket Client"] -->|teleop.move| B["WebSocketHandler"]
  B -->|parse_teleop_command| C["TeleopCommand"]
  C -->|send_velocity| D["RosbridgeAdapter"]
  D -->|notify_command| E["TeleopWatchdog"]
  E -->|timeout check| F["send_velocity ZERO"]
  D -->|publish| G["RosbridgeClient"]
  G -->|single consumer| H["WebSocket Listener"]
  H -->|resolve pending| I["Ping Futures"]
  D -->|start| J["RgbChenillard"]
  J -->|publish HSV→RGB| G

Loading

File Changes

1. src/robocoop_backend/robocoop_backend/adapters/rosbridge_client.py 🐞 Bug fix +130/-42

Fix concurrent WebSocket reads, implement pending ping futures

src/robocoop_backend/robocoop_backend/adapters/rosbridge_client.py


2. src/robocoop_backend/robocoop_backend/modules/robot/teleop.py ✨ Enhancement +74/-0

Add TeleopCommand dataclass with parsing and validation

src/robocoop_backend/robocoop_backend/modules/robot/teleop.py


3. src/robocoop_backend/robocoop_backend/modules/robot/teleop_watchdog.py ✨ Enhancement +79/-0

Implement deadman switch auto-stop watchdog for safety

src/robocoop_backend/robocoop_backend/modules/robot/teleop_watchdog.py


View more (39)
4. src/robocoop_backend/robocoop_backend/modules/robot/rgb_chenillard.py ✨ Enhancement +77/-0

Add RGB LED chenillard animation with HSV color sweep

src/robocoop_backend/robocoop_backend/modules/robot/rgb_chenillard.py


5. src/robocoop_backend/robocoop_backend/adapters/rosbridge_adapter.py ✨ Enhancement +126/-4

Add velocity publishing, battery EMA filtering, RGB integration

src/robocoop_backend/robocoop_backend/adapters/rosbridge_adapter.py


6. src/robocoop_backend/robocoop_backend/adapters/mock_adapter.py ✨ Enhancement +30/-3

Implement send_velocity and emergency_stop methods

src/robocoop_backend/robocoop_backend/adapters/mock_adapter.py


7. src/robocoop_backend/robocoop_backend/adapters/base_adapter.py ✨ Enhancement +15/-0

Add abstract send_velocity and emergency_stop methods

src/robocoop_backend/robocoop_backend/adapters/base_adapter.py


8. src/robocoop_backend/robocoop_backend/adapters/factory.py ✨ Enhancement +45/-3

Wire teleop limits, RGB config, and message types from YAML

src/robocoop_backend/robocoop_backend/adapters/factory.py


9. src/robocoop_backend/robocoop_backend/app/websocket_handler.py ✨ Enhancement +102/-6

Add teleop command parsing, watchdog notification, health endpoint

src/robocoop_backend/robocoop_backend/app/websocket_handler.py


10. src/robocoop_backend/robocoop_backend/app/backend_context.py ✨ Enhancement +41/-5

Integrate teleop watchdog lifecycle and audit file sink

src/robocoop_backend/robocoop_backend/app/backend_context.py


11. src/robocoop_backend/robocoop_backend/app/contracts.py 📝 Documentation +57/-2

Add health, command_ack, command_error message types

src/robocoop_backend/robocoop_backend/app/contracts.py


12. src/robocoop_backend/robocoop_backend/modules/robot/state_store.py ✨ Enhancement +2/-0

Add linear_velocity field to robot state

src/robocoop_backend/robocoop_backend/modules/robot/state_store.py


13. src/robocoop_backend/robocoop_backend/modules/robot/telemetry_service.py ✨ Enhancement +48/-3

Add battery critical threshold and velocity high alerts

src/robocoop_backend/robocoop_backend/modules/robot/telemetry_service.py


14. src/robocoop_backend/robocoop_backend/tests/unit/adapters/test_rosbridge_adapter.py 🧪 Tests +92/-42

Update battery tests for EMA filtering and add odom/command tests

src/robocoop_backend/robocoop_backend/tests/unit/adapters/test_rosbridge_adapter.py


15. src/robocoop_backend/robocoop_backend/tests/unit/adapters/test_rosbridge_client.py 🧪 Tests +87/-2

Add ping future handling and publish method tests

src/robocoop_backend/robocoop_backend/tests/unit/adapters/test_rosbridge_client.py


16. src/robocoop_backend/robocoop_backend/tests/unit/modules/test_teleop.py 🧪 Tests +177/-0

Add comprehensive teleop command parsing and validation tests

src/robocoop_backend/robocoop_backend/tests/unit/modules/test_teleop.py


17. src/robocoop_backend/robocoop_backend/tests/unit/modules/test_teleop_watchdog.py 🧪 Tests +98/-0

Add deadman watchdog timeout and notification tests

src/robocoop_backend/robocoop_backend/tests/unit/modules/test_teleop_watchdog.py


18. src/robocoop_backend/robocoop_backend/tests/unit/modules/test_rgb_chenillard.py 🧪 Tests +29/-0

Add RGB chenillard publish and blackout tests

src/robocoop_backend/robocoop_backend/tests/unit/modules/test_rgb_chenillard.py


19. src/robocoop_backend/robocoop_backend/tests/unit/app/test_websocket_handler.py 🧪 Tests +61/-4

Add teleop watchdog notification and health endpoint tests

src/robocoop_backend/robocoop_backend/tests/unit/app/test_websocket_handler.py


20. src/robocoop_backend/robocoop_backend/tests/unit/adapters/test_factory.py 🧪 Tests +60/-2

Add teleop limits, RGB config, and message type wiring tests

src/robocoop_backend/robocoop_backend/tests/unit/adapters/test_factory.py


21. src/robocoop_backend/robocoop_backend/tests/unit/adapters/test_mock_adapter.py 🧪 Tests +15/-0

Add send_velocity and emergency_stop method tests

src/robocoop_backend/robocoop_backend/tests/unit/adapters/test_mock_adapter.py


22. src/robocoop_backend/robocoop_backend/tests/unit/app/test_backend_context.py 🧪 Tests +25/-0

Add battery threshold and audit sink configuration tests

src/robocoop_backend/robocoop_backend/tests/unit/app/test_backend_context.py


23. src/robocoop_backend/robocoop_backend/tests/unit/modules/test_telemetry_service.py 🧪 Tests +76/-0

Add battery critical and velocity high threshold tests

src/robocoop_backend/robocoop_backend/tests/unit/modules/test_telemetry_service.py


24. src/robocoop_backend/robocoop_backend/tests/unit/modules/test_state_store.py 🧪 Tests +5/-0

Add linear_velocity field update test

src/robocoop_backend/robocoop_backend/tests/unit/modules/test_state_store.py


25. src/robocoop_backend/robocoop_backend/tests/integration/test_context_lifecycle.py 🧪 Tests +57/-0

Add teleop/emergency handler and bundled config integration tests

src/robocoop_backend/robocoop_backend/tests/integration/test_context_lifecycle.py


26. src/robocoop_backend/robocoop_backend/tests/integration/test_telemetry_pipeline.py 🧪 Tests +32/-5

Update battery flow tests for EMA filtering and add odom/velocity tests

src/robocoop_backend/robocoop_backend/tests/integration/test_telemetry_pipeline.py


27. src/robocoop_backend/robocoop_backend/scripts/rgb_chenillard_standalone.py ✨ Enhancement +56/-0

Add standalone RGB chenillard script for testing

src/robocoop_backend/robocoop_backend/scripts/rgb_chenillard_standalone.py


28. src/robocoop_backend/robocoop_backend/app/server.py 🐞 Bug fix +5/-6

Add Windows platform signal handler compatibility

src/robocoop_backend/robocoop_backend/app/server.py


29. src/robocoop_bringup/config/common.params.yaml ⚙️ Configuration changes +27/-0

Add teleop, RGB, and message type configuration defaults

src/robocoop_bringup/config/common.params.yaml


30. src/robocoop_bringup/config/real.params.yaml ⚙️ Configuration changes +14/-0

Add real robot teleop limits, RGB enable, and topic mappings

src/robocoop_bringup/config/real.params.yaml


31. src/robocoop_bringup/config/README.md 📝 Documentation +68/-1

Document RGB chenillard, publish topics, and message types

src/robocoop_bringup/config/README.md


32. src/robocoop_backend/robocoop_backend/adapters/README.md 📝 Documentation +37/-13

Document RGB chenillard and publish layer architecture

src/robocoop_backend/robocoop_backend/adapters/README.md


33. src/robocoop_backend/robocoop_backend/modules/robot/README.md 📝 Documentation +28/-1

Document RGB chenillard module and standalone test script

src/robocoop_backend/robocoop_backend/modules/robot/README.md


34. src/robocoop_backend/robocoop_backend/modules/audit/README.md 📝 Documentation +3/-1

Document battery critical and velocity high audit events

src/robocoop_backend/robocoop_backend/modules/audit/README.md


35. README.md 📝 Documentation +16/-2

Add RGB chenillard, teleop, and standalone test documentation

README.md


36. scripts/rgb_chenillard.sh ✨ Enhancement +16/-0

Add standalone RGB chenillard launcher script

scripts/rgb_chenillard.sh


37. scripts/README.md 📝 Documentation +16/-0

Document RGB chenillard standalone script usage

scripts/README.md


38. run_backend.sh 📝 Documentation +2/-0

Add comment about RGB chenillard backend integration

run_backend.sh


39. src/robocoop_backend/robocoop_backend.egg-info/SOURCES.txt ⚙️ Configuration changes +6/-1

Update package manifest with new modules and scripts

src/robocoop_backend/robocoop_backend.egg-info/SOURCES.txt


40. src/robocoop_backend/robocoop_backend/app/README.md Additional files +1/-1

...

src/robocoop_backend/robocoop_backend/app/README.md


41. src/robocoop_backend/robocoop_backend/scripts/__init__.py Additional files +0/-0

...

src/robocoop_backend/robocoop_backend/scripts/init.py


42. supervisor/src/robocoop_supervisor/ui/main_window.py Additional files +0/-0

...

supervisor/src/robocoop_supervisor/ui/main_window.py


Grey Divider

ⓘ You are approaching your monthly quota for Qodo. Upgrade your plan

Qodo Logo

@qodo-code-review

qodo-code-review Bot commented May 21, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Subscribe logs false success 🐞 Bug ☼ Reliability
Description
RosbridgeClient.subscribe() logs "Subscribed" unconditionally even if the WebSocket send fails,
because _send_json() swallows exceptions and doesn’t signal failure. This can leave the backend
believing critical telemetry subscriptions are active while rosbridge never received them, with
misleading logs.
Code

src/robocoop_backend/robocoop_backend/adapters/rosbridge_client.py[R83-99]

Evidence
In the current implementation, subscribe() logs success after awaiting _send_json(), while
_send_json() catches all send exceptions and only logs them, so subscription failures cannot be
detected by the caller and still produce a success log line.

src/robocoop_backend/robocoop_backend/adapters/rosbridge_client.py[83-100]
src/robocoop_backend/robocoop_backend/adapters/rosbridge_client.py[136-145]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RosbridgeClient.subscribe()` logs success even if the underlying `send()` failed, because `_send_json()` catches and logs exceptions and then returns normally. This creates a false-success state where callbacks are registered and logs say “Subscribed”, but rosbridge never received the subscribe frame.
## Issue Context
Subscriptions are the backbone of battery/odom telemetry; misleading success logs make outages hard to detect and can look like downstream parsing bugs.
## Fix Focus Areas
- src/robocoop_backend/robocoop_backend/adapters/rosbridge_client.py[83-145]
### Suggested implementation direction
- Make `_send_json()` either:
- raise on send failure, OR
- return `bool` success/failure.
- In `subscribe()`:
- only log “Subscribed to …” when send succeeded,
- and consider only storing `_subscribers/_subscription_ids` after a successful send (or remove them if send fails).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Watchdog stop doesn’t halt 🐞 Bug ☼ Reliability
Description
TeleopWatchdog.stop() cancels the watchdog loop without sending a zero-velocity command even if the
last teleop command was non-zero, and BackendContext.disconnect() stops the watchdog before
disconnecting the adapter. This breaks the deadman safety guarantee during graceful shutdown/restart
because the last commanded velocity is never actively cleared.
Code

src/robocoop_backend/robocoop_backend/modules/robot/teleop_watchdog.py[R50-61]

Evidence
TeleopWatchdog.stop() only cancels the internal task and logs “stopped” without publishing a stop
command, and BackendContext.disconnect() calls that stop method during shutdown, so shutdown can
occur while the last teleop command remains non-zero.

src/robocoop_backend/robocoop_backend/modules/robot/teleop_watchdog.py[50-61]
src/robocoop_backend/robocoop_backend/app/backend_context.py[72-82]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Stopping the watchdog currently only stops the monitoring task; it does not publish a final stop command when the last command was non-zero. Since `BackendContext.disconnect()` stops the watchdog as part of shutdown, the system can shut down without ever sending a final zero velocity.
## Issue Context
The PR’s stated purpose is teleop safety via a deadman watchdog. A graceful shutdown/restart is a common operational path and should be safe too.
## Fix Focus Areas
- src/robocoop_backend/robocoop_backend/modules/robot/teleop_watchdog.py[50-79]
- src/robocoop_backend/robocoop_backend/app/backend_context.py[72-82]
### Suggested implementation direction
- In `TeleopWatchdog.stop()` (or in `BackendContext.disconnect()` before stopping/cancelling tasks), if the last command was non-zero, call `adapter.send_velocity(ZERO_TELEOP_COMMAND)` once.
- Wrap the send in a try/except and log if it fails (don’t block shutdown).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Signed velocity threshold check 🐞 Bug ≡ Correctness
Description
TelemetryService._check_velocity() compares signed linear_velocity to the threshold (velocity >
threshold), so high-speed reverse motion (negative linear_x) never triggers the velocity.high audit
event. This makes the “Linear speed exceeds threshold” alert direction-dependent.
Code

src/robocoop_backend/robocoop_backend/modules/robot/telemetry_service.py[R81-94]

Evidence
The implementation only alerts when velocity is greater than the threshold, while the documented
semantics are “speed exceeds threshold”; negative values (reverse) cannot satisfy the current
condition.

src/robocoop_backend/robocoop_backend/modules/robot/telemetry_service.py[81-94]
src/robocoop_backend/robocoop_backend/modules/audit/README.md[47-54]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`_check_velocity()` uses a signed comparison (`velocity > threshold`). For odometry `linear.x`, reverse movement is typically negative, so reverse speeds will never trigger `velocity.high`.
## Issue Context
The audit documentation describes `velocity.high` as “Linear speed exceeds threshold”, which implies magnitude rather than direction.
## Fix Focus Areas
- src/robocoop_backend/robocoop_backend/modules/robot/telemetry_service.py[81-94]
### Suggested implementation direction
- Change the condition to `if abs(velocity) > self.velocity_warning_threshold:`.
- Keep the payload value as either the signed velocity (for direction) or the absolute value—choose one and document it (tests may need updating accordingly).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Battery filter persists reconnect 🐞 Bug ≡ Correctness
Description
RosbridgeRobotAdapter smooths battery voltage using _filtered_voltage but never resets it on
reconnect/disconnect; only _last_battery_time is reset. After a reconnect, the first reported
battery percentage can be skewed by stale pre-disconnect voltage, potentially producing incorrect
state/alerts immediately after recovery.
Code

src/robocoop_backend/robocoop_backend/adapters/rosbridge_adapter.py[R229-233]

Evidence
The adapter maintains _filtered_voltage state and updates it for every battery message, but the
reconnect handler resets only _last_battery_time, leaving the filter state intact across
connection boundaries.

src/robocoop_backend/robocoop_backend/adapters/rosbridge_adapter.py[45-51]
src/robocoop_backend/robocoop_backend/adapters/rosbridge_adapter.py[176-195]
src/robocoop_backend/robocoop_backend/adapters/rosbridge_adapter.py[229-235]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Battery percentage is computed from an exponential moving average stored in `_filtered_voltage`. On reconnect, `_last_battery_time` is reset but `_filtered_voltage` is not, so post-reconnect readings are blended with stale values.
## Issue Context
Reconnect is an explicit supported behavior in `RosbridgeClient`; battery percentage is a core UI/audit signal.
## Fix Focus Areas
- src/robocoop_backend/robocoop_backend/adapters/rosbridge_adapter.py[45-51]
- src/robocoop_backend/robocoop_backend/adapters/rosbridge_adapter.py[176-205]
- src/robocoop_backend/robocoop_backend/adapters/rosbridge_adapter.py[229-235]
### Suggested implementation direction
- Set `self._filtered_voltage = None` in `_on_bridge_reconnected()` and/or `disconnect()` so the next battery reading re-initializes the filter from the first new voltage sample.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

@KelianHalleray KelianHalleray added documentation Improvements or additions to documentation enhancement New feature or request Refactor labels May 21, 2026
@KelianHalleray KelianHalleray requested a review from damnthonyy May 21, 2026 07:05
Comment on lines +81 to +94
def _check_velocity(self, velocity: float) -> None:
if velocity > self.velocity_warning_threshold:
if self.audit_service and not self._velocity_alert_sent:
self._velocity_alert_sent = True
self.audit_service.record(AuditEvent(
action="velocity.high",
actor="system",
payload={
"velocity": round(velocity, 2),
"threshold": self.velocity_warning_threshold,
},
))
else:
self._velocity_alert_sent = False

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Reverse speed not alerted 🐞 Bug ≡ Correctness

TelemetryService._check_velocity() only triggers velocity.high when velocity > threshold, so
reverse motion (negative velocity) above the threshold magnitude will never emit the audit event.
This contradicts the documented behavior that the alert is based on linear speed exceeding a
threshold.
Agent Prompt
### Issue description
`TelemetryService._check_velocity()` uses a signed comparison (`velocity > threshold`). Negative velocities (reverse motion) will not trigger `velocity.high`, even though the feature is documented as a speed threshold.

### Issue Context
Audit docs describe `velocity.high` as “Linear speed exceeds threshold”, which implies magnitude rather than sign.

### Fix Focus Areas
- src/robocoop_backend/robocoop_backend/modules/robot/telemetry_service.py[81-94]

### Suggested fix
- Compare `abs(velocity)` to `velocity_warning_threshold`.
- Consider storing/reporting the magnitude in the audit payload (or include both `velocity` and `abs_velocity`), but at minimum ensure reverse motion triggers the event.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +52 to +60
async def _run(self) -> None:
hue = 0.0
try:
while True:
for led in range(self.led_min, self.led_max + 1):
r, g, b = self._hsv_to_rgb255(hue)
await self._publish_color(r, g, b, float(led))
hue = (hue + self.hue_step) % 360.0
await asyncio.sleep(self.frame_delay)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Chenillard can busy-spin 🐞 Bug ➹ Performance

RgbChenillard._run() only awaits inside the for led in range(...) loop; if led_min > led_max due
to config, the range is empty and the surrounding while True spins without any await. This can peg
a CPU core and block the asyncio event loop.
Agent Prompt
### Issue description
`RgbChenillard._run()` awaits only inside the LED loop. If the configured LED range is invalid (`led_min > led_max`), the `for` loop does zero iterations and the `while True` loop runs without yielding, causing a tight busy-loop.

### Issue Context
`led_min` / `led_max` are loaded directly from config and passed into `RgbChenillard`.

### Fix Focus Areas
- src/robocoop_backend/robocoop_backend/modules/robot/rgb_chenillard.py[52-60]
- src/robocoop_backend/robocoop_backend/adapters/factory.py[68-75]

### Suggested fix
- Validate config in `RgbChenillard.__init__` (or at startup) and raise a clear error if `led_min > led_max`, **or** swap/clamp them.
- Additionally (defensive), ensure the `while True` loop always yields at least once per iteration, e.g. add an `await asyncio.sleep(self.frame_delay)` after the `for` loop when it produced no frames.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@damnthonyy

Copy link
Copy Markdown
Member

@copilot resolve the merge conflicts in this pull request

@damnthonyy damnthonyy closed this May 21, 2026
@damnthonyy damnthonyy reopened this May 21, 2026
@qodo-code-review

qodo-code-review Bot commented May 21, 2026

Copy link
Copy Markdown

Persistent review updated to latest commit d39fded

Comment on lines 83 to +99
async def subscribe(self, topic: str, msg_type: str, callback: Callable) -> None:
if not self._websocket:
logger.error("Cannot subscribe: not connected")
return

sub_id = f"sub_{topic.replace('/', '_')}"
self._subscribers[topic] = callback
self._subscription_ids[topic] = sub_id
try:
await self._websocket.send(json.dumps({
"op": "subscribe", "topic": topic, "type": msg_type, "id": sub_id,
}))
logger.info(f"Subscribed to {topic}")
except Exception as e:
logger.error(f"Subscribe failed for {topic}: {e}")

await self._send_json({
"op": "subscribe",
"topic": topic,
"type": msg_type,
"id": sub_id,
})

logger.info(f"Subscribed to {topic}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Subscribe logs false success 🐞 Bug ☼ Reliability

RosbridgeClient.subscribe() logs "Subscribed" unconditionally even if the WebSocket send fails,
because _send_json() swallows exceptions and doesn’t signal failure. This can leave the backend
believing critical telemetry subscriptions are active while rosbridge never received them, with
misleading logs.
Agent Prompt
## Issue description
`RosbridgeClient.subscribe()` logs success even if the underlying `send()` failed, because `_send_json()` catches and logs exceptions and then returns normally. This creates a false-success state where callbacks are registered and logs say “Subscribed”, but rosbridge never received the subscribe frame.

## Issue Context
Subscriptions are the backbone of battery/odom telemetry; misleading success logs make outages hard to detect and can look like downstream parsing bugs.

## Fix Focus Areas
- src/robocoop_backend/robocoop_backend/adapters/rosbridge_client.py[83-145]

### Suggested implementation direction
- Make `_send_json()` either:
  - raise on send failure, OR
  - return `bool` success/failure.
- In `subscribe()`:
  - only log “Subscribed to …” when send succeeded,
  - and consider only storing `_subscribers/_subscription_ids` after a successful send (or remove them if send fails).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +50 to +61
async def stop(self) -> None:
self._running = False

if self._task:
self._task.cancel()
try:
await self._task
except asyncio.CancelledError:
pass

self._task = None
logger.info("Teleop watchdog stopped")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Watchdog stop doesn’t halt 🐞 Bug ☼ Reliability

TeleopWatchdog.stop() cancels the watchdog loop without sending a zero-velocity command even if the
last teleop command was non-zero, and BackendContext.disconnect() stops the watchdog before
disconnecting the adapter. This breaks the deadman safety guarantee during graceful shutdown/restart
because the last commanded velocity is never actively cleared.
Agent Prompt
## Issue description
Stopping the watchdog currently only stops the monitoring task; it does not publish a final stop command when the last command was non-zero. Since `BackendContext.disconnect()` stops the watchdog as part of shutdown, the system can shut down without ever sending a final zero velocity.

## Issue Context
The PR’s stated purpose is teleop safety via a deadman watchdog. A graceful shutdown/restart is a common operational path and should be safe too.

## Fix Focus Areas
- src/robocoop_backend/robocoop_backend/modules/robot/teleop_watchdog.py[50-79]
- src/robocoop_backend/robocoop_backend/app/backend_context.py[72-82]

### Suggested implementation direction
- In `TeleopWatchdog.stop()` (or in `BackendContext.disconnect()` before stopping/cancelling tasks), if the last command was non-zero, call `adapter.send_velocity(ZERO_TELEOP_COMMAND)` once.
- Wrap the send in a try/except and log if it fails (don’t block shutdown).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@damnthonyy damnthonyy merged commit 42c8379 into development May 21, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request Refactor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants