Skip to content

task: program_units follows the executing G20/G21, like gcodes (#3627) - #4686

Open
tzuohann wants to merge 2 commits into
LinuxCNC:masterfrom
tzuohann:fix-program-units-3627
Open

tzuohann wants to merge 2 commits into
LinuxCNC:masterfrom
tzuohann:fix-program-units-3627

Conversation

@tzuohann

@tzuohann tzuohann commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #3627.

While a program runs, stat.program_units should match gcodes. Instead it follows the interpreter's read-ahead:

G21
G1 X25 F300   (while this runs: gcodes says G21, program_units says inches)
G20           (the interpreter has already read this line)
M2

Why: gcodes comes from the executing move; program_units was set only when the interpreter read the G20.

Fix: emcTaskUpdate (src/emc/task/emctask.cc) sets programUnits from the active G20/G21, as suggested on the issue. This keeps all C readers (linuxcncrsh, emcsh, shcom) consistent. Canon's write stays for canterp. Idle behaviour is unchanged.

Test: tests/program-units fails without the fix (every sample wrong: 670/670, 774/774, 853/853) and passes with it.

Related: #3657.

…CNC#3627)

program_units was only written by canon USE_LENGTH_UNITS, when the
interpreter read a G20/G21 -- often far ahead of motion -- while gcodes
reports the executing move. emcTaskUpdate now sets programUnits from the
active G20/G21 it has just computed. Canon's write stays for interpreters
that report no G20/G21 (canterp).

tests/program-units runs a 5 s G21 move followed by G20 on simulated
motion and fails if program_units disagrees with gcodes during the move.

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

@grandixximo grandixximo 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.

The change looks right to me and fixes #3627: program_units now comes from the same active G20/G21 as gcodes, and nothing in task or canon reads programUnits back, so overwriting canon's read-ahead value is safe. Three timing points in the test below; @hdiethelm's parallel runs (#4669) turned up several tests that failed under load on exactly these patterns.

Comment thread tests/program-units/test-ui.py Outdated

c.state(linuxcnc.STATE_ESTOP_RESET)
c.state(linuxcnc.STATE_ON)
c.home(-1)

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.

The config has NO_FORCE_HOMING = 1, so the test doesn't need homing. wait_complete() returns before homing finishes, and the AUTO switch two lines down runs emcTaskAbort(), so whether it lands during homing or after depends on timing. Dropping the home, or waiting until s.homed reads all true, removes that.

Comment thread tests/program-units/test-ui.py Outdated
start = time.time()
while time.time() - start < 30:
s.poll()
if s.interp_state == linuxcnc.INTERP_IDLE and time.time() - start > 1:

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.

This assumes the program has started within 1 s of c.auto(). On a loaded machine it may not have, and the loop then exits with no samples. Waiting first for interp_state to leave INTERP_IDLE (with a timeout), then looping until it returns, avoids the fixed second.

Comment thread tests/program-units/test-ui.py Outdated
sys.exit(1)
print('samples while G21 executed: %d, program_units not mm in them: %d, end gcodes[5]=%d program_units=%d'
% (in_g21, mismatch, s.gcodes[5], s.program_units))
if in_g21 < 100:

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.

This needs the poll loop to run at 20 Hz or more for the whole 5 s move; with Python starved it can fall short. in_g21 == 0 is enough to show the move was seen.

…mple counts

Review (grandixximo): the config has NO_FORCE_HOMING = 1, so the home and its
wait_complete() only raced the AUTO switch; the 1 s guard assumed the program
had started within a second; 100 samples needed the poll loop to keep 20 Hz
for the whole move. Now: no homing, wait (10 s timeout) for interp_state to
leave IDLE, then sample until it returns; fail only if no sample saw the G21
move -- the mismatch count is the real check.

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

Copy link
Copy Markdown
Contributor Author

Thanks for the review. All three points are addressed in the new commit:

  • Homing (line 15): removed. The config has NO_FORCE_HOMING = 1, so the home and its wait_complete() only raced the AUTO switch.
  • Start of the program (line 35): the fixed 1 s is gone. The test now waits, with a 10 s timeout, for interp_state to leave INTERP_IDLE, then samples until it returns.
  • Sample count (line 54): fails only when no sample saw the G21 move (in_g21 == 0). The mismatch count does the real checking.

On a Pi 5 (aarch64, 6.12 PREEMPT_RT), the final version ran 5 times alone and 5 times with two CPU hogs pinned to the test's CPU; the version before the sample-count change ran 10 + 10. All passed. With the emctask.cc change reverted, the test fails as it should: all 966 samples taken during the G21 move show program_units not in mm.

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.

Python Module program_units returns wrong code

2 participants