Skip to content

Connection recovery and shutdown

After following the Navigation integration guide or the Axis Streaming quickstart, use this recipe to show connection status, recover when Rotatrix restarts, and stop pending work before your application releases its resources. Those guides include client creation and initial connection; this page assumes that client already exists.

Connection management is shared by both interfaces. All four SDKs use OpenAxisConnectionManager. The connection owner reconnects the same client and announces current application metadata.

IntegrationWhat stays attached across retriesWhat is announced again
Axis StreamingAxis-name and frame callbacksTags, subscribed axes and focus
NavigationNavigationSession and its application adaptersTags, navigation capability and focus
BothBoth sets of handlers on the same clientNavigation capability plus the axis subscription, tags and focus

Axis Streaming has no navigation session or pivot cleanup. Navigation keeps its session attached during retries and closes it only when the integration stops.

The manager reports connecting, ready, retrying and stopped. ready means connection startup and metadata announcements have completed. The integration uses these notifications to update the application’s status display.

The excerpts below show status handling in the Navigation demos. Their connection manager and status callbacks also apply to Axis Streaming: retain its input callbacks and axis subscription instead of adding navigation objects. The TypeScript streaming example shows the same manager with an axis subscription and no navigation session.

MyOpenAxisIntegration.start() creates the manager with MyConnectionStatus(app) as its status callback. Its client and session are retained for the integration’s lifetime; self._metadata reads the current application state, and the registered callbacks publish later changes:

self.connection = OpenAxisConnectionManager(
client, metadata=self._metadata, on_state=MyConnectionStatus(app),
)
self.client, self.session = client, session
self._metadata_tasks = set()
app.on_navigation_changed = self._metadata_changed
app.on_focus_changed = self._metadata_changed
app.on_operation_changed = self._operation_changed
self.networking = asyncio.create_task(self.connection.run())

The manager invokes this callback whenever connection state changes. The handler uses the application API to display that state and any retry delay:

class MyConnectionStatus:
def __init__(self, app):
self.app = app
def __call__(self, state, error, delay):
if state == ConnectionManagerState.RETRYING:
self.app.set_status(f'Reconnecting in {delay:.1f}s' + (f': {error}' if error else ''))
else:
self.app.set_status(state.value)

The metadata provider describes current capabilities, tags, axis subscriptions and focus. The dynamic tags recipe explains how application events refresh it while connected.

Keep the connection owner alive while Rotatrix is unavailable. It retries and publishes a fresh metadata snapshot after reconnecting. If a user changes tools or focus while offline, announce that current state rather than replaying old transitions. See application context and focus for the event wiring.

For Axis Streaming, replace the saved axis order when a new axis announcement arrives and reset any rate-integration timing at connection and gesture boundaries. Otherwise an idle interval can become a large movement on the first new frame. For Navigation, the session invalidates old connection work; retain the same session rather than attaching another on each reconnect.

Networking and queued callbacks can still refer to application resources. The integration stops new events and waits for pending work before destroying those resources, so callbacks cannot access a closed application.

For either interface, detach native event producers, stop networking, and await the run task and any outstanding metadata updates. Retain objects referenced by callbacks until that work finishes.

LanguageStop the connection owner
PythonAwait connection.stop() and the retained networking task. If an owned task has not started yet, cancel and await it instead. The quickstart’s console loop is cancelled by Ctrl+C.
C#Await connection.StopAsync() and the task returned by StartAsync(), or cancel the quickstart’s stopping token. Keep the UI dispatcher running while queued work needs it.
TypeScriptAwait connection.stop() and the retained run promise. The streaming helper returns a cleanup function that also removes focus listeners and awaits metadata updates.
C++Call connection.stop() on the application thread before destroying callback state; it joins networking and may finish pending callbacks synchronously.

An Axis Streaming integration can then release its input callbacks and native resources. A Navigation integration also closes its session and drains native cleanup while its scheduler, adapters and viewport still exist.

The following complete stop methods include the shared transport teardown plus Navigation-specific edit cancellation, session closure and graphics cleanup.

Before destroying its window, the application’s run loop awaits MyOpenAxisIntegration.stop(). Its fields refer to the NavigationSession, networking task and updates retained during startup:

async def stop(self):
self.app.on_navigation_changed = lambda: None
self.app.on_focus_changed = lambda: None
self.app.on_operation_changed = lambda: None
self.app.finish_object_edit(False)
updates = tuple(self._metadata_tasks)
for task in updates:
task.cancel()
# Cancel the owned run task even if shutdown arrives before it starts.
self.networking.cancel()
await asyncio.gather(self.networking, *updates, return_exceptions=True)
self._metadata_tasks.clear()
self.session.close()
self.session.drain()
self.app.on_object_changed = lambda: None
self.app.on_operation_changed = lambda: None
self.app.on_camera_changed = lambda: None
self.app.toggle_diagnostics = lambda: None
self.app.diagnostic_frame = lambda: None
self.app.diagnostics_enabled = lambda: False
self.app.set_status('OpenAxis detached')

See connection management for retry policy and ownership. The application separately releases its native event handlers when the window closes.

For a faster edit and test loop, use this teardown path in a development reload command. See reloading during development for choosing a reload mechanism and defining which changes require an application restart.

  • Start the application before Rotatrix. It should connect when Rotatrix becomes available.
  • Restart Rotatrix while the application remains open. The integration should reconnect and announce current state.
  • For Axis Streaming, confirm a new axis announcement is processed before frames and reconnecting does not integrate the disconnected interval.
  • Close the application during a retry or metadata update. Networking should stop, with no later callbacks accessing released resources; Navigation overlays should disappear.