Skip to content

refactor(motor-control): clarity pass - public helpers + named constants (no breaking changes) - #763

Merged
finger563 merged 5 commits into
mainfrom
refactor/motor-control-clarity
Sep 3, 2026
Merged

refactor(motor-control): clarity pass - public helpers + named constants (no breaking changes)#763
finger563 merged 5 commits into
mainfrom
refactor/motor-control-clarity

Conversation

@finger563

Copy link
Copy Markdown
Contributor

Second in the review follow-up stack (bugs → clarity → consistency), on top of #762. Additive and internal only — no existing signature changes (the breaking name/arity/encoder-sign alignment is the separate consistency PR).

From the review §01B (detail:: leaks), §02, §03:

  • Surface the helpers the example/users had to reach into detail:: for:
    • Ds402Drive::to_string(State) and Ds402Drive::state_from_statusword(u16)
    • CanopenClient::abort_code_to_string(u32)
    • The canopen example now uses Ds402Drive::to_string(...) instead of espp::detail::ds402::state_to_string(...).
  • Mcp266: surface arrival/state without decoding the raw statusword — get_state(Axis, State&, ec) and is_target_reached(Axis, bool&, ec) forwarding to the axis Ds402Drive.
  • Mcp266: named constants instead of magic numbers0x6060 → OBJ_MODES_OF_OPERATION, 0x607D → OBJ_SOFTWARE_POSITION_LIMIT (new in canopen_core), both via apply_axis_offset() (which validates the offset). kDefaultPositionP written in decimal (15491 ≈ 15.1 ×1024) and the 25 ms mode-settle delay named kModeSettle.

Verified

  • canopen + mcp266 host tests pass.
  • canopen + mcp266 examples build clean on IDF v6.0.1.

Deliberately deferred to the consistency PR (all breaking): ec-position in configure_position_loop, set_position_limitsset_software_position_limits rename, reset_estope_stop_reset, the drive_duty/drive_speed arity collision, and encoder-sign alignment.

🤖 Generated with Claude Code

finger563 and others added 2 commits September 2, 2026 21:44
… API change)

From the basicmicro/canopen/mcp266 design review. All fixes preserve the public
API and the "true => ec cleared" contract.

- basicmicro set_velocity_pid: route the P/I/D gains through scale_pid_gain()
  (rounds; guards negative -> uint32 wrap and NaN/inf -> UB in std::llround) like
  the position path already does — a raw static_cast of the float*scale product
  bypassed those guards on the more commonly tuned loop.
- basicmicro read_status: the 32-bit fast path returned true without clearing the
  caller's ec (it used a local ec32), so a caller reusing one std::error_code saw
  success reported as a stale error. Clear ec before returning.
- canopen sdo_upload: a conformant server may leave the SDO size-indicated bit
  clear on an expedited upload (all 4 data bytes valid, core reports len == 4).
  The exact-width check then failed read_u8/read_u16 with a spurious
  protocol_error. When the size is NOT indicated, let the caller's requested
  width govern and take the low N bytes; keep the strict check when a size IS
  indicated (a genuine truncation/oversize is still rejected).
- canopen last_abort_code(): reset the cached abort code at the start of every
  transaction so it cannot report a stale code from a much earlier failure.
- canopen node_id: validate to 1-127 in the constructor (0 = broadcast/
  unconfigured would break 0x580/0x600 addressing); clamp to 1 with a loud error.

Verified: basicmicro + canopen host tests pass (cores unchanged); basicmicro and
canopen examples build clean on IDF v6.0.1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…amed constants

From the design review (§01B / §02 / §03). Additive and internal only — no
existing signature changes (the breaking name/arity alignment is the separate
consistency pass).

- Surface the helpers the example/users previously had to reach into detail:: for:
  Ds402Drive::to_string(State) and state_from_statusword(u16); CanopenClient::
  abort_code_to_string(u32). The canopen example now uses Ds402Drive::to_string
  instead of espp::detail::ds402::state_to_string.
- Mcp266: add get_state(Axis, State&, ec) and is_target_reached(Axis, bool&, ec)
  (forwarding to the axis Ds402Drive), so a motor-control user gets arrival/state
  without decoding the raw statusword themselves.
- Mcp266: replace hardcoded CiA 402 indices with the canonical constants +
  apply_axis_offset() (which validates the offset): 0x6060 -> OBJ_MODES_OF_OPERATION,
  0x607D -> new OBJ_SOFTWARE_POSITION_LIMIT in canopen_core. Express kDefaultPositionP
  in decimal (15491, ~15.1 x1024) and name the 25 ms mode-settle delay (kModeSettle).

Verified: canopen + mcp266 host tests pass; canopen + mcp266 examples build clean
on IDF v6.0.1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 3, 2026 02:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Pull request overview

This PR improves clarity and usability by promoting previously-detail:: helper functionality to public APIs and replacing magic numbers with named constants, while keeping existing public signatures intact.

Changes:

  • Added public helpers for CiA-402 state decoding/stringification and CiA-301 SDO abort code stringification.
  • Added MCP266 convenience accessors for decoded drive state and “target reached”.
  • Replaced raw object-index literals and a hardcoded delay with named constants.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
components/mcp266/include/mcp266.hpp Uses named OD constants + axis offset helper; adds state/arrival convenience methods; names the mode-settle delay and clarifies default P gain.
components/canopen/include/ds402.hpp Exposes public to_string(State) and state_from_statusword(u16) helpers.
components/canopen/include/detail/canopen_core.hpp Introduces a named constant for software position limits object index (0x607D).
components/canopen/include/canopen_client.hpp Exposes public abort_code_to_string(u32) helper.
components/canopen/example/main/canopen_example.cpp Updates example to use the new public DS402 to_string helper.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread components/mcp266/include/mcp266.hpp Outdated
Comment thread components/mcp266/include/mcp266.hpp Outdated
Comment thread components/canopen/include/detail/canopen_core.hpp Outdated
Comment thread components/canopen/include/ds402.hpp
Doc/readability follow-ups from the #763 review (no code behavior change):
- mcp266.hpp: split the combined `\param`/`\return` doc lines onto one tag per
  `///` line throughout — Doxygen only parses the first tag on a line, so the
  compact form dropped every parameter after the first from the generated docs.
- canopen_core.hpp: spell out the OBJ_SOFTWARE_POSITION_LIMIT comment as
  "i32; subindex 1 = min, 2 = max" instead of the cryptic "i32:1/:2".
- ds402.hpp: document that Ds402Drive::to_string() returns a static-lifetime
  string literal (non-owning, never freed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The changes are additive, correctly delegate to existing implementations, and preserve prior behavior.

Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Per review, add fmt::formatter specializations so application code can print the
CANopen / DS402 enums directly -- e.g. logger.info("Drive state: {}", state) --
instead of calling a to-string helper inline at every call site, matching the
formatter pattern used elsewhere in espp (e.g. wifi_format_helpers.hpp).

New canopen_format_helpers.hpp covers Ds402Drive::State (reuses state_to_string),
Ds402Drive::OperatingMode, CanopenClient::NmtState, and CanopenClient::NmtCommand.
It is included from canopen_client.hpp, so ds402.hpp / mcp266 get it transitively.
Ds402Drive::to_string() is retained for non-fmt callers. The canopen example now
prints the drive state and heartbeat NMT state via {}.

Builds clean: canopen example on IDF v6.0.1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Base automatically changed from fix/motor-control-bugs to main September 3, 2026 16:36
@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

✅Static analysis result - no issues found! ✅

@finger563
finger563 merged commit d353459 into main Sep 3, 2026
156 checks passed
@finger563
finger563 deleted the refactor/motor-control-clarity branch September 3, 2026 19:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants