--- alias: tutorials-automation-scheduled-action-scenario-05-custom-dee-action-at-every-stage description: "Show how to drive every Automation Scheduled Action stage with DEE Actions when an external system owns targeting, readiness, execution, and validation." --- # Scenario 5 - Custom DEE Action at Every Stage ## Overview This scenario shows how to use a **Custom DEE Action** at every stage of the pipeline, for example for **Context Resolution**, **Pre-Condition**, **Detector**, **Acceptance Gate**, **Action**, and **Post-Action Validation**. It also demonstrates how to combine a native built-in check with a **DEE action** in the same **Pre-Conditions** list, so the cheap native check runs first and the **DEE Action** call is only made when needed. Use this as a reference when built-in component types cannot express your readiness or maintenance logic, or when an external system owns the data that drives each decision. In this example, the cookie factory's baking oven recalibration is governed entirely by an external QMS. The QMS owns the recalibration schedule that determines which stations are due this cycle, per-oven readiness signals, change approval records, the recalibration procedure, and acceptance results - none of which are visible to MES directly. ### When to Use - **External system integration** - when readiness, execution, or validation data lives outside MES in a QMS, ERP, CMMS, or calibration service and must be queried at runtime for each target. - **Change management enforcement** - when company policy requires a registered work order, change request, or digital approval before any maintenance action can proceed on a specific piece of equipment. - **Custom maintenance operations** - when the **Action** is not a standard version update but a bespoke procedure (setpoint adjustment, certificate renewal, sensor calibration, firmware flash) driven by an external system. - **QMS-owned target selection** - when the external system determines not just whether a target is ready, but which targets should be in scope at all. Using a **DEE Action** for **Context Resolution** avoids creating tasks for stations the QMS has not scheduled, eliminating unnecessary polling overhead for out-of-cycle equipment. - **Dynamic or computed readiness** - when the conditions that determine per-target readiness change frequently, depend on cross-system state, or cannot be expressed as a fixed MES entity state or material count. - **Full audit trail through DEE Action** - when each decision point (schedule check, window check, readiness signal, approval, execution result, acceptance test) must be logged in an external system of record, and the **DEE actions** provide that integration at every stage. ### Example The cookie factory's baking oven recalibration is managed entirely by an external QMS. The QMS owns which stations are due for recalibration this cycle, whether each oven is ready, whether a change request has been approved, and whether calibration passed acceptance. None of this data is available in MES directly, so every decision point is delegated to a **DEE action** that queries the QMS at runtime. Each **DEE Action** has a single, well-defined responsibility: answer one question or perform one operation, keeping each independently testable and replaceable. - **Resources**: `ASA Baker-01`, `ASA Baker-02`. - **Controller**: `BakingController`, revision `A`, version `1`. - Operation: baking oven recalibration fully driven by an external QMS. ```mermaid graph LR QMS["External QMS"] -->|"**Context Resolution**: which stations are due?"| ASA[ASA Definition] ASA -->|task| B1[Baker-01] ASA -->|task| B2[Baker-02] B1 <-->|"per-oven checks & calibration"| QMS B2 <-->|"per-oven checks & calibration"| QMS ``` !!! warning "Illustrative scenario - not a production pattern" This scenario is intentionally artificial on two levels. First, Automation Scheduled Action is designed for MES internal maintenance such as changing managers, updating controller versions, and similar infrastructure operations. It is not designed to drive equipment calibration workflows; those belong in dedicated calibration or quality management processes. Second, placing a DEE Action at every single stage is not a recommended design, it exists here solely to demonstrate the full flexibility and potential of Automation Scheduled Actions. ## Prerequisites Before creating the Definition, confirm the following: - Load the Scenario 5 automation master data into MES so it creates the `BakingController [A.1]` controller definition, `baking-manager` record, and both baker instances (master data package: [01-automation-masterdata.json](./masterdata/scenario05/01-automation-masterdata.json)). - All target instances are currently running `BakingController` revision `A`, version `1`. - The following **DEE Actions** are deployed and tested. Each must return a `Boolean` unless noted otherwise: | DEE action | Stage | Returns | Responsibility | |---|---|---|---| | [`ASA_Scenario5_ResolveStationsDueForCalibration`](./dees/ASA_Scenario5_ResolveStationsDueForCalibration.cs) | **Context Resolution** | `Entity[]` | Returns the entities of stations the QMS has scheduled for recalibration this cycle. Stations not on the schedule are excluded - no task is created for them. | | [`ASA_Scenario5_CheckMaintenanceWindowOpen`](./dees/ASA_Scenario5_CheckMaintenanceWindowOpen.cs) | PreCondition | `Boolean` | Returns `true` if the global QMS maintenance window is currently active. Applied to all tasks - if it returns `false`, no task is progressed on this pass. | | [`ASA_Scenario5_CheckStationHasNoPendingTests`](./dees/ASA_Scenario5_CheckStationHasNoPendingTests.cs) | Detector | `Boolean` | Returns `true` if the target station has no active or queued quality tests in the QMS. | | [`ASA_Scenario5_CheckChangeApprovalRegistered`](./dees/ASA_Scenario5_CheckChangeApprovalRegistered.cs) | AcceptanceGate | `Boolean` | Returns `true` if a change request for this station is registered and approved in the QMS. | | [`ASA_Scenario5_ExecuteStationRecalibration`](./dees/ASA_Scenario5_ExecuteStationRecalibration.cs) | Action | *(void or success signal)* | Runs the recalibration procedure for the target station via the QMS API. | | [`ASA_Scenario5_ValidateCalibrationResult`](./dees/ASA_Scenario5_ValidateCalibrationResult.cs) | PostActionValidation | `Boolean` | Returns `true` if the QMS reports the station passed post-calibration acceptance tests. | Table 1: Scenario 5 DEE Actions by Stage - Notification templates `ASA PreAction Template`, `ASA Success Template`, `ASA Failure Template`, and `ASA Ignored Template` exist in MES if you intend to use notifications. ## Configuration ### Why This Pattern This Definition uses the following combination of components: | Component | Type | Purpose | |---|---|---| | **Context** | **DEE Action** | Queries the QMS recalibration schedule and returns only the stations due this cycle. Stations not scheduled are excluded before any task is created. | | **Pre-Conditions** | `AllowedWeekdays` + **DEE Action** | Two preconditions in order: first a native weekday check (baking oven maintenance is never scheduled on weekends); second a **DEE Action** check for the QMS maintenance window. The native check is cheap and prevents the **DEE Action** from running unnecessarily on weekends. | | **Detector** | **DEE Action** | Checks whether this specific oven has no pending tests - the per-oven readiness condition. | | **Acceptance Gate** | **DEE Action** | Checks whether an approved change request exists for this oven in the QMS. | | **Action** | **DEE Action** | Runs the recalibration procedure via the QMS API. | | **Post Action Validation** | **DEE Action** | Confirms the oven passed post-calibration acceptance tests in the QMS. | Table 2: Scenario 5 Component Design Using a **DEE Action** for **Context Resolution** means the QMS controls which stations enter the pipeline at all, as stations the QMS has not scheduled never receive a task. Using a **DEE Action** as a **Pre-Condition** vs. an **Acceptance Gate** has different semantics: a **Pre-Condition** applies globally to all tasks on a polling pass; an **Acceptance Gate** applies per task and only blocks that specific task if it fails. ### Create the Definition Open **Automation Scheduled Action** and select **Create** (see [[user-guide-automation-scheduled-action-create|Create Definition]]). The wizard has four tabs. #### Tab 1 - General Data Start by filling in the general definition fields. - Give it a name like `CF_BakingOven_Recalibration_A1` and a description that captures its purpose (for example, "Run baking oven recalibration when the QMS signals all conditions are met.") - Set **Is Enabled** to **Disabled** for now. - Set **Validity** to `5` days. For **Context**, select **DEE Action** and enter `ASA_Scenario5_ResolveStationsDueForCalibration` as the action. The **Context Resolution** queries the QMS recalibration schedule and returns the entity names of stations due this cycle, creating one task per returned station. Stations the QMS has not scheduled are excluded entirely: no task is created for them and no downstream **DEE Action** is ever invoked on their behalf. For the **Detector**, select **DEE Action** and enter `ASA_Scenario5_CheckStationHasNoPendingTests`. This is a per-task readiness check that receives the target entity as context, so QMS lookups are scoped to the correct oven. For the **Action**, select **DEE Action** and enter `ASA_Scenario5_ExecuteStationRecalibration`. This runs the recalibration procedure for the target oven via the QMS API, and executes exactly once when all checks pass. Optionally, configure notification templates to be alerted at key moments: | Event | Template | |---|---| | Pre-Action | `ASA PreAction Template` | | Success | `ASA Success Template` | | Failure | `ASA Failure Template` | | Ignored | `ASA Ignored Template` | Table 3: Scenario 5 Notification Templates #### Tab 2 - Pre-Conditions Select :material-plus: twice to add the following pre-conditions in this order: **1. Allowed Weekdays** - Set the weekdays to `Monday` through `Friday`. Baking oven maintenance is never scheduled on weekends. This check is evaluated first and is inexpensive, so it prevents the DEE action from running unnecessarily on weekends. **2. DEE Action** - Enter `ASA_Scenario5_CheckMaintenanceWindowOpen`. This is a global check: if the QMS maintenance window is closed, all tasks are skipped on that pass without evaluating individual station readiness. !!! note "Pre-Condition vs. Acceptance Gate semantics" A Pre-Condition applies to all tasks: if it fails, no task is evaluated on that pass. An Acceptance Gate applies per task: if it fails, only that task is skipped while others continue. `ASA_Scenario5_CheckMaintenanceWindowOpen` belongs here as a Pre-Condition because the QMS maintenance window is a global condition that governs all ovens simultaneously. #### Tab 3 - Acceptance Gates Select :material-plus: and select **DEE Action**. Enter `ASA_Scenario5_CheckChangeApprovalRegistered`. This is a per-task **Acceptance Gate** that checks whether an approved change request exists for this specific oven in the QMS. If it returns `false`, only that oven's task is skipped, and other ovens continue to be evaluated. #### Tab 4 - Post-Action Validations Select :material-plus: and select **DEE Action**. Enter `ASA_Scenario5_ValidateCalibrationResult`. This checks whether the QMS reports the oven passed post-calibration acceptance tests. A `false` return marks the task as `ValidationFailed`. !!! tip "Verify tasks before enabling" Click **Save** with **Is Enabled** set to **Disabled**. The system immediately resolves the context and creates one task per target instance. Before enabling, open **Automation Scheduled Action Tasks** and confirm: - All expected targets appear in the task list. - No unexpected targets are included. Once the task list is correct, enable the Definition from the top ribbon (see [[user-guide-automation-scheduled-action-enable-disable|Enable or Disable Definition]]). ## How It Works at Runtime After you enable the Definition, the system processes each task on every polling pass as follows: 1. `ASA_Scenario5_ResolveStationsDueForCalibration` queries the QMS recalibration schedule and returns the entity names of stations due this cycle. One task is created per returned station. Stations not on the schedule are excluded - no task is created for them and no further **DEE Action** is invoked. 2. The native `AllowedWeekdays` **Pre-Condition** runs first. If today is a weekend, all tasks are skipped with `SkippedPreConditionFailed` immediately and the maintenance window **DEE Action** is not invoked. 3. `ASA_Scenario5_CheckMaintenanceWindowOpen` runs once per pass. If it returns `false`, all tasks are skipped with `SkippedPreConditionFailed` and no further evaluation happens on this pass. 4. For each task individually: `ASA_Scenario5_CheckStationHasNoPendingTests` runs for the target oven. If it returns `false`, the task is skipped with `SkippedNotReady`. 5. `ASA_Scenario5_CheckChangeApprovalRegistered` runs for the target oven. If it returns `false`, the task is skipped with `SkippedGateFailed`. 6. `ASA_Scenario5_ExecuteStationRecalibration` runs the recalibration procedure via the QMS API. 7. `ASA_Scenario5_ValidateCalibrationResult` checks whether the oven passed acceptance tests. If it returns `false`, the task is marked `ValidationFailed`. 8. The task is finalized with outcome and notifications are sent. ```mermaid flowchart TD R["ResolveStations
DueForCalibration"] --> RS{"Station
scheduled?"} RS -- No --> NOTASK([No task created]) RS -- Yes --> A([Task per scheduled station]) A --> W{"Mon–Fri?"} W -- No --> S1([SkippedPreConditionFailed]) W -- Yes --> B{"CheckMaintenance
WindowOpen = true?"} B -- No --> S1 B -- Yes --> C{"CheckStationHas
NoPendingTests = true?"} C -- No --> S2([SkippedNotReady]) C -- Yes --> D{"CheckChangeApproval
Registered = true?"} D -- No --> S3([SkippedGateFailed]) D -- Yes --> E["ExecuteStation
Recalibration"] E --> F{"ValidateCalibration
Result = true?"} F -- No --> FAIL([ValidationFailed]) F -- Yes --> OK([ProcessedSuccess]) ``` ## Expected Task Outcomes | `LastOutcome` | `State` | Meaning | |---|---|---| | `ProcessedSuccess` | `Processed` | All **DEE Action** checks passed, recalibration ran, and calibration result validated successfully. | | `ActionFailed` | `Failed` | `ASA_Scenario5_ExecuteStationRecalibration` threw an error or returned a failure signal. | | `ValidationFailed` | `Failed` | Recalibration ran but `ASA_Scenario5_ValidateCalibrationResult` returned `false` or an error. | | `SkippedPreConditionFailed` | *(task stays active)* | `ASA_Scenario5_CheckMaintenanceWindowOpen` returned `false` - maintenance window is closed. All tasks are skipped on this pass. | | `SkippedNotReady` | *(task stays active)* | `ASA_Scenario5_CheckStationHasNoPendingTests` returned `false` - this oven still has pending tests. | | `SkippedGateFailed` | *(task stays active)* | `ASA_Scenario5_CheckChangeApprovalRegistered` returned `false` - no approved change request for this oven. | Table 4: Scenario 5 Task Outcomes !!! note "No IgnoredNoOp for a **DEE Action**" Built-in actions like `UpdateControllerVersion` automatically detect when the target is already in the desired state and produce `IgnoredNoOp`. When a DEE Action is the Action, this detection does not happen automatically as the DEE Action itself is responsible for all outcome signaling. Design `ASA_Scenario5_ExecuteStationRecalibration` to throw an explicit error (resulting in `ActionFailed`) or complete successfully if the oven is already calibrated, depending on your process requirements. ## Simulating This Scenario Confirm the Scenario 5 master data is loaded and all baker instances are running on `baking-manager` before starting. All six **DEE Actions** must be deployed. The recommended simulation approach is to back each **DEE Action** with a MES Configuration Entry so you can toggle each condition independently from the MES UI without redeploying code. ### Recommended Configuration Entry Paths | DEE action | Configuration Entry path | Value | |---|---|---| | `ASA_Scenario5_ResolveStationsDueForCalibration` | `/Cmf/Tutorials/AutomationScheduleAction/Scenario5/CalibrationScheduled/{stationKey}` | `true` / `false` | | `ASA_Scenario5_CheckMaintenanceWindowOpen` | `/Cmf/Tutorials/AutomationScheduleAction/Scenario5/MaintenanceWindowOpen` | `true` / `false` | | `ASA_Scenario5_CheckStationHasNoPendingTests` | `/Cmf/Tutorials/AutomationScheduleAction/Scenario5/NoPendingTests/{resourceName}` | `true` / `false` | | `ASA_Scenario5_CheckChangeApprovalRegistered` | `/Cmf/Tutorials/AutomationScheduleAction/Scenario5/ChangeApprovalRegistered/{resourceName}` | `true` / `false` | | `ASA_Scenario5_ExecuteStationRecalibration` | `/Cmf/Tutorials/AutomationScheduleAction/Scenario5/RecalibrationSucceeds/{resourceName}` | `true` / `false` | | `ASA_Scenario5_ValidateCalibrationResult` | `/Cmf/Tutorials/AutomationScheduleAction/Scenario5/CalibrationPassed/{resourceName}` | `true` / `false` | Table 5: Scenario 5 Simulation Configuration Entries In the shipped sample, the **Context Resolution** maps station keys `Baker-01` and `Baker-02` to the MES resources `ASA Baker-01` and `ASA Baker-02`. All downstream **DEE Actions** use the MES resource name from the task, so the `resourceName` examples below include the `ASA` prefix. ### To Trigger Processing for One Oven 1. Set `…/CalibrationScheduled/Baker-01` to `true` (schedules `ASA Baker-01` in the **Context Resolution**). 2. Set `…/MaintenanceWindowOpen` to `true`. 3. Set `…/NoPendingTests/ASA Baker-01` to `true`. 4. Set `…/ChangeApprovalRegistered/ASA Baker-01` to `true`. 5. Set `…/RecalibrationSucceeds/ASA Baker-01` to `true`. 6. Set `…/CalibrationPassed/ASA Baker-01` to `true`. 7. Enable the Definition. The **Context Resolution** returns only `ASA Baker-01`, so one task is created. On the next polling pass, `ASA Baker-01` passes all checks and `ASA_Scenario5_ExecuteStationRecalibration` runs. 8. The task transitions to `ProcessedSuccess`. ### To Demonstrate the Context Resolution Excluding One Station 1. Set `…/CalibrationScheduled/Baker-01` to `true` and `…/CalibrationScheduled/Baker-02` to `false`. 2. Enable the Definition. Confirm only one task is created, for `ASA Baker-01`. No task appears for `ASA Baker-02`. 3. Set `…/CalibrationScheduled/Baker-02` to `true` and create a new Definition so both stations are now included. ### To Demonstrate Precondition Blocking All Tasks 1. Set both `CalibrationScheduled` entries to `true`, then set `…/MaintenanceWindowOpen` to `false`. 2. Enable the Definition. Confirm both tasks show `SkippedPreConditionFailed` so no individual station checks run. 3. Set the entry back to `true` and the system evaluates tasks individually on the next pass. ### To Demonstrate Gate Blocking One Oven While Others Proceed 1. Set all Configuration Entries to `true`. 2. Set `…/ChangeApprovalRegistered/ASA Baker-02` to `false`. 3. Confirm `ASA Baker-01` proceeds to `ProcessedSuccess` while `ASA Baker-02` stays at `SkippedGateFailed`. 4. Set `ASA Baker-02`'s approval to `true` and the task processes on the next pass. ### To Simulate a Validation Failure 1. Set all Configuration Entries for `ASA Baker-01` to `true`, but set `…/CalibrationPassed/ASA Baker-01` to `false`. 2. The action runs but the post-action validation returns `false`. 3. The task transitions to `ValidationFailed`. Inspect `LastError` for the **DEE Action** output. ### To Reset and Repeat 1. Set `…/CalibrationScheduled/Baker-01` and `…/CalibrationScheduled/Baker-02` back to `false` so that the **Context Resolution** no longer returns those stations. 2. Create a new Definition (the original tasks are in a terminal state and will not re-evaluate). The **Context Resolution** runs fresh and creates new tasks for any stations that are scheduled. ## Troubleshooting ### All DEE Actions Are Fail-Closed If any **DEE Action** throws an error, returns an unexpected type, or times out, the system treats it as a failed check. A runtime error in `ASA_Scenario5_CheckMaintenanceWindowOpen` skips all tasks on that pass. A runtime error in `ASA_Scenario5_CheckStationHasNoPendingTests` skips that specific oven's task. Design each action to return `false` explicitly for expected negative conditions rather than raising exceptions. ### DEE Actions Receive the Target Entity as Context The **Detector**, **Acceptance Gate**, **Action**, and **Post-Action Validation** **DEE Actions** each receive the specific target entity (the **Automation Controller Instance** and its linked **Resource**) when they run. Use this context to scope QMS lookups to the correct oven. The **Context Resolution** and **Pre-Condition** **DEE Actions** run without a target entity as the **Context Resolution** returns the entity list, and the **Pre-Condition** evaluates a global condition only. ### Test Each DEE Action in Isolation Before Enabling the Definition Deploy and test each action independently using a **DEE Action** test runner before creating the Definition. Confirm return types, error handling, and Configuration Entry toggling all work as expected. This avoids situations where a **DEE Action** bug prevents any task from progressing. ### Mixing DEE with Built-Ins Not every stage needs to be a **DEE Action**. This scenario itself uses a native `AllowedWeekdays` **Pre-Condition** alongside the **DEE Action** maintenance window check. If your process has a fixed time window, use `AllowedWeekdays` and `AllowedTimeWindow` as **Pre-Conditions** and reserve **DEE Actions** for the stages that genuinely require external logic. See [Scenario 4](04-update-controller-version-cookie-packaging.md) for an example of mixing built-ins with a custom **Post-Action Validation**.