Refactor codebase structure #5
Conversation
* feat(sim): add Docker environment for ROS2 Humble + Gazebo Fortress with GUI access * docs(README): update launch instructions for lunch Docker Co-authored-by: Antoine <damnthonyy@users.noreply.github.com> * init(backend): initialize backend architecture * feat(init): Add backend's TODO * feat(init): update readme * feat(docs): adding structure diagram * fix(docs): update diagram's alt 👀 * feat(websockets): Create websocket server with Ping/Pong handler * feat(readme): update readme to explain websocket server * fix: guard asyncio.run behind __main__ to prevent server start on import * feat(archi): refactor into multi modules * refactor(readme): refactor repository structure Co-authored-by Antoine Mahassadi <dmantoinepro@gmail.com> * feat(back): create packages for ROS2 exec * fix(back): resolve threads * resolve threads --------- Co-authored-by: Kelian <kelianhalleray@gmail.com> Co-authored-by: Antoine <damnthonyy@users.noreply.github.com>
- Add unit tests for event formatting, sinks, state store, and telemetry service - Implemented unit tests for EventFormatter to ensure required fields, action, actor, ID, timestamp, and payload flattening. - Added tests for ConsoleSink and FileSink to verify logging behavior and file writing. - Created tests for RobotState and RobotStateStore to validate state management and updates. - Developed tests for TelemetryService to check state updates and connection event emissions. - Removed outdated test files for MessageRouter, MissionStateMachine, ModeManager, RobotStateStore, Schema Validation, TeleopService, and WatchdogService. - Introduced configuration loading tests to validate YAML parameter handling and environment overrides. - Implemented Config class to manage configuration loading and access with typed getters. - Updated setup.py to include testing dependencies. - Added configuration files for backend, common, mock, and real environments. Assisted-by : Claude :)
Review Summary by QodoRefactor codebase structure: remove stubs, flatten architecture, implement battery monitoring pipeline
WalkthroughsDescription**Major codebase refactor and cleanup:** • Removed ~65 stub files (empty modules, unused utilities, dead test placeholders) totaling ~400 lines of TODO code • Flattened folder structure: promoted adapters/ to top-level, removed infrastructure/ wrapper • Simplified RobotState from 7 fields to 3 (is_connected, battery_level, last_updated) matching actual scope • Renamed adapters to consistent *_adapter.py convention; removed unused m3pro and sim adapters • Converted AdapterFactory class to create_adapter() function for cleaner instantiation **Core implementation:** • Added .env support with stdlib dotenv loading and Config.load() YAML+environment merging • Implemented RosbridgeClient WebSocket transport layer with primary/secondary URL fallback and exponential backoff • Built RosbridgeAdapter with battery topic subscription, voltage-to-percentage conversion (9V→0%, 12.6V→100%), and watchdog timeout detection • Created TelemetryService to receive adapter data, update state store, emit connection events, and broadcast to WebSocket clients • Implemented AuditService with in-memory event history, sink dispatch, and newest-first retrieval • Added WebSocketHandler with client lifecycle management, message routing, and broadcast capabilities • Created BackendContext singleton for dependency injection and service wiring **Testing and documentation:** • Comprehensive unit test coverage for adapters, services, configuration, and WebSocket handling • Integration tests wiring real objects end-to-end (adapter → telemetry → audit → state store) • Real integration tests for live rosbridge server (marked @pytest.mark.real, skipped in CI) • Complete module documentation (adapters, app, audit, robot, configuration) with extension guides • Project README with architecture diagram, setup instructions, and roadmap • GitHub PR template for consistent contribution workflow **Configuration and deployment:** • Created common.params.yaml with WebSocket, battery thresholds, and watchdog settings • Added environment-specific configs (mock.params.yaml, real.params.yaml) • Docker setup for ROS 2 Humble + Gazebo simulation environment with VNC/noVNC access • Bash launch scripts for backend server with environment selection • ROS 2 launch file placeholders for mock, simulation, real hardware, and debug modes Diagramflowchart LR
ROS["ROS Topics<br/>/battery"]
RC["RosbridgeClient<br/>WebSocket"]
RA["RosbridgeAdapter<br/>Voltage→%<br/>Watchdog"]
TS["TelemetryService<br/>State Updates<br/>Events"]
SS["StateStore<br/>3-field Model"]
AS["AuditService<br/>Event History"]
WS["WebSocketHandler<br/>Client Broadcast"]
ROS -->|subscribe| RC
RC -->|battery msg| RA
RA -->|telemetry| TS
TS -->|update| SS
TS -->|emit| AS
TS -->|broadcast| WS
AS -->|history| WS
File Changes1. src/robocoop_backend/robocoop_backend/tests/unit/adapters/test_rosbridge_adapter.py
|
Code Review by Qodo
1. Missing adapter command methods
|
| elif msg_type == MSG_TELEOP_MOVE: | ||
| self.context.adapter.send_velocity(message.get("data")) | ||
| elif msg_type == MSG_EMERGENCY_STOP: | ||
| logger.warning("Emergency stop via WebSocket") | ||
| self.context.adapter.emergency_stop() | ||
| self.context.audit_service.record( | ||
| AuditEvent(action=MSG_EMERGENCY_STOP, actor="dashboard", payload={}) | ||
| ) |
There was a problem hiding this comment.
1. Missing adapter command methods 🐞 Bug ≡ Correctness
WebSocketHandler.handle_message() calls adapter.send_velocity() and adapter.emergency_stop(), but RobotAdapter/MockRobotAdapter/RosbridgeRobotAdapter do not define these methods, so those message types will trigger AttributeError and the command/audit won’t execute.
Agent Prompt
## Issue description
`WebSocketHandler.handle_message()` routes `teleop.move` and `emergency_stop` to `context.adapter.send_velocity(...)` and `context.adapter.emergency_stop()`, but the adapter interface/implementations don’t provide these methods. This causes runtime failures (caught/logged) and prevents emergency stop from being recorded after the exception.
## Issue Context
- The adapter abstraction currently only defines `is_connected()`.
- Both concrete adapters shown in this PR only implement `is_connected()` (+ connect/disconnect for rosbridge).
## Fix Focus Areas
- src/robocoop_backend/robocoop_backend/adapters/base_adapter.py[1-6]
- src/robocoop_backend/robocoop_backend/adapters/mock_adapter.py[1-9]
- src/robocoop_backend/robocoop_backend/adapters/rosbridge_adapter.py[12-107]
- src/robocoop_backend/robocoop_backend/app/websocket_handler.py[55-73]
## Suggested fix
1. Extend `RobotAdapter` with the command methods actually used by the WebSocket API (at minimum `send_velocity(...)` and `emergency_stop()`), ideally with clear typing (sync vs async).
2. Implement them in `MockRobotAdapter` as safe no-ops.
3. Implement them in `RosbridgeRobotAdapter` (even if initially no-op with logged warning) or remove/disable the message routes until implemented.
4. Update `WebSocketHandler` to `await` these methods if you make them async.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
- enhance battery level calcul - update tests - update readme Co-authored-by: Kelian <kelianhalleray@gmail.com>
…er, update BackendContext for dependency injection Co-authored-by: Kelian <kelianhalleray@gmail.com>
TroubleShooting
refactoring initiative
Summary
Full codebase refactor: removed ~65 stub files, flattened folder structure, renamed adapters to a consistent convention, simplified the state model to only what's actually implemented, added
.envsupport, and documented every module.Description
The codebase had accumulated ~400 lines of pure TODO placeholders across 65+ files (empty modules for mission, mode, safety, ROS nodes, schemas, utils, tests). None of these were wired up or called anywhere. Alongside the dead weight, several structural issues made the code hard to navigate:
infrastructure/adapters/with inconsistent naming (mock_robot_adapter.py,adapter_factory.py)m3proandsimlisted as separate adapters despite both communicating through rosbridgeRobotStatehad 7 fields (position_x,position_y,linear_velocity,angular_velocity,mode…) with no ROS topic feeding them.envfile — environment selection was done by manually exporting shell variablesbackend.params.yamlexisted but was never loaded byConfig.load()This PR cleans all of that up to match the actual current scope: battery monitoring via
/battery→ WebSocket push to dashboard.What's Changed
Deleted (~65 files removed)
app/auth.py,app/rate_limiter.py,app/message_router.py— stubs, never calledinfrastructure/— entire folder removed (adapters promoted, ROS nodes and schemas deleted)modules/mission/,modules/mode/,modules/safety/— unimplemented modulesmodules/robot/domain/,modules/audit/domain/— unnecessary subdirectory nestingutils/logger.py,utils/enums.py,utils/ids.py,utils/time.py,utils/thread_safe_lock.py— empty stubstests/— all stub test files (no actual assertions)list_ros_topics.py,test_ros_bridge_direct.py,test_ros_bridge_integration.py,TESTING_PHASE3.mdAdapters — restructured
infrastructure/adapters/→adapters/(top-level, no infrastructure wrapper)*_adapter.pyconvention:mock_adapter.py,rosbridge_adapter.py,base_adapter.pyAdapterFactoryclass →create_adapter()function infactory.pym3pro_adapterandsim_adapterremoved — both communicate via rosbridge, not directbase_adapter.pystripped to one abstract method:is_connected()(removed prematuresend_velocity,navigate_to, etc.)robot_state_storeparam removed from adapter — telemetry_service owns state updatesmodules/robot — simplified
robot_state_store.pyrenamed tostate_store.py,RobotStateconverted to dataclassRobotStatereduced to 3 fields:is_connected,battery_level,last_updated(only what/batteryfeeds)audit_event.pymoved fromaudit/domain/audit_event.py→audit/audit_event.py(flat)Configuration
Config.load()now auto-loads.envvia stdlib (no external dependency).envand.env.examplecreated at project rootbackend.params.yamlmerged intocommon.params.yaml(it was never loaded)m3pro_topics.yaml,security.params.yaml,sim.params.yamldeleted (stubs)app/ — contracts + constants
contracts.pyadded: full WebSocket API documentation + message type constantswebsocket_handler.pyupdated to use constants fromcontracts.pyinstead of raw stringsDocs
README.mdrewritten with current structure, setup, running instructions, roadmapadapters/README.md— how to subscribe/publish to a ROS topic, step by stepmodules/robot/README.md— state model, telemetry pipeline, how to add a fieldmodules/audit/README.md— event types, how to record an event, how to add a sinkapp/README.md— startup sequence, message routing table, contracts usage rulessrc/robocoop_bringup/config/README.md— YAML loading order, how to add an environmentKey files to review:
adapters/rosbridge_adapter.py,adapters/factory.py,modules/robot/state_store.py,app/contracts.py,app/backend_context.pyHow to Test