--- render_macros: false pdfexport: true alias: tutorials-connectiot-how-values-travel timetoread: true tutorial: full module: Connect IoT (Equipment Integration) description: "Build a File Raw workflow that logs each new file and watch when a task runs, what it keeps between runs, how a second file overwrites a value, and how Control Flow keeps each file apart" --- # Connect IoT - How Values Travel Tutorial ## Overview A workflow reads like a short program: each task is a call that receives inputs, emits outputs, and then signals `Success` or `Error`. Many defects that look like bad equipment data come from how values move between those calls: a task that never runs, a value left from an earlier event, or a value that the next event replaces before it is used. This tutorial is for integrators who completed [[tutorials-connectiot-configuration-basic]] and build workflows in the **Automation Controller** designer. A File Raw driver plays the equipment, so you raise each event yourself by dropping a file into a folder, and you read each result on the **Automation Manager** console. At the end, you have a Data Flow workflow that shows each mechanism one at a time, and a Control Flow workflow that handles overlapping files without mixing their values. !!! info For more information, see [[user-guide-automation-controller-workflow-custom-dataflow]], [[user-guide-automation-controller-workflow-custom-controlflow]], and [[user-guide-automation-task-core-logmessage]]. ## Before You Start - Prepare a File Raw integration: an **Automation Protocol** on the `@criticalmanufacturing/connect-iot-driver-fileraw` package, an **Automation Driver Definition** with the driver's shipped events, and an **Automation Controller** with that driver. [[tutorials-connectiot-configuration-controlflow]] creates these entities, and [Connect IoT - Raw File Driver](../../../../userguide/automation/definition/automation-protocol/automation-protocol-protocols/driver_fileraw.md) lists the events `OnNewFile` and `OnFileDeleted` and the properties they carry. - Set `path` in the `Equipment Configuration` task of the controller setup workflow to an empty folder, for example `c:/temp/Handler`. Keep `alwaysStat` at its default, `true`, so that `OnNewFile` carries `Size`. - Run an **Automation Manager** in console mode with an instance of the controller. - Raise the events by hand, or from an automated test with the Test Orchestrator [File-Raw simulator](https://developer.criticalmanufacturing.com/explore/guides/customizations/automation/how-tos/test-orchestrator/plugins/simulators/FileRaw/), which creates and deletes files in the watched folder. !!! note From version 12.0, the File Drivers tasks, such as `New File Event`, are deprecated. Listen to file events with `On Equipment Event`, as this tutorial does, and run file operations with `Execute Equipment Command`. See the [File Raw tasks migration guide](https://developer.criticalmanufacturing.com/explore/guides/migration/version-specific/1200/connect_iot_fileraw_tasks_1200/). ## Sequence of Steps ### Step 1: Log Each New File A task runs when a value reaches its `Activate` input. A value that reaches any other input waits there until the task is activated. 1. In the **Automation Controller**, add a Data Flow workflow named, for example, `How Values Travel`. 2. Add an `On Equipment Event` task and set **Equipment Event** to `OnNewFile`. Its outputs include `FileName` and `Size`. 3. Add a `Log Message` task named `Log file name`, with **Mode** `RawText` and **Default Message** `(no file yet)`. 4. Link the `FileName` output to the `message` input of `Log file name`, save, and drop a file, such as `a.csv`, into the watched folder. No line from `Log file name` appears: a value on `message` does not run the task. 5. Link the `Success` output of `On Equipment Event` to the `Activate` input of `Log file name`, save, and drop `b.csv`. The console shows `b.csv`. ![FileName and Success links to Log file name](images/connectiot_hvt_step1.png) Every task has `Activate`, `Success`, and `Error`. A task emits its own outputs first and `Success` last, so a task activated by `Success` finds the values already on its inputs. Link the values that a task reads, then link `Success` to `Activate` to say when to read them. [[user-guide-automation-task-core-switch]] follows the same rule: it compares only when a value reaches `Activate`. !!! note From version 12.0, `Log Message` and `Switch` run only from `Activate`. The 12.0.0 migration adds, in Data Flow workflows, an `Activate` link from each output already linked to these tasks. See the [normalized tasks migration guide](https://developer.criticalmanufacturing.com/explore/guides/migration/version-specific/1200/connect_iot_normalized_tasks_1200/). ### Step 2: Activate the Log From Another Event In Data Flow, one instance of each task serves the whole workflow, and an input keeps its value until something clears or replaces it. `Log Message` clears its inputs after each line while **Reset inputs on activate** is on, its default. 1. Add a second `On Equipment Event` task with **Equipment Event** `OnFileDeleted`, link its `Success` to the `Activate` input of `Log file name`, and save. 2. Drop `c.csv`, and then delete it. The console shows `c.csv` when the file arrives and `(no file yet)` when you delete it. The first line cleared `message`, so the second activation falls back to **Default Message**. 3. Turn off **Reset inputs on activate**, save, and repeat with `d.csv`. The deletion now shows `d.csv` again: the task reads a value that another event left on its input, and nothing in the log says so. 4. Turn **Reset inputs on activate** back on, delete the `OnFileDeleted` task, and save. Tasks differ in what they keep between runs. `Execute Equipment Command`, for example, keeps its arguments unless **Clear Inputs (on activate)** is on, and that setting is off by default. Check the reference page of each task, and feed each chain from one event, so that every activation brings every value the task reads. ### Step 3: Log Several Values Through Added Ports Some tasks cannot declare in advance the values they carry, so you add their ports. Linking an output to the `Link here to add new` input of such a task adds a named input. 1. Add a `Log Message` task named `Log file details`, with **Mode** `MultipleInputs`. 2. Link the `FileName` and `Size` outputs of `On Equipment Event` to the `Link here to add new` input of `Log file details`, link `Success` to its `Activate`, and save. 3. Drop `e.csv`. `Log file details` shows `FileName = e.csv` and `Size = `. Each label is the name of the added input. ![Added FileName and Size inputs of Log file details](images/connectiot_hvt_step3.png) The canvas labels an added port with the name you gave it, but each task stores the port under a name of its own. A Data Flow link uses the stored name without showing it, while a Control Flow input row or expression must write it: | Task | Added port on the canvas | Stored port name | |---|---|---| | `On Equipment Event` | output `FileName` | `FileName` | | `Log Message`, `MultipleInputs` mode | input `FileName` | `FileNameIn`. In Control Flow, the task also reads a row named `FileName` | | `Code` | input `Size`, output `status` | `SizeIn`, `statusOut` | | `Synchronize` | `FileName` | `FileNameIn` and `FileNameOut` | | `Store Data`, `Retrieve Data` | entry `count` | `count` | Table: Stored names of added ports A wrong port name raises no error. The value does not arrive, and the task logs `` or runs on a default value. ### Step 4: Tell null From undefined An output that emits `undefined` changes nothing on the input it reaches, so it activates nothing. An output that emits `null` is delivered like any other value, and it activates the task it reaches. 1. Add a `Code` task named `classify`, and on its **Outputs** tab add an output `status` of type `String`. 2. Link the `Size` output of `On Equipment Event` to the `Link here to add new` input of `classify`, which adds the input `Size`, and link `Success` to its `Activate`. 3. On the **Code** tab, make `main` return a status: ```ts public async main(inputs: any, outputs: any): Promise { return { status: inputs.Size > 0 ? "has data" : null }; } ``` 4. Add a `Log Message` task named `Log status`, with **Mode** `RawText` and **Default Message** `(no status)`. Link the `status` output of `classify` to both the `message` and the `Activate` inputs of `Log status`, and save. 5. Drop a file with some text in it, and then an empty file, such as `empty.csv`. `Log status` shows `has data`, and then `(no status)`. The `null` reached `Activate` and ran the task, and the empty `message` fell back to **Default Message**. 6. Replace `null` with `undefined` in the code, save, and drop another empty file. `Log status` shows nothing: the `undefined` did not activate it. 7. Replace `undefined` with `"empty"`, save, and drop another empty file. `Log status` shows `empty`. Give a value its fallback where you produce it, and do not emit `null`. The link from `status` to `Activate` only makes the difference visible: in a workflow, activate `Log status` from the `Success` of `classify`. ### Step 5: Let Two Files Share One Task Instance Overlapping events share every Data Flow task. When a second event reaches an input before the first run reads it, the second value replaces the first. 1. Add a `Sleep` task with **Interval** `5000`. Remove the link from `Success` of `On Equipment Event` to `Activate` of `Log file name`, link that `Success` to the `Activate` of `Sleep`, and link the `Success` of `Sleep` to the `Activate` of `Log file name`. Keep the link from `FileName` to `message`, and save. 2. Drop `f1.csv`, and drop `f2.csv` within five seconds. Five seconds after each file, `Log file name` shows `f2.csv`, and then `(no file yet)`. The second file replaced `f1.csv` on `message` while the first run waited, and the first line cleared it. ![Sleep before Log file name](images/connectiot_hvt_step5.png) The `Sleep` only makes the window visible. Events that arrive closer together than their chain takes to run overwrite values in the same way. When a second value reaches an input before the first was delivered, the controller warns `Task '\' input '' will receive value '' instead of ''`. No task or workflow setting makes Data Flow safe for fast events. ### Step 6: Rebuild the Handler in Control Flow A Control Flow branch builds its tasks anew for each event, so each run reads only its own values. There are no links: an input row reads an earlier task through an expression, `{{ $. }}`. This step writes the `On Equipment Event` block as `onFile` and the `Code` block as `classify`; in your workflow, type `$` inside `{{ }}` and pick the block names that the designer suggests. 1. In the `How Values Travel` workflow, turn off **Auto Enable** on `On Equipment Event`, so that only the new workflow reacts to files, and save. 2. Add a Control Flow workflow named, for example, `How Values Travel CF`, and place an `On Equipment Event` task with **Equipment Event** `OnNewFile`. 3. Place these tasks in the handler of the event, in this order: | Task | Settings | Input rows | |---|---|---| | `Sleep` | **Interval** `5000` | - | | `Code` | Input `Size` of type `Integer`, output `status` of type `String`, and the code from Step 4 with `"empty"` | `SizeIn`: `{{ $onFile.Size }}` | | `Log Message` | **Mode** `RawText`, and **Default Message** `{{ $onFile.FileName }}: {{ $classify.statusOut }}` | - | Table: Tasks in the Control Flow handler The field after `Log` on the block is **Default Message**. In Control Flow, `Log Message` evaluates the expressions in it. ![Control Flow handler with Sleep, Code, and Log Message](images/connectiot_hvt_step6.png) 4. Save, drop `g1.csv` with some text in it, and drop an empty `g2.csv` within five seconds. After each wait, the console shows `g1.csv: has data` and then `g2.csv: empty`, each line under the execution context of its own file. An expression that reads `$classify.status` gets nothing, because `Code` emits its added output as `statusOut`. Handle a fast or overlapping event in Control Flow. Data Flow suits events spaced further apart than their chain takes to run, and workflows that switch their own triggers on and off. ### Validate the Result 1. Drop three files within five seconds, one of them empty. `How Values Travel CF` shows one line per file, each with its own name and status. `How Values Travel` shows nothing, because its trigger is off. 2. Turn **Auto Enable** back on in `How Values Travel`, save, and repeat. `Log file details` and `Log status` show each file immediately. `Log file name` shows the name of a later file after the first wait and `(no file yet)` after the others. The Control Flow lines stay correct. ## What You Learned - A task runs when a value reaches `Activate`, and it emits its outputs before `Success`. - A Data Flow task is one instance for the whole workflow: it keeps what its inputs received unless it clears them, as **Reset inputs on activate** does for `Log Message`. - An added port has the name you gave it on the canvas and a stored name, such as `statusOut`, that Control Flow rows and expressions use. - An `undefined` output activates nothing, and a `null` output is delivered. - Overlapping events overwrite values in Data Flow, and Control Flow gives each event its own run. ## Next Steps Continue with [[tutorials-connectiot-control-flow-in-depth]] to branch, loop, and handle failures in Control Flow, and with [[tutorials-connectiot-timing-and-state]] to add clocks, time limits, and values that outlive a run. The [[tutorials-connectiot-learning-path]] lists the full sequence.