# Logic

> The Logic page, its Address space column, the folders, the command bar, the sweep and the write funnel behind every panel, and the three permissions that decide what the page draws.

Logic is the third entry of the navigation rail. It opens for any role holding **View logic**, and its header reads "Define variables (derived or writable values) and actions (scripts that write them), composed over the address space." It is a master-detail page ([how the pages work](app-conventions)): a column headed **Address space** on the left, a command bar and a detail panel on the right. Everything you edit here autosaves as you type; there is no Save button anywhere on the page.

## In this area {#in-this-area}

| Page | What it covers |
| --- | --- |
| [The address space](logic-address-space) | The seven reserved roots, how an address is spelled and sanitized, the filter box, and the three ways to enter an address. |
| [Variables](logic-variables) | The variable editor: identity and address, the Expression and Memory kinds, the Value card, Set value. |
| [Expressions](logic-expressions) | The expression language, the read surface of a point, the rules the evaluator enforces, the script editor. |
| [Actions](logic-actions) | The action editor, what a script may write, every validation message, Run and Enable/Disable. |
| [Triggers](logic-triggers) | The trigger editor and the interval scheduler. |
| [Alarms](logic-alarms) | The alarm editor, the state machine, annunciation, Acknowledge and Ack all. |
| [From other pages](logic-from-other-pages) | The System, Server, Connector and Process roots, the point panel, and the notice naming the page that edits them. |

The **Assets** root of the same column is a group of its own: [Assets](logic-assets) covers Images, Icons, Colors, Symbols and Connection types.

## The Address space column {#the-address-space-column}

The column lists the roots of the address space under two captions. **Defined here** holds what this page creates and edits: **Variables**, **Alarms**, **Actions**, **Triggers** and **Assets**. **From other pages** holds what it only shows: **System**, **Server**, **Connector** and **Process**. Every root under the first caption carries a count (Variables and Alarms count their definitions, Actions and Triggers theirs, Assets the sum of its five categories), and the Alarms root, every alarm folder and every alarm row carry a colored dot with the worst priority they hold that is active or awaiting acknowledge; the dot clears the moment the alarm is answered.

The tree is lazy: a branch loads when you expand it. Live values are never drawn in the tree; the detail panel of the selected node shows them, refreshed twice a second.

| Control | What it does |
| --- | --- |
| Filter… | Narrows the tree to rows whose name contains the text, case-insensitively. A folder survives when any descendant matches and opens to its matches; the Assets root narrows to the categories whose name matches, or comes whole when only its own name matches; an address-space root whose own name matches stays listed and loads its branch when expanded, while the Actions and Triggers sections list only the rows that match. |
| New… (column footer) | Opens the create menu: **New variable**, **New action**, **New trigger**, **New alarm**, then **New folder** with one destination per section (Variables, Alarms, Actions, Triggers). Everything made here is created at the root of its section under the first free name of the `Variable1`, `Action1`, `Trigger1`, `Alarm1` or `Folder1` series. A new definition is not opened (see [Creating and copying](#creating-and-copying)); a new folder opens its name field on its tree row. Drawn only for a role with **Configure logic**; greyed with the reason while the station is locked. |
| Expand all / Collapse all | Open every branch (loading each level as it opens) or close everything back to the roots. |
| Right-click | On a section root, one of your folders, a definition row or the Images, Icons, Colors or Symbols row: selects the row and opens its menu (below). Nothing opens on a node from another page. |

The right-click menu carries, depending on the row and the role: **New variable / New action / New trigger / New alarm** (into the folder or root you clicked), **New folder** (greyed with the reason on a folder already three levels deep), **Run** (action rows), **Acknowledge** (alarm rows), **Ack all** (the Alarms root), **Enable** or **Disable** (actions, triggers and alarms), **Move to** (a submenu with the section root and every folder the row may be filed into), **Copy**, the delete the bar names (**Delete variable** and so on), and on a folder **Rename folder** and **Remove folder**. On Images, Icons, Colors and Symbols it carries their bar: **Import image…**, **New user icon**, **New color**, **New user symbol**, **Import symbol…** and **Copy chat instruction**. A role without Configure logic is offered only Run, Acknowledge and Ack all when it holds Operate logic, and Copy chat instruction on Symbols; a row with none of these opens no menu.

The detail area is empty until you select something: "Select a node to inspect it: variables, actions, triggers and alarms open their editor. Create one with New."

## The command bar {#the-command-bar}

The bar draws the verbs of the selected node. What the role never permits is not drawn; what the situation refuses right now stays drawn, greyed, with the reason in its tooltip: "Configuration is locked while a Unit is running." during an engineering operation, "Runtime stopped" while the station runs read-only.

| Selection | Leading cluster | Trailing (danger) cluster |
| --- | --- | --- |
| One of your folders | New variable / action / trigger / alarm (the kind its section holds), New folder (greyed "This folder is already as deep as folders nest." at the depth cap), Rename folder | Remove folder |
| A variable | Copy | Delete variable |
| An action | Run, Enable or Disable, Copy | Delete action |
| A trigger | Enable or Disable, Copy | Delete trigger |
| An alarm | Acknowledge, Enable or Disable, Copy | Delete alarm |
| The Alarms root | Ack all | |
| Assets, Images | Import image… | |
| Assets, Icons | New user icon | |
| Assets, Colors | New color | |
| Assets, Symbols | New user symbol, Import symbol…, Copy chat instruction | |
| A System, Server, Connector or Process node | The [edited-elsewhere notice](logic-from-other-pages#the-edited-elsewhere-notice) and its Open button | |

**Copy** duplicates the selected definition to the root of its section, under the source's own name when that is free and otherwise `Name (2)`, `Name (3)` and so on, and marks the copy in that root's table instead of opening it (see [Creating and copying](#creating-and-copying)); a copied alarm starts at Normal, a copied trigger starts unscheduled, a copied action has no run recorded. **Enable** and **Disable** flip the definition's Enabled flag through the same autosave as any field. Every **Delete** asks first ("Delete 'X'? … This cannot be undone.") and reports "Deleted." in the action feed; deleting a variable or an alarm leaves every expression, script, condition and dashboard binding that named its address unresolved, which [Validation](validation) lists. A delete lands on the parent of what it deleted: the screen goes to the folder the definition was filed in, or its section root, selected and open in the tree with its table listing what is left, and **Remove folder** lands the same way on the folder above or the section root.

## Creating and copying {#creating-and-copying}

Creating never opens what it made, and a copy is a creation. **New variable**, **New action**, **New trigger** and **New alarm**, whether from the column's **New…**, from the right-click menu of a section root or of a folder, or from the bar of an open folder, make the row under the first free name of its series and take the screen to the page of its parent: the folder it was filed in, or the section root. The tree selects that parent and opens it, the table on the page marks the new row and scrolls it into view, and the feed reads "Created 'Variable1'. Open it to edit it." A create made from a folder's own bar leaves the screen on that folder. **Copy** does the same at the root of its section, and its feed reads "Copied to 'Setpoint (2)'. Open the copy to edit it." A filter typed in the **Filter…** box is cleared by the landing, so that the parent and the new row are in the tree to be shown selected and open.

The page of a section root or of one of your folders is a table of what it holds, with **Name**, **Address** and **Value**, and at the end of each row an **Open** that opens the definition just as its row in the tree does (on the Alarms root, a role with **Operate logic** also finds **Ack** there). A variable whose output type is Icon, Image or Color draws its icon, picture or color swatch in its Value cell, small enough for the row (a color also shows its hex); the cell never writes the icon's source out. Pressing a row picks it, and the picked row is the marked row: the mark stays until you pick another row or move the column anywhere else. A new folder is the exception: it opens its name field on its tree row.

## Folders {#folders}

Each of the four sections can be organized into folders. A folder belongs to one section and holds only that section's items; it nests three levels deep at most. A folder is organization only: it has no permission, no evaluation and no state.

For **Variables** and **Alarms** the folder chain composes the address: a variable in `Forno` reads `Variables.Forno.Setpoint`, one in its `Zona1` subfolder `Variables.Forno.Zona1.Setpoint`. One level of one section is a single segment namespace shared by its folders and its definitions: two folders may each hold a `Setpoint`, but nothing filed at the same level may sanitize to the same segment, and the editor refuses such a name as you type, naming the address already taken. **Actions** and **Triggers** are referenced by identity, so their folders organize the tree only and their panels show a location line instead of an address.

| Command | What happens |
| --- | --- |
| New folder | Creates an empty folder named `FolderN` and opens its name field on the tree row; leaving the field empty or unchanged keeps that name. |
| Rename folder | Opens the name field on the row. Every address below the folder moves with it, every reference is rewritten, and the feed says how many ("Folder renamed; 3 references updated."). |
| Move to | Files a folder or a definition under another folder of the same section, or back at the section root; addresses and references follow. The list offers only the legal destinations: never the folder itself, one of its own subfolders, another section, or a level where its subfolders would pass the depth cap. |
| Remove folder | Asks first, stating what moves: "Its 3 items move to Variables; nothing is deleted. References to the addresses that move are updated." The items and subfolders are re-filed one level up; the removal is refused when two of them would collide at the destination. |

## The sweep and the queue {#the-sweep-and-the-queue}

The runtime runs on two lanes, and neither of them ever waits for equipment. The **sweep** runs every 100 ms on one sequential queue: it evaluates every expression variable, then every enabled alarm condition, in that order, so a condition reads this sweep's variable values, and it also carries the memory and tag writes arriving from elsewhere and the alarm acknowledgements. The **actions lane** runs action scripts, one at a time, in the order they were asked for. A separate scan, also every 100 ms, fires due [triggers](logic-triggers) onto the actions lane.

Because the lanes are independent, a sweep can happen in the middle of a script, so two reads in one script may see different cycles; in exchange, a script that stops to wait for a device holds back only the actions behind it, and never the expressions, the alarms or the operator. Writes do not race for it: order is kept where the wire is, one queue per device (see below). Consumers (dashboards, channels, this page) read the published state; nothing on this page triggers an evaluation by hand.

## Writing values {#writing-values}

Every write goes through one funnel, whatever started it: a script's assignment or `Write(...)`, the **Set value** field of a memory variable, a dashboard control, a Process recipe, the agent, an external OPC UA client writing a public node, a Modbus client writing a row of the [Modbus server](connector-modbus-server) map that allows writing. The funnel refuses a value that is not finite or cannot convert to the target's type, refuses a source that a running recipe or an interlock holds (naming the holder), journals the write as old value to new value under its source (Operator, MCP, the run), and applies it. What the funnel decides, it decides without asking anything: the value, the target, the lock, and whether the device is even connected. A tag write it admits is then handed to that device's own delivery queue in the Connector, where the values owed to that device are sent one at a time, in the order they arrived, while the other devices are served in parallel; a value a newer write to the same tag overtakes there never leaves the queue, and the newer one is what the equipment is told. Whoever asked is who waits for the answer, off the Logic and in its own code: the dashboard component, the [tag page](connector-tags#the-live-value-card)'s manual write, the agent tool, the external OPC UA client, the Modbus client, a running recipe, and a script only where it said `WriteAndWait`. A write whose source is an action is the exception: it goes to the [detailed capture](events-console#detailed-capture) only, never to the Events console, because a script that writes on every sweep produces values by the thousand and would bury the station's news under them. What the console carries of an action is its failures. A tag write takes the engineering value and reverses the tag's [conversion pipeline](connector-value-pipeline) to the raw value the device takes. The details are on [Variables](logic-variables#set-value) and [Actions](logic-actions#what-an-action-may-write).

## Permissions and locks {#permissions-and-locks}

| Permission | What it allows here |
| --- | --- |
| View logic | Opening the page and reading everything on it, the live values included. |
| Configure logic | Creating, editing, renaming, moving, copying, enabling, disabling and deleting variables, actions, triggers, alarms and folders, and curating the asset library. |
| Operate logic | Writing a memory value, running an action, acknowledging alarms, here, on dashboards and on the Events page. |

A role with View logic only reads the whole page: the editors are inert under a note ("Your role can view Logic but cannot configure it (variables, actions, triggers, alarms and assets).") and no configure or operate verb is drawn. A role with Operate but not Configure keeps Run, Acknowledge, Ack all and Set value live. While a Process unit holds the station, the page shows the engineering-lock notice and every configure verb and editor field is greyed; Run, Acknowledge and Set value stay available. While the runtime is stopped, both configure and operate refuse with "Runtime stopped"; the tree and the panels stay readable but carry no live data. The catalog of bits is on [Permissions](users-permissions).

## What the page does not do {#what-the-page-does-not-do}

There is no Save and no Undo: a keystroke is a persisted change (the feed says "Saved."), and a refused save reloads the field to what the station holds. Definitions the agent creates, changes or deletes through its own tools, or another window changes, reach the open column by themselves. An editor open on a definition deleted that way closes, and the feed names it and says it was deleted outside the editor. Alarm history, the Events console and dashboards live on their own pages.
