I'm trying to summarize a high-level view of tool information flow in EMC2. Please correct/extend as needed. 'GUI' is synonymous with Axis for now.
This table is shared by means of the emcStatus structure, which conceptually is shared memory for the purpose of this discussion (it is implemented with periodic synchronous message passing with pretty much the same effect).
iocontrol is the authoritative source for tool information and reads/writes the external tool table file. It works directly from/to the emcStatus.tool.toolTable array.
The NML messages manipulating tool information are:
When a UI needs to execute a code, for instance G10 L1 (set tool table), this command is sent from the UI, to task, as an EMC_TASK_PLAN_EXECUTE message. The task executor passes this message to the milltask interpreter instance for execution. As a result, an EMC_TOOL_SET_OFFSET message is queued to task, which in turn sends it to iocontrol, which acts upon it, updates tool.toolTable in emcStatus, and writes the tool table to the filesystem. Other components like the UI pull the updated emcStatus structure and now have updated information.
Conceptually emcStatus is managed by the task executor, and the other components pull a copy when needed, or "push" in the case of iocontrol.
On a lower level, an interpreter instance uses the GET_EXTERNAL_TOOL_TABLE() canon call to obtain a single tool table entry, which is currently indexed by pocket.
Depending on the environment the canon layer may access tool information as follows:
"tool change" from an interpreter perspective happens at different points in time: in the GUI during preview generation, and in milltask during execution.
During "parse_file", the GUI retrieves the current tooltable lib/python/rs274/interpret.py, Class StatMixin? method __init__()) and holds it as a member variable of type list of tools.
During preview, the GUI accesses a tool entry by means of the 'get_tool' method and 'changes tools' by means of the change_tool method (lib/python/rs274/interpret.py Class StatMixin?). The result of this method is only significant for the UI, and causes no external changes. Also, the fact that a tool change must be waited for is ignored with the UI (like probe, or wait for HAL pin) since it's a simulated process which should run as fast as possible.
For the milltask interpreter instance, which runs in execution-time context, a change operation is a 'queue buster': task stops readahead until the change is complete and emcStatus is updated by iocontrol, reflecting updated toolInSpindle? and toolTable configuration. Then the task executor calls the interpreter.synch() method, which directs the interpreter its internal view with the external world model. Among others, that causes calls to GET_EXTERNAL_TOOL_TABLE() to re-read tool_table from milltask's emcStatus structure entry-by-entry.
iocontrol modifies several fields in emcStatus - it changes toolInSpindle?, and swaps the current and prepared pocket, effectively swapping two entries in the toolTable array. This is managed by iocontrol, depending on the changer type (random or nonrandom).
In the GUI, the same process is mirrored during preview generation (StatMixin?.change_tool).