Skip to content

Bind one FOCAS handle per path instead of mutating path state - #124

Open
deadman96385 wants to merge 1 commit into
Ladder99:mainfrom
deadman96385:handle-per-path
Open

Bind one FOCAS handle per path instead of mutating path state#124
deadman96385 wants to merge 1 commit into
Ladder99:mainfrom
deadman96385:handle-per-path

Conversation

@deadman96385

Copy link
Copy Markdown

Summary

Platform holds a single library handle and calls cnc_setpath on it before every per-path read, mutating the controller-global path pointer on each sweep. This PR gives each path its own dedicated handle, bound once, so selecting a path becomes a handle swap rather than a global state mutation.

Background

This is the root cause behind #92 ("When option stay_connected=true, multi-path machines get stuck on last path") and its downstream report #103 (a DMG Mori 31i-B where only the last path's data flowed after a restart). Both were addressed by the "reset path on every iteration" workaround — re-issuing SetPath(0) each collect cycle to stop the shared handle from drifting to the last path.

That workaround masks the problem rather than removing it: the driver still keeps one handle and re-points it with cnc_setpath on every path switch, so the controller-global path pointer is shared across all reads and has to be defensively reset.

FOCAS actually supports a cleaner model. A handle is owned by the thread that created it, and FANUC explicitly permits one thread to hold multiple handles for the same CNC — binding each to a path — while the library still opens only a single TCP connection per process. It's quoted in the header comment of Platform.cs:

▎ "...on the 2 path control system, it is possible that the thread on an application gets two library handles for the same CNC ... and allocates the individual path to each handle."

What this changes

Platform now keeps a Dictionary<short, ushort> of path → handle plus the primary connection handle:

  • SetPath(path) returns the cached handle for the path if one exists; otherwise it allocates a handle (reusing the primary connection handle for the first path so single-path machines still use exactly one handle), binds it once with cnc_setpath, and caches it. Later selections are a pure handle swap — no native call, and no shared path state that can drift between reads. path_no <= 0 selects the primary handle.
  • Connect records the newly-allocated handle as the primary and clears any stale per-path handles (FANUC drops every handle for a node when its TCP connection closes).
  • Disconnect frees every per-path handle exactly once.
  • FanucExtendedStrategy keeps the SetPath(0) call (it selects the primary handle before GetPath), but its "reset to first path / issue When option stay_connected=true, multi-path machines get stuck on last path. #92" comment is updated — the reset is no longer load-bearing, since path state can no longer leak between reads.

Because each handle carries its own path selection, the stay_connected=true multi-path scenario from #92/#103 is fixed structurally: handles persist across sweeps already bound to their paths, with no cnc_setpath re-issuing and nothing to reset.

Compatibility

  • The SetPath return object keeps the same shape (method, invocationMs, success, rc, request.cnc_setpath.path_no, response), so focas_perf accounting and the strategy's path markers are unaffected.
  • Single-path controllers: no change in handle count or cost — the primary handle is reused for path 1.
  • Multi-path controllers: N handles over the single shared TCP connection, each permanently bound to its path.

Platform held a single library handle and called cnc_setpath on it before
every per-path read, mutating controller-global path state on each sweep.
FANUC permits one thread to hold multiple handles for the same CNC (the
library still opens only one TCP connection per process) and bind each
handle to a path; this is the documented multi-path approach.

Keep a dedicated handle per path: allocate and bind it with a single
cnc_setpath the first time the path is selected, then just switch the
selected handle on later reads. The single-path case is unchanged in cost
because the primary connection handle is reused for the first path.
Connect records the primary handle and clears stale ones; Disconnect frees
every per-path handle exactly once.

This eliminates the shared path-pointer races that the previous
reset-to-first-path workaround only masked.
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.

1 participant