:lang: en
:toc:

[[cha:tooldatabase]]
= Tool Database Interface

// Custom lang highlight
// must come after the doc title, to work around a bug in asciidoc 8.6.6
:ini: {basebackend@docbook:'':ini}
:hal: {basebackend@docbook:'':hal}
:ngc: {basebackend@docbook:'':ngc}

Tool data is conventionally described by a tool table file specified by an inifile setting: [EMCIO]TOOL_TABLE=tooltable_filename.
A tool table file consists of a text line for each available tool describing the tool's parameters, see <<sec:tool-table,Tool Table Format>>.

The tool database interface provides an alternative method for obtaining tool data via a separate program that manages a database of tools.

==  Interface

=== INI file Settings

INI file settings enable the (optional) operation of a user-provided tool database program:

[source,{ini}]
----
[EMCIO]
DB_PROGRAM = db_program [args]
----

When included, *db_program* specifies the path to a user-provided executable program that provides tooldata.
Up to 10 space-separated args may be included and passed to the *db_program* at startup.

[NOTE]
INI file settings for [EMCIO]TOOL_TABLE are ignored when a *db_program* is specified.

[NOTE]
The *db_program* may be implemented in any language currently supported in LinuxCNC (e.g., BASH scripts, Python or Tcl scripts, C/C++ programs)
as long as it conforms to the interface messages received on stdin and replied on stdout.
A *db_program* could manage data from a flat file, a relational database (SQLite for example), or other data sources.

=== *db_program* operation (v2.1)

When a *db_progam* is specified, operation is as follows:

. At startup, LinuxCNC starts the *db_program* and connects to its stdin and stdout.
. The *db_program* must respond by writing a single line acknowledgement consisting of a version string (e.g., "v2.1").
  No tools will be available if the version is not compatible with the LinuxCNC database interface version.
. Upon a successful acknowledgement, LinuxCNC issues a 'g' (*get*) command to request all tools.
  The *db_program* must respond with a sequence of replies to identify each available tool.
  The textual reply format is identical to the text line format used in conventional tool table files.
  A final response of "FINI" terminates the reply.
. The *db_program* then enters an event wait loop to receive commands that indicate that tool data has been changed by LinuxCNC.
  Tool data changes include:
  * a) spindle loading(T__n__ M6)/unloading(T0 M6)
  * b) tool parameter changes (G10L1P__n__ for example)
  * c) tool substitutions (M61Q__n__).

When a tool data change occurs, LinuxCNC sends a command to the *db_program* consisting of an identifying command letter followed by a full or abbreviated tool data line.
The *db_program* must respond with a reply to confirm receipt.
If the reply includes the text "'NAK'", a message is printed to stdout but execution continues.
The "'NAK'" message signifies a lack of synchronization between the *db_program* and LinuxCNC -- accompanying text should give an indication for the cause of the fault.

The commands issued for tool data changes are:

* "p" put data changes caused by G10L1, G10L10, G10L11 G-codes.  The tool data line will include all elements of a tool table text line.
* "l" spindle_load (T__n__M6). The tool data line includes only the 'T' and 'P' items identifying the relevant tool number and pocket number.
* "u" spindle_unload (T0M6).  The tool data line includes only the 'T' and 'P' items identifying the relevant tool number and pocket number.

[NOTE]
When a NON_RANDOM tool changer is specified using [EMCIO]RANDOM_TOOL_CHANGER=0 (the default),
the spindle_load command issued for T__n__M6 (or M61Q__n__) is: 'l T__n__ P0' (pocket 0 is the spindle).
The spindle_unload command issued for T0M6 is 'u T0 P0'.

[NOTE]
When a RANDOM tool changer is specified using [EMCIO]RANDOM_TOOL_CHANGER=1, a pair of spindle_unload/spindle_load commands are issued at each tool exchange.
The pair of commands issued for T__n__M6 (or M61Q__n__) are 'u T__u__ P__m__' followed by 'l T__n__ P0',
where __u__ is the current tool to be sent to pocket __m__ and __n__ is the new tool to load in the spindle (pocket '0').
By convention, a tool number of 0 is used to specify an empty tool,

=== Usage

Using a *db_program* does not change the way LinuxCNC operates but provides support for new database functionality for tool management.

For example, a *db_program* database application can maintain the operating hours for all tools by tracking each load/unload of a tool.
A machine could then have three 6 mm endmills in pockets 111, 112, and 113 with the database application programmed to assign tool number 110
to the 6 mm endmill with the fewest operating hours.
Then, when a LinuxCNC program requests tool 110, the database would specify the appropriate pocket based on tool usage history.

Tool data changes made within LinuxCNC ('p','u','l' commands) are pushed immediately to the *db_program* which is expected to synchronize its source data.
By default, LinuxCNC requests for tool data ('g' commands) are made at startup only.
A database program may update tool usage data on a continuous basis so long-lived LinuxCNC applications may benefit by refreshing the tool data provided by the *db_program*.
The G-code command *G10L0* can be used to request a tool data reload ('g' command) from within G-code programs or by MDI.
A reload operation is also typically provided by a Graphical User Interface (GUI) so that on-demand reloads can be requested.
For example, a Python GUI application can use:

[source,python]
----
#!/usr/bin/env python3
from  linuxcnc import command
command().load_tool_table()
----

Alternatively, a *db_program* may push its local data changes to synchronize its data with LinuxCNC by using the load_tool_table() interface command.
Commands which push changes to LinuxCNC may be rejected if the interpreter is running.
The interpreter state can be checked before issuing a load_tool_table() command.  Example:

[source,python]
----
#! /usr/bin/env python3
import linuxcnc
s = linuxcnc.stat()
s.poll()
if s.interp_state == linuxcnc.INTERP_IDLE:
    linuxcnc.command().load_tool_table()
else: # defer loading until interp is idle
   ...
----

If the database application adds or removes tools after initialization, a call to tooldb_tools() must be issued with an updated user_tools list.
The updated list of tools will be used on subsequent get commands or load_tool_table() requests.

[NOTE]
Removal of a tool number should only be done if the tool number is not currently loaded in spindle.


==== Debug Environmental Variables

Exporting the environmental variable DB_SHOW enables LinuxCNC prints (to stdout) that show tool data retrieved from the *db_program* at startup and at subsequent reloading of tool data.

Exporting the environmental variable DB_DEBUG enables LinuxCNC prints (to stdout) for additional debugging information about interface activity.

=== Example program

An example *db_program* (implemented as a Python script) is provided with the simulation examples.
The program demonsrates the required operations to:

. acknowledge startup version
. receive tool data requests:  'g' (*get* command)
. receive tool data updates:   'p' (*put* command)
. receive tool load updates:   'l' (*load_spindle* command)
. receive tool unload updates: 'u' (*unload_spindle* command)

=== Python tooldb module

The example program uses a LinuxCNC provided Python module ('tooldb') that manages the low-level details for communication and version verification.
This module uses callback functions specified by the *db_program* to respond to  the 'g' (get) command and the commands that indicate tool data changes ('p', 'l', 'u').

The *db_program* uses the 'tooldb' module by implementing the following Python code:

[source,python]
----
user_tools = list(...)   # list of available tool numbers

def user_get_tool(toolno):
    # function to respond to 'g' (get) commands
    # called once for each toolno in user_tools
    ...
def user_put_tool(toolno,params):
    # function to respond to 'p' (put) commands
    ...
def user_load_spindle(toolno,params):
    # function to respond to 'l' (put) commands
    ...
def user_unload_spindle(toolno,params):
    # function to respond to 'u' (put) commands
    ...

#------------------------------------------------------------
# Begin:
from tooldb import tooldb_tools     # identify known tools
from tooldb import tooldb_callbacks # identify functions
from tooldb import tooldb_loop      # main loop

tooldb_tools(user_tools)
tooldb_callbacks(user_get_tool,
                 user_put_tool,
                 user_load_spindle,
                 user_unload_spindle,
                )
tooldb_loop()
----

[NOTE]
Use of 'tooldb' is not required -- it is provided as a demonstration of the required interface
and as a convenience for implementing Python-based applications that interface with an external database.

== Simulation configs

Simulation configs using the AXIS gui:

. configs/sim/axis/db_demo/*db_ran*.ini    (random_toolchanger)
. configs/sim/axis/db_demo/*db_nonran*.ini (nonrandom_toolchanger)

Each sim config simulates a *db_program* implementing a database with 10 tools numbered 10--19.

The *db_program* is provided by a single script (db.py) and symbolic links to it for alternative uses: db_ran.py and db_nonran.py.
By default, the script implements random_toolchanger functionality.
Nonrandom toolchanger functions are substituted if the link name includes the text "'nonran'".

The sim configs demonstrate the use of the Python 'tooldb' interface module and implement a basic flat-file database
that tracks tool time usage for multiple tools having equal diameters.
The database rules support selection of the tool having the lowest operating time.

The sim configs use a primary task to monitor and respond to tool updates initiated from within LinuxCNC.
A periodic task updates tool time usage at reguar intervals.
Separate, concurrent tasks are implemented as threads to demonstrate the code required
when changes are initiated by the *db_program* and demonstrate methods for synchronizing LinuxCNC internal tooldata.
Examples include:

. updates of tool parameters
. addition and removal of tool numbers

A mutual exclusion lock is used to protect data from inconsistencies due to race conditions between LinuxCNC tooldata updates and the database application updates.

=== Notes

When a *db_program* is used in conjunction with a random tool changer ([EMCIO]RANDOM_TOOLCHANGER),
LinuxCNC maintains a file ('db_spindle.tbl' in the configuration directory) that consists of a single tool table line identifying the current tool in the spindle.

// vim: set syntax=asciidoc:
