# Heidenhain DNC

The Connectware implementation of the Heidenhain DNC protocol enables interaction with CNC machines over Heidenhain DNC. It supports:

* Reading machine data and status information.
* Editing tool and pocket tables.
* Accessing data and files on the machine’s filesystem.

Each machine connection consists of:

* **Connection settings**: How Connectware connects to the machine.
* **Data mappings**: How Heidenhain data is mapped to MQTT topics.

## Cybus Heidenhain Agent

To access the Heidenhain DNC COM component, Connectware uses the Windows-based Cybus Heidenhain Agent, which runs as a Windows service.

<figure><img src="https://639096190-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfDpOJO2upcq5EpoSahvK%2Fuploads%2Fgit-blob-90f067dcefd8747bebb5e53cca884dc896e2fb12%2Fcybus_heidenhain_agent.png?alt=media" alt=""><figcaption></figcaption></figure>

The Cybus Heidenhain Agent implements the Heidenhain RemoTools SDK, exposing a Microsoft COM component for communication with Heidenhain DNC interfaces.

### Installing the Cybus Heidenhain Agent

1. Download the Cybus Heidenhain Installer.

<a href="https://download.cybus.io/heidenhain-agent/cybus-heidenhain-installer-4.0.4.msi" class="button primary">Cybus Heidenhain Installer (v4.0.4)</a>

2. Run the installer.
3. After installation, enable/start the agent via the Windows Services console.
4. Optional: Configure the service to restart on failure.
5. Agent logs are available in Windows Event Viewer.

## Service Commissioning File Specifics

This section explains the [Cybus::Connection](https://docs.cybus.io/2-0-6/documentation/services/service-commissioning-files/resources/cybus-connection) and [Cybus::Endpoint](https://docs.cybus.io/2-0-6/documentation/services/service-commissioning-files/resources/cybus-endpoint) configuration objects.

For a complete reference, see the example:

{% file src="<https://639096190-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfDpOJO2upcq5EpoSahvK%2Fuploads%2Fgit-blob-2d4c440a48e54775e5ed252d5fd86d9a8cae6167%2Fheidenhain-dnc-reference.yml?alt=media>" %}

For an overview of the available connection and endpoint properties, see:

* [Connection Properties](https://docs.cybus.io/2-0-6/documentation/industry-protocol-details/heidenhain-dnc/heidenhainconnection)
* [Endpoint Properties](https://docs.cybus.io/2-0-6/documentation/industry-protocol-details/heidenhain-dnc/heidenhainendpoint)

### Data Mappings

Data mappings are defined in the [Heidenhain Methods](#heidenhain-methods) section.

Each mapping for a Heidenhain method specifies:

* Communication pattern
* Heidenhain method (e.g., `setToolTableRow`)

Every Heidenhain method provides one of the following access patterns:

* `subscription`
* `read` (request/response) or `write` (as an alias)
* `notification`

#### Subscription

Use the subscribe pattern to poll a Heidenhain method at a fixed interval.

**Example mapping**

{% code lineNumbers="true" %}

```yaml
getStateSubscribe:
  type: Cybus::Endpoint
  properties:
    protocol: Heidenhain
    connection: !ref heidenhainConnection
    topic: myCustomOutputTopic
    subscribe:
      type: poll
      method: getState
      pollInterval: 5000
```

{% endcode %}

**Payload format**

The MQTT message of the output data will be sent to the topic as specified in the endpoint configuration (in the example: `myCustomOutputTopic`). See also [topic property](https://docs.cybus.io/2-0-6/services/service-commissioning-files/resources/cybus-endpoint#topic).

**Message format**

{% code lineNumbers="true" %}

```yaml
{ 'result': <data>, 'timestamp': <timestamp> }
```

{% endcode %}

#### Read

The endpoint’s `read` property implements a request/response pattern. This enables you to request a machine action and receive a response.

It follows JSON-RPC 2.0 specification with the following exceptions:

* The `method` is defined by the `Cybus::Endpoint` (derived from the MQTT topic) and is not provided in the request.
* The `jsonrpc` member is ignored and currently not sent.

Send requests to the MQTT topic `<endpointName>/req` (e.g., `setToolTableRow/req`), and receive responses on `<endpointName>/res`.

Alternatively, configure the endpoint as `write`. The behavior is identical to `read` except that the request topic uses `/set` (responses still arrive on `/res`). In other words, `write` is just an alias for `read`.

The payload of the initial request message must be a JSON object as string, which must contain the `params` member as a JSON array (unless the method has no parameters anyway). The `params` member must be a JSON array with the method arguments listed as JSON values. The array of method arguments is passed on to the machine and is used depending on the Heidenhain machine method. The response message of `read`or `write` will contain the return value from the machine method as payload member `result`, or, if the method call failed, a payload member `error` which contains the error message.

**Example mapping**

{% code lineNumbers="true" %}

```yaml
setToolTableRow:
 type: Cybus::Endpoint
  properties:
    protocol: Heidenhain
    connection: !ref heidenhainConnection
    read:
      method: setToolTableRow
```

{% endcode %}

**MQTT payload**

The MQTT request payload must be a valid JSON object and can contain the following properties:

* `id` (optional): User-defined correlation ID which can be used to identify the response. If this property was given, its value will be returned in the return message.
* `params`: Array of parameters required for the used method. If the method requires no parameters, this property is optional.

Following the example mapping above (method: `setToolTableRow`), the request payload sent on topic `setToolTableRow/req` must be as follows:

{% code lineNumbers="true" %}

```yaml
{ 'params': ['1', { 'NAME': 'REQUEST-EXAMPLE' }], 'id': '1' }
```

{% endcode %}

This modifies the tool table row `1` and sets the field `NAME` of that row to `REQUEST-EXAMPLE`. The following response will be replied on topic `setToolTableRow/res`:

{% code lineNumbers="true" %}

```yaml
On success: { 'result': null, timestamp: 123123123, 'id': '1' }
On error: { 'error': '<error message>', timestamp: 123123123, 'id': '1' }
```

{% endcode %}

Since `setToolTableRow` returns `void`, the successful result is `null`.

#### Notification

Some Heidenhain methods provide a notification pattern, publishing messages when specific events occur.

**Example mapping**

{% code lineNumbers="true" %}

```yaml
onToolTableChangedSubscribe:
  type: Cybus::Endpoint
  properties:
    protocol: Heidenhain
    connection: !ref heidenhainConnection
    subscribe:
      type: notify
      method: onToolTableChanged
```

{% endcode %}

## Heidenhain Methods

These are the available Heidenhain methods. The examples are shown in pseudo-code to clarify input and output types. Each function name matches its corresponding method name (see examples).

### Machine Information

**isConnected**

Indicates whether the CNC controller is currently connected to the agent. Returns `true` if the CNC controller is connected to the agent.

{% code lineNumbers="true" %}

```yaml
bool isConnected = isConnected()
```

{% endcode %}

**getVersion**

Returns the version of the Heidenhain SDK that the agent uses to communicate with the machine.

{% code lineNumbers="true" %}

```yaml
string version = getVersion()
```

{% endcode %}

**getState**

Retrieves the connection state of the CNC.

{% code lineNumbers="true" %}

```yaml
string state =  getState()
```

{% endcode %}

**getNcUpTime**

Returns the CNC up-time in minutes.

{% code lineNumbers="true" %}

```yaml
int ncUpTimeMin = getNcUpTime()
```

{% endcode %}

**getMachineUpTime**

Returns the machine up-time in minutes.

{% code lineNumbers="true" %}

```yaml
int machineUpTimeMin = getMachineUpTime()
```

{% endcode %}

**getSpindleRunningTime**

Get the cumulative spindle run-time the spindle has been running since installation. Parameter is the identifier of the spindle.

{% code lineNumbers="true" %}

```yaml
int spindleRunningTimeMin = getSpindleRunningTime (int axisId)
```

{% endcode %}

**getMachineRunningTime**

Returns the total machine working time (in minutes) since installation.

{% code lineNumbers="true" %}

```yaml
int machineRunningTimeMin = getMachineRunningTime()
```

{% endcode %}

**getMachineState**

Returns a JSON object with selected machine state information.

{% code lineNumbers="true" %}

```yaml
{state, machineRunningTime, machineUpTime, ncUpTime} = getMachineState()
```

{% endcode %}

**getAvailableInterfaces**

Returns an object with key/value pairs for each known CNC interface. Values are `1` (available) or `0` (unavailable).

{% code lineNumbers="true" %}

```yaml
object getAvailableInterfaces()
```

{% endcode %}

**hasAutomaticInterface**

Returns `true` if the Automatic interface is available for this CNC controller.

{% code lineNumbers="true" %}

```yaml
bool hasAutomaticInterface()
```

{% endcode %}

**hasDataAccessInterface**

Returns `true` if the DataAccess interface is available for this CNC controller.

{% code lineNumbers="true" %}

```yaml
bool hasDataAccessInterface()
```

{% endcode %}

**hasErrorInterface**

Returns `true` if the Error interface is available for this CNC controller.

{% code lineNumbers="true" %}

```yaml
bool hasErrorInterface()
```

{% endcode %}

**hasFileSystemInterface**

Returns `true` if the FileSystem interface is available for this CNC controller.

{% code lineNumbers="true" %}

```yaml
bool hasFileSystemInterface()
```

{% endcode %}

**hasItncTableInterface**

Returns `true` if the ItncTable interface is available for this CNC controller.

{% code lineNumbers="true" %}

```yaml
bool hasItncTableInterface()
```

{% endcode %}

**hasProcessDataInterface**

Returns `true` if the ProcessData interface is available for this CNC controller.

{% code lineNumbers="true" %}

```yaml
bool hasProcessDataInterface()
```

{% endcode %}

**hasAutomaticEvents**

Returns `true` if the AutomaticEvents interface is available for this CNC controller.

{% code lineNumbers="true" %}

```yaml
bool hasAutomaticEvents()
```

{% endcode %}

**hasDataAccessEvents**

Returns `true` if the DataAccessEvents interface is available for this CNC controller.

{% code lineNumbers="true" %}

```yaml
bool hasDataAccessEvents()
```

{% endcode %}

**hasErrorEvents**

Returns `true` if the ErrorEvents interface is available for this CNC controller.

{% code lineNumbers="true" %}

```yaml
bool hasErrorEvents()
```

{% endcode %}

**hasMachineEvents**

Returns `true` if the MachineEvents interface is available for this CNC controller.

{% code lineNumbers="true" %}

```yaml
bool hasMachineEvents()
```

{% endcode %}

### Tool and Pocket Table

**getToolTableRow**

Returns the tool table row for the specified tool ID (row number).

{% code lineNumbers="true" %}

```yaml
object row = getToolTableRow (string toolId)
```

{% endcode %}

**setToolTableRow**

Writes a tool table row. Provide the tool ID and an object of key (column name) value pairs.

{% code lineNumbers="true" %}

```yaml
void setToolTableRow (string toolId, object row)
```

{% endcode %}

**addLinkedToolTableRow**

Creates the next empty tool table row and links the tool to the next empty pocket table ID.

{% code lineNumbers="true" %}

```yaml
string pocketId = addLinkedToolTableRow(object row)
```

{% endcode %}

**removeTool**

DEPRECATED: Alias of `removeToolTableRow`. Removes the given `toolId` from the tool table. Use `removeToolTableRow` instead.

{% code lineNumbers="true" %}

```yaml
void removeTool(string toolId)
```

{% endcode %}

**removeToolTableRow**

Removes the tool with the specified `toolId` from the tool table.

{% code lineNumbers="true" %}

```yaml
void removeToolTableRow(string toolId)
```

{% endcode %}

**clearToolTableRow**

Empties the row in the tool table identified by `toolId`.

{% code lineNumbers="true" %}

```yaml
void clearToolTableRow(string toolId)
```

{% endcode %}

**getToolTableCell**

Reads a single cell from the tool table. Parameters: tool ID and column name.

{% code lineNumbers="true" %}

```yaml
string cell = getToolTableCell (string toolId, string fieldName)
```

{% endcode %}

**setToolTableCell**

Writes a single cell in the tool table. Parameters: tool ID, column name, and value.

{% code lineNumbers="true" %}

```yaml
void setToolTableCell (string toolId, string fieldName, string fieldValue)
```

{% endcode %}

**getLinkedToolId**

Returns the tool ID linked to the specified pocket table ID (row number).

{% code lineNumbers="true" %}

```yaml
string toolId = getLinkedToolId (string pocketId)
```

{% endcode %}

**linkToolToPocketTable**

Link tool to pocket table by specifying the pocket table ID (row number) and the tool ID to be linked.

{% code lineNumbers="true" %}

```yaml
void linkToolToPocketTable (string pocketId, string toolId)
```

{% endcode %}

**unlinkToolFromPocketTable**

Unlink tool from pocket table by specifying the pocket table ID of tool to be unlinked.

{% code lineNumbers="true" %}

```yaml
void unlinkToolFromPocketTable (string pocketId)
```

{% endcode %}

**setLinkedToolTableRow**

Set the next empty tool table row and link the tool to the specified pocket table ID.

{% code lineNumbers="true" %}

```yaml
void setLinkedToolTableRow(string pocketId, object row)
```

{% endcode %}

**getPocketTable**

Reads the entire pocket table. Returns an array of objects, one per row.

{% code lineNumbers="true" %}

```yaml
array pocketTable = getPocketTable()
```

{% endcode %}

**getToolTableRowByPocketId**

Returns the tool table row for the tool currently selected at the given pocketId.

{% code lineNumbers="true" %}

```yaml
TableRow getToolTableRowByPocketId(string pocketId)
```

{% endcode %}

**removeToolTableRowByPocketId**

Removes the tool table row for the tool currently selected at the given pocketId.

{% code lineNumbers="true" %}

```yaml
void removeToolTableRowByPocketId(string pocketId)
```

{% endcode %}

**setTool**

Sets the tool table entry at `toolId` to `newToolRow`. If `pocketId` is provided, this also links the tool to that pocket.

{% code lineNumbers="true" %}

```yaml
string setTool(string pocketId, string toolId, TableRow newToolRow)
```

{% endcode %}

**addLinkedToolTableRow**

Set the next empty tool table row and link the tool to the next empty pocket table ID.

{% code lineNumbers="true" %}

```yaml
string pocketId = addLinkedToolTableRow(object row)
```

{% endcode %}

### Data Access

**setAccessMode**

Set the access mode to gain access to protected data sub trees. Not available for iTNCs.

{% code lineNumbers="true" %}

```yaml
void setAccessMode (string dataPath, string password)
```

{% endcode %}

**getValue**

Reads a value.

{% code lineNumbers="true" %}

```yaml
string value = getValue (string dataPath)
```

{% endcode %}

**setValue**

Writes a value.

{% code lineNumbers="true" %}

```yaml
void setValue (string dataPath, string value)
```

{% endcode %}

**getPlcData**

Reads PLC data.

{% code lineNumbers="true" %}

```yaml
string value = getPlcData (string memoryType, string memoryAddress)
```

{% endcode %}

### File System

**transmitFile**

Sends a base64-encoded buffer to the CNC and stores it as a file at `destinationPath`.

{% code lineNumbers="true" %}

```yaml
void transmitFile (string fileBuffer, string destinationPath)
```

{% endcode %}

**receiveFile**

Retrieves a file from the CNC as a base64-encoded string (not raw binary or text).

{% code lineNumbers="true" %}

```yaml
string receiveFile(string cncFile)
```

{% endcode %}

**deleteFile**

Deletes the file at the given path.

{% code lineNumbers="true" %}

```yaml
void deleteFile(string filePath)
```

{% endcode %}

**copyFile**

Copies the file at `sourceFilePath` to `destFilePath`.

{% code lineNumbers="true" %}

```yaml
void copyFile(string sourceFilePath, string destFilePath)
```

{% endcode %}

**readDirectory**

Lists entries in the directory at `directoryPath`. The returned JSON includes the resolved path (input argument plus potential prefix) and the entry list.

{% code lineNumbers="true" %}

```yaml
object readDirectory(string directoryPath)
```

{% endcode %}

**makeDirectory**

Creates a directory at the specified path.

{% code lineNumbers="true" %}

```yaml
void makeDirectory(string directoryPath)
```

{% endcode %}

**deleteDirectory**

Removes the directory at the specified path. The directory must be empty.

{% code lineNumbers="true" %}

```yaml
void deleteDirectory(string directoryPath)
```

{% endcode %}

**deleteDirectoryRecursively**

Recursively deletes all files and subdirectories at the path. Use with caution.

{% code lineNumbers="true" %}

```yaml
object deleteDirectoryRecursively(string directoryPath)
```

{% endcode %}

### NC Program Handling

**selectProgram**

Selects or deselects an NC part program for execution.

{% code lineNumbers="true" %}

```yaml
void selectProgram (int channel, string programName, string startBlockNumber)
```

{% endcode %}

**stopProgram**

Stops the active NC program.

{% code lineNumbers="true" %}

```yaml
void stopProgram (int channel, bool onBlockEnd)
```

{% endcode %}

**cancelProgram**

Cancels the execution of a stopped NC program.

{% code lineNumbers="true" %}

```yaml
void cancelProgram (int channel)
```

{% endcode %}

**setExecutionMode**

Sets the execution mode.

{% code lineNumbers="true" %}

```yaml
void setExecutionMode (string executionMode)
```

{% endcode %}

**startProgram**

Starts or restarts the specified program.

{% code lineNumbers="true" %}

```yaml
void startProgram (string programName)
```

{% endcode %}

**getProgramStatus**

Returns the current program execution status.

{% code lineNumbers="true" %}

```yaml
string programStatus = getProgramStatus()
```

{% endcode %}

**getDncMode**

Returns the current DNC mode.

{% code lineNumbers="true" %}

```yaml
string dncMode = getDncMode()
```

{% endcode %}

**clearControl**

Executes a Clear Control.

{% code lineNumbers="true" %}

```yaml
void clearControl()
```

{% endcode %}

**setOverrideFeed**

Sets the feed override (percentage).

{% code lineNumbers="true" %}

```yaml
void setOverrideFeed (int percentage)
```

{% endcode %}

**setOverrideSpeed**

Sets the speed override (percentage).

{% code lineNumbers="true" %}

```yaml
void setOverrideSpeed (int percentage)
```

{% endcode %}

**setOverrideRapid**

Sets the rapid override (percentage).

{% code lineNumbers="true" %}

```yaml
void setOverrideRapid (int percentage)
```

{% endcode %}

**getOverrideInfoFeed**

Gets the current feed override.

{% code lineNumbers="true" %}

```yaml
int feed = getOverrideInfoFeed()
```

{% endcode %}

**getOverrideInfoSpeed**

Gets the current speed override.

{% code lineNumbers="true" %}

```yaml
int speed = getOverrideInfoSpeed()
```

{% endcode %}

**getOverrideInfoRapid**

Gets the current rapid override.

{% code lineNumbers="true" %}

```yaml
int rapid = getOverrideInfoRapid()
```

{% endcode %}

### Custom Functions of the Cybus Heidenhain Agent

**getAgentVersion**

Returns the Cybus Heidenhain Agent version.

{% code lineNumbers="true" %}

```yaml
string getAgentVersion()
```

{% endcode %}

**setTableMonitorInterval**

For itnc530 and tnc426, sets the manual monitoring (polling) interval of the tool table.

{% code lineNumbers="true" %}

```yaml
void setTableMonitorInterval(int32_t seconds)
```

{% endcode %}

**getTableMonitorInterval**

For itnc530 and tnc426, returns the manual tool-table polling interval.

{% code lineNumbers="true" %}

```yaml
int32_t getTableMonitorInterval()
```

{% endcode %}

**setSdkMutexTimeout**

Sets the special timeout ensuring thread-safe access to Heidenhain SDK methods (needed only for some itnc530 variants).

{% code lineNumbers="true" %}

```yaml
void setSdkMutexTimeout(int32_t seconds)
```

{% endcode %}

**getSdkMutexTimeout**

Returns the special timeout for thread-safe SDK access (needed only for some itnc530 variants).

{% code lineNumbers="true" %}

```yaml
int32_t getSdkMutexTimeout()
```

{% endcode %}

### Events

All event methods are available as `subscribe` operation with `type: notify`.

**onToolTableChanged**

Notification on any tool table cell change.

{% code lineNumbers="true" %}

```yaml
{ toolId, oldRow, newRow } = onToolTableChanged()
```

{% endcode %}

**onToolChanged**

Tool in spindle changed event. This event is fired by the CNC when a new tool is placed in the spindle or when the actual tool is removed from the spindle.

{% code lineNumbers="true" %}

```yaml
{ int toolId, object toolOut, object toolIn, double timestamp } = onToolChanged()
```

{% endcode %}

**onStateChanged**

Connection state changed event. Notifies the client application of the connection state changes of the CNC. (`_IJHMachineEvents2::onStateChanged`).

{% code lineNumbers="true" %}

```yaml
{ string newState } = onStateChanged()
```

{% endcode %}

**onDncModeChanged**

Notifies when DNC mode changes.

{% code lineNumbers="true" %}

```yaml
{ string newDncMode } = onDncModeChanged()
```

{% endcode %}

**onPocketTableChanged**

Notification on any pocket table cell change.

{% code lineNumbers="true" %}

```yaml
{ toolId, oldRow, newRow } = onPocketTableChanged()
```

{% endcode %}

**onProgramChanged**

Notification on execution switch to another program, subprogram, or macro.

{% code lineNumbers="true" %}

```yaml
string program = onProgramChanged()
```

{% endcode %}

**onNcProgramChanged**

Notification on a changed NC program. From `_IJHAutomaticEvents3::OnProgramChanged` and `_IJHAutomaticEvents3::OnProgramStatusChanged`. This event is fired when the CNC program execution status is changed. For itnc530 note that the iTNC 530 emits this event when a newly selected program is started and not when the program is selected.

Typically, this event will be emitted after the OnProgramStatusChanged programEvent DNC\_PRG\_EVT\_STARTED.

{% code lineNumbers="true" %}

```yaml
{string state, string programPath, string programName} = onNcProgramChanged
```

{% endcode %}

**onProgramStatusChanged**

Notification on program execution status change.

{% code lineNumbers="true" %}

```yaml
string programEvent = onProgramStatusChanged()
```

{% endcode %}

## Service Commissioning File (Example)

{% file src="<https://639096190-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FfDpOJO2upcq5EpoSahvK%2Fuploads%2Fgit-blob-a0466790acb1dd5cffa02861fa23facb2e7cda9a%2Fheidenhain-example.yml?alt=media>" %}

{% code title="heidenhain-example.yml" lineNumbers="true" %}

```yaml
---
# ----------------------------------------------------------------------------#
# Commissioning File
# ----------------------------------------------------------------------------#
# Protocol: Heidenhain
# Copyright: Cybus GmbH (2020)
# Contact: support@cybus.io
# ----------------------------------------------------------------------------#
description: >
  Heidenhain sample commissioning file

metadata:
  name: Heidenhain example
  icon: https://www.cybus.io/wp-content/uploads/2017/10/for-whom1.svg
  provider: cybus
  homepage: https://www.cybus.io
  version: 1.0.0

parameters:
  ipAddress:
    type: string
    description: The IP address of the CNC machine
    default: 10.0.0.2

  cncType:
    type: string
    description: The CNC type; can be tnc640, itnc530, tnc426
    default: tnc640

  agentName:
    type: string
    description: The name of the Cybus Heidenhain agent on the windows machine
    default: heidenhain-WIN1234

resources:
  heidenhainConnection:
    type: Cybus::Connection
    properties:
      protocol: Heidenhain
      connection:
        domain: edge.cybus
        agent: !ref agentName
        ipAddress: !ref ipAddress
        cncType: !ref cncType
        plcPassword: <password>
        usrPassword: <password>
        tablePassword: <password>
        sysPassword: <password>
        connectionStrategy:
          initialDelay: 5000
          maxDelay: 30000
          incrementFactor: 2

  getVersion:
    type: 'Cybus::Endpoint'
    properties:
      protocol: Heidenhain
      connection: !ref heidenhainConnection
      topic: getVersion
      read:
        method: getVersion

  getNcUpTime:
    type: Cybus::Endpoint
    properties:
      protocol: Heidenhain
      connection: !ref heidenhainConnection
      topic: getNcUpTime
      read:
        method: getNcUpTime

  getToolTable:
    type: Cybus::Endpoint
    properties:
      protocol: Heidenhain
      connection: !ref heidenhainConnection
      topic: getToolTable
      read:
        method: getToolTable

  onToolTableChanged:
    type: Cybus::Endpoint
    properties:
      protocol: Heidenhain
      connection: !ref heidenhainConnection
      topic: notify/onToolChanged
      subscribe:
        type: notify
        method: onToolTableChanged

  onProgramChanged:
    type: Cybus::Endpoint
    properties:
      protocol: Heidenhain
      connection: !ref heidenhainConnection
      topic: notify/onProgramChanged
      subscribe:
        type: notify
        method: onProgramChanged
```

{% endcode %}

## MQTT Examples

MQTT examples following the example device service commissioning file above.

{% code lineNumbers="true" %}

```yaml
Request --> (MQTT topic) MQTT payload
Response <-- (MQTT topic) MQTT payload
```

{% endcode %}

**Get SDK Version**

{% code lineNumbers="true" %}

```yaml
--> (getVersion/req) { "params": [], "id": 1 }
<-- (getVersion/res) { "result": "1.6.3", "id": 1 }
```

{% endcode %}

**Set tool table row**

{% code lineNumbers="true" %}

```yaml
--> (setToolTableRow/req) { "params": ["10", { "NAME": "MY-AWESOME-TOOL", "L": "1", "R":"4" }], "id": 2 }
<-- (setToolTableRow/res) { "result": null, "id": 2 }
```

{% endcode %}

**Get tool table cell**

{% code lineNumbers="true" %}

```yaml
--> (getToolTableCell/req) { "params": ["10", "NAME"], "id": 3 }
<-- (getToolTableCell/res) { "result": "MY-AWESOME-TOOL", "id": 3 }
```

{% endcode %}
