--- render_macros: false pdfexport: true alias: tutorials-howto-configureeachmachinefrommes description: "Run one Automation Controller on several machines by reading the connection values of each machine from MES in the Setup workflow" --- # How to: Configure Each Machine from MES One **Automation Controller** can run on several machines that differ only in their connection values, such as an address, a folder, or a port. Each machine gets its own controller instance, and every instance runs the same workflows. Keep what differs in MES, per entity, and let the `Setup` workflow read it before the driver connects: 1. `Driver Event` with the **Event** `OnInitialize` - the controller has reached the driver. 1. `Entity Instance` - the entity of this instance. 1. `Resolve Table` - the row of that entity in a **Smart Table**. 1. `Equipment Configuration` - the values of the row, linked into the protocol parameters. 1. `Driver Command` with the **Command** `Connect`. `OnInitialize` is the right moment: nothing has contacted the equipment yet, `Entity Instance` and `Resolve Table` talk only to MES, and `Driver Command` refuses to connect a driver that `Equipment Configuration` has not configured. !!! info For the generated Setup page, see [[user-guide-automation-controller-workflow-setup]]. For instances and their names, see [[operation-guide-connect-iot-runbook#instances-and-names]]. ## Before You Start - You have a controller with a `Setup` page and one driver, such as the OPC UA controller of [[tutorials-connectiot-configuration-basic]]. The examples use its protocol parameter `address`. - Each machine is an entity of the entity type of the controller, such as the **Resources** `Oven-01` and `Oven-02`. An entity takes one active controller instance only. - The driver definitions of the controller have the same entity type as the controller. A driver instance takes the entity type of its **Automation Driver Definition**. ## Sequence of Steps ### Step 1: Store the Values of Each Machine in MES Create a **Smart Table**, such as `MachineConnection`, and add one row per machine. See [[user-guide-smart-tables-index#creating-a-smart-table]]. | Property | Role | Example value | |---|---|---| | `Resource` | Key, a **Resource** | `Oven-01` | | `ResourceType` | Key | `Oven` | | `Address` | Value, `String` | `opc.tcp://oven-01:48010` | Table 1: Smart Table with the address of each machine Order the precedence keys `Resource` first and `ResourceType` second. The row of a resource wins, and a row for its resource type stands in for a resource that has no row of its own. You can keep the values elsewhere: - An attribute of the entity - select it on the **Attributes** tab of `Entity Instance`. Each selected attribute is an output with the name of the attribute. Skip Step 3, and link that output in Step 4. - A configuration entry per entity, such as `/Custom/ConnectIoT/OPC-UA/Oven-01/Address` - use `Get Configurations` in Step 3. ### Step 2: Read the Entity of the Instance On the `Setup` page, remove the link from **Success** of `Driver Event` to **Activate** of `Equipment Configuration`. Add an `Entity Instance` task, rename its block to `machine`, and link **Success** of `Driver Event` to its **Activate**. The task emits the entity of the controller instance on `instance`, and then **Success**. An `Entity Instance` task bound to a driver emits the entity of that driver instance instead, which the Connect wizard asks for separately. Bind it only when the driver instance entity holds the values. See [[user-guide-automation-task-core-entityinstance]]. ### Step 3: Look Up the Row of the Machine Add a `Resolve Table` task, rename its block to `lookup`, and set **Table Type** to `SmartTable` and **Table** to `MachineConnection`. Use the key `Resource` as input and the column `Address` as output. | From | To | |---|---| | `machine` - `instance`, through the converter `Any To Any` | `lookup` - `Resource` | | `machine` - **Success** | `lookup` - **Activate** | Table 2: Links into Resolve Table When you select the table, the task creates one input for each key column and one output for each column of the table. The task sends an entity key as its `Name`, and asks MES for the row. It reads each key on the port `In` and emits each column on `Out`. In `Control Flow`, write the key row `ResourceIn` as `{{ $machine.instance }}`, and read the column as `{{ $lookup.AddressOut }}`. See [[user-guide-automation-task-core-resolvetable]]. To read a configuration entry instead, add a `Get Configurations` task with an input `resourceName` and an output `Address` of type `String` with the path `/Custom/ConnectIoT/OPC-UA/${resourceName}/Address`. In `Data Flow`, link `instance` to `resourceName` through the converters `Any To Any` and `Get Object Property`, with the path `Name` and the type `String`. In `Control Flow`, write the row as `{{ $machine.instance.Name }}`. When the entry does not exist, the output emits its default value and the task logs a warning. See [[user-guide-automation-task-core-getconfigurations]]. ### Step 4: Link the Values into Equipment Configuration `Equipment Configuration` has one input per communication parameter of the protocol, named after the parameter. Link `Address` of `lookup` to `address`, and **Success** of `lookup` to **Activate**. In `Control Flow`, set the `address` row to `{{ $lookup.AddressOut }}`. Keep the link from **Success** of `Equipment Configuration` to **Activate** of `Driver Command` `Connect`. Leave **Address** empty on the **Communication Parameters** tab. A linked value replaces the value of the tab, and every parameter that you do not link keeps its value from the tab. `Data Flow` sends those values as text: an `Integer` parameter stored as `100` reaches the driver as `"100"`. `Control Flow` converts each value to the data type of its parameter and does not send an empty value. A linked value keeps the type of its source in both designers. ![Setup page reading the address from MES](images/connectiot_howto_setup_from_mes.png) ### Step 5: Connect an Instance per Machine Connect the controller to each machine, as in [[tutorials-howto-createautomationcontrollerinstance]]: one instance per **Resource**, such as `Oven-01` and `Oven-02`, each with the **Automation Manager** that reaches the machine. Every instance runs the same `Setup` workflow and reads its own row. ### Validate the Result 1. On the [[user-guide-automation-connect-iot]] page, the driver instance of each machine reaches `Communicating`. 1. In the controller log of each instance, `Configuring the equipment connected using driver '' instance` comes before `Sending a 'Connect' command to the driver '' instance`. ## Add a Machine Add a row to the table, or give the new entity its attribute or configuration entry, and connect the controller to the new entity. The workflow does not change. ## Change the Values of a Machine The table is read only when the `Setup` workflow runs, that is, when the driver initializes. A driver that is already `Communicating` keeps its parameters: `Equipment Configuration` emits **Success** without sending the new values. After you change a row, an attribute, a configuration entry, or the `Setup` workflow, restart each instance that it concerns. See [[operation-guide-connect-iot-runbook#restart-an-instance]]. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | `Entity Instance` fails with `EntityInstance was not yet notified about the entity it will represent.`, and the chain stops. | The task was activated before the controller received the entity of its instance. | Activate the task from `Driver Event` `OnInitialize`. In `Data Flow`, link its **Error** output so that a failure is seen. | | `Equipment Configuration` fails with `Connection with driver instance was not yet established`. | The task was activated before the driver became available, or by a trigger of another driver. | Activate the chain from `Driver Event` `OnInitialize` bound to the same driver. | | The driver receives an empty list as `address`. | No row matches the entity, and there is no row for its resource type. `Resolve Table` emits `[]` on each column output and **Success**. | Add a row for the machine or for its type. | | A file driver fails new files with `ENOENT`. | Two file drivers on the same host watch the same folder and race for each file. | Give each machine its own folder in its row. | Table 3: Troubleshooting the setup from MES