ofplang-run 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ofplang_run-0.1.0/.github/workflows/ci.yml +56 -0
- ofplang_run-0.1.0/.github/workflows/publish.yml +96 -0
- ofplang_run-0.1.0/.gitignore +12 -0
- ofplang_run-0.1.0/LICENSE +21 -0
- ofplang_run-0.1.0/PKG-INFO +189 -0
- ofplang_run-0.1.0/README.md +155 -0
- ofplang_run-0.1.0/examples/count_chain.env.yaml +20 -0
- ofplang_run-0.1.0/examples/count_chain.workflow.yaml +42 -0
- ofplang_run-0.1.0/examples/data_flow.env.yaml +44 -0
- ofplang_run-0.1.0/examples/data_flow.workflow.yaml +105 -0
- ofplang_run-0.1.0/examples/outputs/data_flow.trace.txt +18 -0
- ofplang_run-0.1.0/examples/outputs/job_run.boundary.yaml +9 -0
- ofplang_run-0.1.0/examples/outputs/job_run.trace.txt +9 -0
- ofplang_run-0.1.0/examples/outputs/plate_chain.boundary.yaml +15 -0
- ofplang_run-0.1.0/examples/outputs/plate_chain.trace.txt +11 -0
- ofplang_run-0.1.0/examples/outputs/poll_drift.exact.svg +2 -0
- ofplang_run-0.1.0/examples/outputs/poll_drift.polled.svg +2 -0
- ofplang_run-0.1.0/examples/outputs/reroute.final.svg +2 -0
- ofplang_run-0.1.0/examples/outputs/reroute.initial.svg +2 -0
- ofplang_run-0.1.0/examples/outputs/script_literal.trace.txt +26 -0
- ofplang_run-0.1.0/examples/plate_chain.env.yaml +29 -0
- ofplang_run-0.1.0/examples/plate_chain.workflow.yaml +59 -0
- ofplang_run-0.1.0/examples/render_data_flow.py +100 -0
- ofplang_run-0.1.0/examples/render_job_run.py +94 -0
- ofplang_run-0.1.0/examples/render_plate_chain.py +109 -0
- ofplang_run-0.1.0/examples/render_poll_drift.py +66 -0
- ofplang_run-0.1.0/examples/render_reroute.py +71 -0
- ofplang_run-0.1.0/examples/render_script_literal.py +131 -0
- ofplang_run-0.1.0/examples/reroute.env.yaml +48 -0
- ofplang_run-0.1.0/examples/reroute.workflow.yaml +41 -0
- ofplang_run-0.1.0/examples/script_literal.env.yaml +27 -0
- ofplang_run-0.1.0/examples/script_literal.workflow.yaml +91 -0
- ofplang_run-0.1.0/ofplang/run/__init__.py +21 -0
- ofplang_run-0.1.0/ofplang/run/__main__.py +9 -0
- ofplang_run-0.1.0/ofplang/run/cli.py +328 -0
- ofplang_run-0.1.0/ofplang/run/py.typed +0 -0
- ofplang_run-0.1.0/ofplang/run/runner/__init__.py +33 -0
- ofplang_run-0.1.0/ofplang/run/runner/boundary.py +195 -0
- ofplang_run-0.1.0/ofplang/run/runner/contract_eval.py +324 -0
- ofplang_run-0.1.0/ofplang/run/runner/contracts.py +325 -0
- ofplang_run-0.1.0/ofplang/run/runner/dataflow.py +173 -0
- ofplang_run-0.1.0/ofplang/run/runner/loader.py +30 -0
- ofplang_run-0.1.0/ofplang/run/runner/provenance.py +55 -0
- ofplang_run-0.1.0/ofplang/run/runner/rolling.py +964 -0
- ofplang_run-0.1.0/ofplang/run/runner/runner.py +170 -0
- ofplang_run-0.1.0/ofplang/run/runner/schedule_client.py +71 -0
- ofplang_run-0.1.0/ofplang/run/runner/status.py +63 -0
- ofplang_run-0.1.0/ofplang/run/runner/values.py +145 -0
- ofplang_run-0.1.0/ofplang/run/simulator/__init__.py +69 -0
- ofplang_run-0.1.0/ofplang/run/simulator/core.py +772 -0
- ofplang_run-0.1.0/ofplang/run/simulator/environment.py +134 -0
- ofplang_run-0.1.0/ofplang/run/simulator/errors.py +70 -0
- ofplang_run-0.1.0/ofplang/run/simulator/script.py +185 -0
- ofplang_run-0.1.0/ofplang_run.egg-info/PKG-INFO +189 -0
- ofplang_run-0.1.0/ofplang_run.egg-info/SOURCES.txt +119 -0
- ofplang_run-0.1.0/ofplang_run.egg-info/dependency_links.txt +1 -0
- ofplang_run-0.1.0/ofplang_run.egg-info/entry_points.txt +2 -0
- ofplang_run-0.1.0/ofplang_run.egg-info/requires.txt +12 -0
- ofplang_run-0.1.0/ofplang_run.egg-info/scm_file_list.json +115 -0
- ofplang_run-0.1.0/ofplang_run.egg-info/scm_version.json +8 -0
- ofplang_run-0.1.0/ofplang_run.egg-info/top_level.txt +1 -0
- ofplang_run-0.1.0/pyproject.toml +89 -0
- ofplang_run-0.1.0/setup.cfg +4 -0
- ofplang_run-0.1.0/tests/fixtures/composite_contract.env.yaml +21 -0
- ofplang_run-0.1.0/tests/fixtures/composite_contract.workflow.yaml +52 -0
- ofplang_run-0.1.0/tests/fixtures/composite_requires_gate.env.yaml +11 -0
- ofplang_run-0.1.0/tests/fixtures/composite_requires_gate.workflow.yaml +72 -0
- ofplang_run-0.1.0/tests/fixtures/contract.env.yaml +26 -0
- ofplang_run-0.1.0/tests/fixtures/contract.workflow.yaml +71 -0
- ofplang_run-0.1.0/tests/fixtures/count_chain.env.yaml +20 -0
- ofplang_run-0.1.0/tests/fixtures/count_chain.workflow.yaml +41 -0
- ofplang_run-0.1.0/tests/fixtures/failure.env.yaml +40 -0
- ofplang_run-0.1.0/tests/fixtures/failure.workflow.yaml +48 -0
- ofplang_run-0.1.0/tests/fixtures/interface_load.boundary.yaml +10 -0
- ofplang_run-0.1.0/tests/fixtures/interface_load.document.yaml +9 -0
- ofplang_run-0.1.0/tests/fixtures/interface_load.env.yaml +33 -0
- ofplang_run-0.1.0/tests/fixtures/interface_load.workflow.yaml +40 -0
- ofplang_run-0.1.0/tests/fixtures/literal_chain.env.yaml +20 -0
- ofplang_run-0.1.0/tests/fixtures/literal_chain.workflow.yaml +30 -0
- ofplang_run-0.1.0/tests/fixtures/nested_composite_contract.env.yaml +26 -0
- ofplang_run-0.1.0/tests/fixtures/nested_composite_contract.workflow.yaml +78 -0
- ofplang_run-0.1.0/tests/fixtures/nested_returns.workflow.yaml +96 -0
- ofplang_run-0.1.0/tests/fixtures/preflight.env.yaml +26 -0
- ofplang_run-0.1.0/tests/fixtures/preflight.workflow.yaml +64 -0
- ofplang_run-0.1.0/tests/fixtures/producer_run_phase.env.yaml +18 -0
- ofplang_run-0.1.0/tests/fixtures/producer_run_phase.workflow.yaml +59 -0
- ofplang_run-0.1.0/tests/fixtures/pure_data.document.yaml +7 -0
- ofplang_run-0.1.0/tests/fixtures/pure_data.env.yaml +44 -0
- ofplang_run-0.1.0/tests/fixtures/pure_data.plan.yaml +70 -0
- ofplang_run-0.1.0/tests/fixtures/pure_data.workflow.yaml +76 -0
- ofplang_run-0.1.0/tests/fixtures/reroute.env.yaml +49 -0
- ofplang_run-0.1.0/tests/fixtures/script.env.yaml +29 -0
- ofplang_run-0.1.0/tests/fixtures/script.workflow.yaml +68 -0
- ofplang_run-0.1.0/tests/fixtures/simple.env.yaml +35 -0
- ofplang_run-0.1.0/tests/fixtures/simple.workflow.yaml +43 -0
- ofplang_run-0.1.0/tests/fixtures/simple_no_target.env.yaml +29 -0
- ofplang_run-0.1.0/tests/fixtures/static_view.env.yaml +25 -0
- ofplang_run-0.1.0/tests/fixtures/static_view.workflow.yaml +69 -0
- ofplang_run-0.1.0/tests/fixtures/typed_returns.workflow.yaml +100 -0
- ofplang_run-0.1.0/tests/test_boundary.py +197 -0
- ofplang_run-0.1.0/tests/test_cli.py +182 -0
- ofplang_run-0.1.0/tests/test_composite_contract.py +93 -0
- ofplang_run-0.1.0/tests/test_composite_requires_gate.py +71 -0
- ofplang_run-0.1.0/tests/test_contract.py +243 -0
- ofplang_run-0.1.0/tests/test_contracts.py +241 -0
- ofplang_run-0.1.0/tests/test_data_values.py +377 -0
- ofplang_run-0.1.0/tests/test_dataflow.py +464 -0
- ofplang_run-0.1.0/tests/test_failure.py +163 -0
- ofplang_run-0.1.0/tests/test_mode_ids.py +32 -0
- ofplang_run-0.1.0/tests/test_nested_composite_contract.py +95 -0
- ofplang_run-0.1.0/tests/test_observability.py +149 -0
- ofplang_run-0.1.0/tests/test_poll_interval.py +99 -0
- ofplang_run-0.1.0/tests/test_preflight.py +146 -0
- ofplang_run-0.1.0/tests/test_pure_data.py +136 -0
- ofplang_run-0.1.0/tests/test_reroute.py +142 -0
- ofplang_run-0.1.0/tests/test_rolling.py +122 -0
- ofplang_run-0.1.0/tests/test_runner.py +365 -0
- ofplang_run-0.1.0/tests/test_script.py +256 -0
- ofplang_run-0.1.0/tests/test_simulator.py +664 -0
- ofplang_run-0.1.0/tests/test_static_views.py +69 -0
- ofplang_run-0.1.0/tests/test_variance.py +132 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: ${{ matrix.python-version }}
|
|
21
|
+
cache: pip
|
|
22
|
+
# `ofplang-schedule` and `ofplang-validate` are sibling repos, not on PyPI.
|
|
23
|
+
# Install them from git so the runner's schedule-dependent tests and the CLI
|
|
24
|
+
# front door resolve. validate first: schedule itself depends on it.
|
|
25
|
+
- run: pip install "ofplang-validate @ git+https://github.com/ofplang/validate.git@main"
|
|
26
|
+
- run: pip install "ofplang-schedule @ git+https://github.com/ofplang/schedule.git@main"
|
|
27
|
+
- run: pip install -e ".[test]"
|
|
28
|
+
- run: pytest
|
|
29
|
+
|
|
30
|
+
lint:
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
- uses: actions/setup-python@v5
|
|
35
|
+
with:
|
|
36
|
+
python-version: "3.12"
|
|
37
|
+
cache: pip
|
|
38
|
+
# ruff is a static analyzer: it needs neither the project installed nor the
|
|
39
|
+
# (PyPI-absent) ofplang-schedule dependency resolved, so install ruff alone.
|
|
40
|
+
- run: pip install "ruff>=0.16"
|
|
41
|
+
- run: ruff check ofplang tests
|
|
42
|
+
|
|
43
|
+
typecheck:
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
|
+
- uses: actions/setup-python@v5
|
|
48
|
+
with:
|
|
49
|
+
python-version: "3.12"
|
|
50
|
+
cache: pip
|
|
51
|
+
# mypy resolves `ofplang.schedule` and `ofplang.validate`, so install both
|
|
52
|
+
# sibling repos from git too (validate first: schedule depends on it).
|
|
53
|
+
- run: pip install "ofplang-validate @ git+https://github.com/ofplang/validate.git@main"
|
|
54
|
+
- run: pip install "ofplang-schedule @ git+https://github.com/ofplang/schedule.git@main"
|
|
55
|
+
- run: pip install -e ".[dev]"
|
|
56
|
+
- run: mypy
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
# Tag-driven release via PyPI Trusted Publishing (OIDC) — no API tokens.
|
|
4
|
+
#
|
|
5
|
+
# Version comes from the pushed git tag via setuptools-scm (`v0.1.0` -> 0.1.0).
|
|
6
|
+
# Routing:
|
|
7
|
+
# - a pre-release tag containing `rc` (e.g. v0.1.0rc1) -> TestPyPI (rehearsal)
|
|
8
|
+
# - any other `v*` tag (e.g. v0.1.0) -> PyPI (production)
|
|
9
|
+
# - manual workflow_dispatch -> choose the target explicitly (run it from a
|
|
10
|
+
# tagged commit, else setuptools-scm yields a dev+local version PyPI rejects)
|
|
11
|
+
#
|
|
12
|
+
# PyPI-side setup (done once, by the project owner): register a Trusted Publisher
|
|
13
|
+
# for owner=ofplang, repo=run, workflow=publish.yml, environment=pypi
|
|
14
|
+
# (and environment=testpypi on TestPyPI). First release: register as "pending".
|
|
15
|
+
#
|
|
16
|
+
# Publish order across the org: validate -> schedule -> run. Do not publish this
|
|
17
|
+
# before ofplang-validate and ofplang-schedule are available on the same index.
|
|
18
|
+
|
|
19
|
+
on:
|
|
20
|
+
push:
|
|
21
|
+
tags: ["v*"]
|
|
22
|
+
workflow_dispatch:
|
|
23
|
+
inputs:
|
|
24
|
+
target:
|
|
25
|
+
description: "Publish target"
|
|
26
|
+
type: choice
|
|
27
|
+
options: [testpypi, pypi]
|
|
28
|
+
default: testpypi
|
|
29
|
+
|
|
30
|
+
jobs:
|
|
31
|
+
build:
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v4
|
|
35
|
+
with:
|
|
36
|
+
# setuptools-scm derives the version from tags + history.
|
|
37
|
+
fetch-depth: 0
|
|
38
|
+
- uses: actions/setup-python@v5
|
|
39
|
+
with:
|
|
40
|
+
python-version: "3.12"
|
|
41
|
+
- run: python -m pip install --upgrade build twine
|
|
42
|
+
- run: python -m build
|
|
43
|
+
- run: twine check dist/*
|
|
44
|
+
# Guard against ambiguous tags: the built version (from setuptools-scm) must
|
|
45
|
+
# equal the pushed tag — e.g. tag v0.1.0 must build 0.1.0. Catches a commit
|
|
46
|
+
# carrying two tags (an rc and a release) where git describe can resolve to
|
|
47
|
+
# the wrong one and publish the wrong artifact.
|
|
48
|
+
- name: Verify built version matches tag
|
|
49
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
50
|
+
shell: bash
|
|
51
|
+
run: |
|
|
52
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
53
|
+
built="$(ls dist/*.tar.gz | sed -E 's#.*/[^/]*-([0-9][^-]*)\.tar\.gz#\1#')"
|
|
54
|
+
echo "tag=$tag built=$built"
|
|
55
|
+
if [ "$tag" != "$built" ]; then
|
|
56
|
+
echo "::error::built version $built != tag $tag (ambiguous or misplaced tag?)"
|
|
57
|
+
exit 1
|
|
58
|
+
fi
|
|
59
|
+
- uses: actions/upload-artifact@v4
|
|
60
|
+
with:
|
|
61
|
+
name: dist
|
|
62
|
+
path: dist/
|
|
63
|
+
|
|
64
|
+
publish-testpypi:
|
|
65
|
+
needs: build
|
|
66
|
+
if: >-
|
|
67
|
+
github.event_name == 'workflow_dispatch' && inputs.target == 'testpypi'
|
|
68
|
+
|| (github.event_name == 'push' && contains(github.ref_name, 'rc'))
|
|
69
|
+
runs-on: ubuntu-latest
|
|
70
|
+
environment: testpypi
|
|
71
|
+
permissions:
|
|
72
|
+
id-token: write
|
|
73
|
+
steps:
|
|
74
|
+
- uses: actions/download-artifact@v4
|
|
75
|
+
with:
|
|
76
|
+
name: dist
|
|
77
|
+
path: dist/
|
|
78
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
79
|
+
with:
|
|
80
|
+
repository-url: https://test.pypi.org/legacy/
|
|
81
|
+
|
|
82
|
+
publish-pypi:
|
|
83
|
+
needs: build
|
|
84
|
+
if: >-
|
|
85
|
+
github.event_name == 'workflow_dispatch' && inputs.target == 'pypi'
|
|
86
|
+
|| (github.event_name == 'push' && !contains(github.ref_name, 'rc'))
|
|
87
|
+
runs-on: ubuntu-latest
|
|
88
|
+
environment: pypi
|
|
89
|
+
permissions:
|
|
90
|
+
id-token: write
|
|
91
|
+
steps:
|
|
92
|
+
- uses: actions/download-artifact@v4
|
|
93
|
+
with:
|
|
94
|
+
name: dist
|
|
95
|
+
path: dist/
|
|
96
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kazunari Kaizu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ofplang-run
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Object-flow programming language v0 runner
|
|
5
|
+
Author-email: Kazunari Kaizu <kwaizu@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ofplang/run
|
|
8
|
+
Project-URL: Repository, https://github.com/ofplang/run
|
|
9
|
+
Keywords: ofplang,dataflow,workflow,runner,rolling-horizon
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: PyYAML>=6.0
|
|
24
|
+
Requires-Dist: ofplang-schedule
|
|
25
|
+
Requires-Dist: ofplang-validate
|
|
26
|
+
Provides-Extra: test
|
|
27
|
+
Requires-Dist: pytest>=7.0; extra == "test"
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
30
|
+
Requires-Dist: ruff>=0.16; extra == "dev"
|
|
31
|
+
Requires-Dist: mypy>=1.11; extra == "dev"
|
|
32
|
+
Requires-Dist: types-PyYAML; extra == "dev"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# ofplang run
|
|
36
|
+
|
|
37
|
+
[](https://github.com/ofplang/run/actions/workflows/ci.yml)
|
|
38
|
+
[](https://pypi.org/project/ofplang-run/)
|
|
39
|
+
|
|
40
|
+
A runner for **Object-flow Programming Language v0** — a YAML-based dataflow
|
|
41
|
+
workflow IR with linear Object tracking. The language is defined in the
|
|
42
|
+
[ofplang/spec](https://github.com/ofplang/spec) repository.
|
|
43
|
+
|
|
44
|
+
The runner drives an ofplang v0 workflow to completion against an execution
|
|
45
|
+
backend, emitting an execution status document (spec §6/§7) as it progresses and
|
|
46
|
+
routing typed **view values** through the workflow. It runs on a **simulator** — a
|
|
47
|
+
simulated physical backend — so a full workflow can be exercised end to end
|
|
48
|
+
without real hardware; the same dispatch contract targets real hardware later.
|
|
49
|
+
|
|
50
|
+
> **Status:** the simulator, the runner, and a typed (dummy) value layer are
|
|
51
|
+
> implemented.
|
|
52
|
+
>
|
|
53
|
+
> - **Simulator** (`ofplang.run.simulator`) — a physical backend: devices, spots,
|
|
54
|
+
> transporters, and timed operations advanced on a virtual clock. It validates
|
|
55
|
+
> every dispatch (an inconsistent plan is rejected), models timed device up/down
|
|
56
|
+
> and injected operation failure, and at completion produces each operation's
|
|
57
|
+
> output view values via a **device model** (the built-in `default_device_model`
|
|
58
|
+
> fills type defaults and carries Object outputs through from their `objects.map`
|
|
59
|
+
> inputs; a custom / real model computes them).
|
|
60
|
+
> - **Runner** (`ofplang.run.runner`) — two ways to drive a backend:
|
|
61
|
+
> - **`replay`** runs a given execution plan (spec §6) on the backend verbatim.
|
|
62
|
+
> - **`run`** is a rolling-horizon loop: it calls
|
|
63
|
+
> [`ofplang.schedule`](https://github.com/ofplang/schedule) each tick,
|
|
64
|
+
> dispatches the work that can start now, advances the clock, and polls —
|
|
65
|
+
> replanning from the committed history as it goes. It re-routes around a
|
|
66
|
+
> downed device, polls at a fixed interval with completion-time estimation,
|
|
67
|
+
> absorbs duration variance (an operation running longer or shorter than
|
|
68
|
+
> planned), and stops the whole run if any activity fails (marking the abandoned
|
|
69
|
+
> work cancelled). Device up/down, operation failure, and duration variance are
|
|
70
|
+
> scenario concerns injected from Python (not CLI flags).
|
|
71
|
+
> - **Value layer** — the runner resolves each port's type and view schema (§7),
|
|
72
|
+
> routes typed view values along the workflow's arcs (producer output → consumer
|
|
73
|
+
> input, across nested composites), contract-checks them, and assembles the
|
|
74
|
+
> whole-workflow outputs. A caller supplies the whole-workflow I/O as a single
|
|
75
|
+
> **run boundary** (`--boundary`): one document with a per-port `{spot, view}`
|
|
76
|
+
> descriptor — `spot` places a boundary Object (§6.8), `view` supplies an input
|
|
77
|
+
> value — for the workflow's entry inputs and final outputs. Unsupplied entry
|
|
78
|
+
> views default. A workflow-embedded static literal (`bind: {port: {value: …}}`,
|
|
79
|
+
> §11) is seeded as that consumer input's value in place of a default. At run end
|
|
80
|
+
> the produced output views are echoed back into a result boundary of the same
|
|
81
|
+
> schema (`--boundary-out`). Non-script values are typed but still dummy — a real
|
|
82
|
+
> device backend plugs into the same seam later.
|
|
83
|
+
> - **Python script processes** (spec §22, `python_script_processes`) — an atomic
|
|
84
|
+
> Pure-Data process may carry a `script: {language: python, code: …}` section.
|
|
85
|
+
> The built-in device model runs it: the input port values are bound as locals,
|
|
86
|
+
> the code returns a mapping of the declared outputs, and those become the
|
|
87
|
+
> operation's computed output values — the first genuine (non-dummy) computation
|
|
88
|
+
> in the value layer. A script that raises, returns the wrong output names, or
|
|
89
|
+
> returns a non-conformant value fails runtime verification (§22.2) and stops the
|
|
90
|
+
> run gracefully like any other activity failure (the failed activity is marked
|
|
91
|
+
> `failed`, its unstarted successors `cancelled`, exit 1). The script runs inline
|
|
92
|
+
> in real time but advances no simulation time; its outputs appear when the
|
|
93
|
+
> operation completes at its `end`, and the environment mode's `duration` is the
|
|
94
|
+
> scheduler's estimate of the compute cost. Scripts run with full Python builtins
|
|
95
|
+
> and no import restriction (§22.3 permits an implementation to restrict these;
|
|
96
|
+
> this one does not).
|
|
97
|
+
> - **Contracts** (spec §9) — an atomic process may declare `contracts` with
|
|
98
|
+
> `requires` (preconditions over `inputs.*.view`) and `ensures` (postconditions
|
|
99
|
+
> over `inputs.*.view` and `outputs.*.view`). The runner evaluates them at
|
|
100
|
+
> runtime against the actual view values: `requires` before the operation runs
|
|
101
|
+
> (a violation stops it before dispatch), `ensures` after it completes. An atomic
|
|
102
|
+
> `requires` that references only run/graph-phase inputs (§5.6) is knowable at run
|
|
103
|
+
> start, so it is checked there as a *preflight* — before any work is dispatched —
|
|
104
|
+
> rather than waiting for that (possibly late) operation. A
|
|
105
|
+
> violation — or a runtime evaluation error — stops the run gracefully like any
|
|
106
|
+
> activity failure (`failed` + downstream `cancelled`, exit 1). Static /
|
|
107
|
+
> graph-time contract checking (a fully-constant contract that is statically
|
|
108
|
+
> false, type errors, bad references) is `ofplang-validate`'s job and is not
|
|
109
|
+
> repeated here. Contracts are checked on atomic processes and on the top-level
|
|
110
|
+
> entry composite (`main`): the entry composite's `requires` is evaluated at run
|
|
111
|
+
> start over the whole-workflow inputs (a violation stops the run before any work
|
|
112
|
+
> runs) and its `ensures` at run end over the whole-workflow inputs and outputs (a
|
|
113
|
+
> violation marks the completed run failed). Nested composite contracts are checked
|
|
114
|
+
> too, at each composite invocation's value boundary: its `requires` once its inputs
|
|
115
|
+
> are available (for an input fed by an upstream process this is mid-run, before the
|
|
116
|
+
> composite's body runs) and its `ensures` once its outputs are, a violation
|
|
117
|
+
> stopping the run gracefully at the composite boundary (its not-yet-run body
|
|
118
|
+
> cancelled). Because non-script process outputs are still typed defaults, `ensures`
|
|
119
|
+
> bites mainly on script processes and boundary-supplied inputs until a real device
|
|
120
|
+
> backend computes physical outputs.
|
|
121
|
+
> - **Failure observability** — when a run stops (an injected activity failure, a
|
|
122
|
+
> script error, or a contract violation), the reason is exposed as a structured
|
|
123
|
+
> `RollingRunner.failure` (a machine-readable `kind` code, e.g. `contract_requires`
|
|
124
|
+
> / `script_error`, plus a human-readable `detail`, the `subject`, and the time),
|
|
125
|
+
> and the CLI prints it to stderr. The status document itself stays a valid §6
|
|
126
|
+
> document (the reason is out of band). An optional `contract_observer` callback is
|
|
127
|
+
> invoked for every contract check (held or violated) — a trace hook for debugging.
|
|
128
|
+
> - **Static view values** (spec §7.4) — a type whose view field declares a `value:`
|
|
129
|
+
> fixes that field to a constant for every value of the type. The runner projects it
|
|
130
|
+
> onto every view record it routes, so a Python script reading the field and a
|
|
131
|
+
> contract referencing it both see the static value (not a runtime default). A value
|
|
132
|
+
> that carries a conflicting one is forced to the static value.
|
|
133
|
+
|
|
134
|
+
## Install
|
|
135
|
+
|
|
136
|
+
```sh
|
|
137
|
+
pip install ofplang-run
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Requires Python 3.10+. Runtime dependencies (pulled in automatically) are PyYAML,
|
|
141
|
+
the sibling [`ofplang-schedule`](https://pypi.org/project/ofplang-schedule/) that
|
|
142
|
+
`run` (rolling-horizon) calls each tick, and
|
|
143
|
+
[`ofplang-validate`](https://pypi.org/project/ofplang-validate/) used by the CLI
|
|
144
|
+
front door. The runner *library* never imports validate (and the per-tick replans
|
|
145
|
+
never re-validate), so it stays a one-shot CLI front door.
|
|
146
|
+
|
|
147
|
+
For development, install editable with the test extra from a clone:
|
|
148
|
+
|
|
149
|
+
```sh
|
|
150
|
+
pip install -e ".[test]"
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Command line
|
|
154
|
+
|
|
155
|
+
```sh
|
|
156
|
+
ofp-run run <workflow> --env <env>
|
|
157
|
+
[--boundary DOC] [--boundary-out FILE]
|
|
158
|
+
[--poll-interval D] [--margin M] [--seed N] [-o OUT]
|
|
159
|
+
ofp-run replay <plan> --env <env> [-o OUT]
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
`run` drives a v0 workflow to completion by replanning as it goes: each tick it
|
|
163
|
+
renders the committed history as a status, calls the scheduler, and dispatches the
|
|
164
|
+
newly-runnable work. `--boundary` supplies the whole-workflow I/O as one document —
|
|
165
|
+
a `boundary:` mapping with a `{spot, view}` descriptor per entry input / final
|
|
166
|
+
output port. `spot` places a boundary Object on an environment spot (spec §6.8;
|
|
167
|
+
Object ports only); `view` supplies an input's view value (unsupplied entry views
|
|
168
|
+
default). The runner projects it into the scheduler's interface (spots only, so the
|
|
169
|
+
scheduler stays value-independent) and the seeded input values. `--boundary-out`
|
|
170
|
+
writes the result boundary — the same schema with each produced output's `view`
|
|
171
|
+
filled in — a run-local artifact, separate from the value-free status document. On
|
|
172
|
+
completion each pinned Object output is checked to have reached its declared spot.
|
|
173
|
+
`--poll-interval` sets the fixed polling interval (default 1). `replay` runs a plan
|
|
174
|
+
produced by `ofp-schedule` verbatim on the simulator (no value layer). Both write
|
|
175
|
+
the final execution status as YAML (`-o`, else stdout). Exit codes: `0` success,
|
|
176
|
+
`1` execution failed (an activity failed, or a replan is infeasible), `2`
|
|
177
|
+
usage/input error.
|
|
178
|
+
|
|
179
|
+
This tool is also intended to be exposed as the `run` subcommand of the umbrella
|
|
180
|
+
`ofp` CLI (a separate repository in the `ofplang` organization).
|
|
181
|
+
|
|
182
|
+
The package lives under the `ofplang` PEP 420 namespace (`ofplang.run`), shared
|
|
183
|
+
across the organization's tools.
|
|
184
|
+
|
|
185
|
+
## Tests
|
|
186
|
+
|
|
187
|
+
```sh
|
|
188
|
+
pytest
|
|
189
|
+
```
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# ofplang run
|
|
2
|
+
|
|
3
|
+
[](https://github.com/ofplang/run/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/ofplang-run/)
|
|
5
|
+
|
|
6
|
+
A runner for **Object-flow Programming Language v0** — a YAML-based dataflow
|
|
7
|
+
workflow IR with linear Object tracking. The language is defined in the
|
|
8
|
+
[ofplang/spec](https://github.com/ofplang/spec) repository.
|
|
9
|
+
|
|
10
|
+
The runner drives an ofplang v0 workflow to completion against an execution
|
|
11
|
+
backend, emitting an execution status document (spec §6/§7) as it progresses and
|
|
12
|
+
routing typed **view values** through the workflow. It runs on a **simulator** — a
|
|
13
|
+
simulated physical backend — so a full workflow can be exercised end to end
|
|
14
|
+
without real hardware; the same dispatch contract targets real hardware later.
|
|
15
|
+
|
|
16
|
+
> **Status:** the simulator, the runner, and a typed (dummy) value layer are
|
|
17
|
+
> implemented.
|
|
18
|
+
>
|
|
19
|
+
> - **Simulator** (`ofplang.run.simulator`) — a physical backend: devices, spots,
|
|
20
|
+
> transporters, and timed operations advanced on a virtual clock. It validates
|
|
21
|
+
> every dispatch (an inconsistent plan is rejected), models timed device up/down
|
|
22
|
+
> and injected operation failure, and at completion produces each operation's
|
|
23
|
+
> output view values via a **device model** (the built-in `default_device_model`
|
|
24
|
+
> fills type defaults and carries Object outputs through from their `objects.map`
|
|
25
|
+
> inputs; a custom / real model computes them).
|
|
26
|
+
> - **Runner** (`ofplang.run.runner`) — two ways to drive a backend:
|
|
27
|
+
> - **`replay`** runs a given execution plan (spec §6) on the backend verbatim.
|
|
28
|
+
> - **`run`** is a rolling-horizon loop: it calls
|
|
29
|
+
> [`ofplang.schedule`](https://github.com/ofplang/schedule) each tick,
|
|
30
|
+
> dispatches the work that can start now, advances the clock, and polls —
|
|
31
|
+
> replanning from the committed history as it goes. It re-routes around a
|
|
32
|
+
> downed device, polls at a fixed interval with completion-time estimation,
|
|
33
|
+
> absorbs duration variance (an operation running longer or shorter than
|
|
34
|
+
> planned), and stops the whole run if any activity fails (marking the abandoned
|
|
35
|
+
> work cancelled). Device up/down, operation failure, and duration variance are
|
|
36
|
+
> scenario concerns injected from Python (not CLI flags).
|
|
37
|
+
> - **Value layer** — the runner resolves each port's type and view schema (§7),
|
|
38
|
+
> routes typed view values along the workflow's arcs (producer output → consumer
|
|
39
|
+
> input, across nested composites), contract-checks them, and assembles the
|
|
40
|
+
> whole-workflow outputs. A caller supplies the whole-workflow I/O as a single
|
|
41
|
+
> **run boundary** (`--boundary`): one document with a per-port `{spot, view}`
|
|
42
|
+
> descriptor — `spot` places a boundary Object (§6.8), `view` supplies an input
|
|
43
|
+
> value — for the workflow's entry inputs and final outputs. Unsupplied entry
|
|
44
|
+
> views default. A workflow-embedded static literal (`bind: {port: {value: …}}`,
|
|
45
|
+
> §11) is seeded as that consumer input's value in place of a default. At run end
|
|
46
|
+
> the produced output views are echoed back into a result boundary of the same
|
|
47
|
+
> schema (`--boundary-out`). Non-script values are typed but still dummy — a real
|
|
48
|
+
> device backend plugs into the same seam later.
|
|
49
|
+
> - **Python script processes** (spec §22, `python_script_processes`) — an atomic
|
|
50
|
+
> Pure-Data process may carry a `script: {language: python, code: …}` section.
|
|
51
|
+
> The built-in device model runs it: the input port values are bound as locals,
|
|
52
|
+
> the code returns a mapping of the declared outputs, and those become the
|
|
53
|
+
> operation's computed output values — the first genuine (non-dummy) computation
|
|
54
|
+
> in the value layer. A script that raises, returns the wrong output names, or
|
|
55
|
+
> returns a non-conformant value fails runtime verification (§22.2) and stops the
|
|
56
|
+
> run gracefully like any other activity failure (the failed activity is marked
|
|
57
|
+
> `failed`, its unstarted successors `cancelled`, exit 1). The script runs inline
|
|
58
|
+
> in real time but advances no simulation time; its outputs appear when the
|
|
59
|
+
> operation completes at its `end`, and the environment mode's `duration` is the
|
|
60
|
+
> scheduler's estimate of the compute cost. Scripts run with full Python builtins
|
|
61
|
+
> and no import restriction (§22.3 permits an implementation to restrict these;
|
|
62
|
+
> this one does not).
|
|
63
|
+
> - **Contracts** (spec §9) — an atomic process may declare `contracts` with
|
|
64
|
+
> `requires` (preconditions over `inputs.*.view`) and `ensures` (postconditions
|
|
65
|
+
> over `inputs.*.view` and `outputs.*.view`). The runner evaluates them at
|
|
66
|
+
> runtime against the actual view values: `requires` before the operation runs
|
|
67
|
+
> (a violation stops it before dispatch), `ensures` after it completes. An atomic
|
|
68
|
+
> `requires` that references only run/graph-phase inputs (§5.6) is knowable at run
|
|
69
|
+
> start, so it is checked there as a *preflight* — before any work is dispatched —
|
|
70
|
+
> rather than waiting for that (possibly late) operation. A
|
|
71
|
+
> violation — or a runtime evaluation error — stops the run gracefully like any
|
|
72
|
+
> activity failure (`failed` + downstream `cancelled`, exit 1). Static /
|
|
73
|
+
> graph-time contract checking (a fully-constant contract that is statically
|
|
74
|
+
> false, type errors, bad references) is `ofplang-validate`'s job and is not
|
|
75
|
+
> repeated here. Contracts are checked on atomic processes and on the top-level
|
|
76
|
+
> entry composite (`main`): the entry composite's `requires` is evaluated at run
|
|
77
|
+
> start over the whole-workflow inputs (a violation stops the run before any work
|
|
78
|
+
> runs) and its `ensures` at run end over the whole-workflow inputs and outputs (a
|
|
79
|
+
> violation marks the completed run failed). Nested composite contracts are checked
|
|
80
|
+
> too, at each composite invocation's value boundary: its `requires` once its inputs
|
|
81
|
+
> are available (for an input fed by an upstream process this is mid-run, before the
|
|
82
|
+
> composite's body runs) and its `ensures` once its outputs are, a violation
|
|
83
|
+
> stopping the run gracefully at the composite boundary (its not-yet-run body
|
|
84
|
+
> cancelled). Because non-script process outputs are still typed defaults, `ensures`
|
|
85
|
+
> bites mainly on script processes and boundary-supplied inputs until a real device
|
|
86
|
+
> backend computes physical outputs.
|
|
87
|
+
> - **Failure observability** — when a run stops (an injected activity failure, a
|
|
88
|
+
> script error, or a contract violation), the reason is exposed as a structured
|
|
89
|
+
> `RollingRunner.failure` (a machine-readable `kind` code, e.g. `contract_requires`
|
|
90
|
+
> / `script_error`, plus a human-readable `detail`, the `subject`, and the time),
|
|
91
|
+
> and the CLI prints it to stderr. The status document itself stays a valid §6
|
|
92
|
+
> document (the reason is out of band). An optional `contract_observer` callback is
|
|
93
|
+
> invoked for every contract check (held or violated) — a trace hook for debugging.
|
|
94
|
+
> - **Static view values** (spec §7.4) — a type whose view field declares a `value:`
|
|
95
|
+
> fixes that field to a constant for every value of the type. The runner projects it
|
|
96
|
+
> onto every view record it routes, so a Python script reading the field and a
|
|
97
|
+
> contract referencing it both see the static value (not a runtime default). A value
|
|
98
|
+
> that carries a conflicting one is forced to the static value.
|
|
99
|
+
|
|
100
|
+
## Install
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
pip install ofplang-run
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Requires Python 3.10+. Runtime dependencies (pulled in automatically) are PyYAML,
|
|
107
|
+
the sibling [`ofplang-schedule`](https://pypi.org/project/ofplang-schedule/) that
|
|
108
|
+
`run` (rolling-horizon) calls each tick, and
|
|
109
|
+
[`ofplang-validate`](https://pypi.org/project/ofplang-validate/) used by the CLI
|
|
110
|
+
front door. The runner *library* never imports validate (and the per-tick replans
|
|
111
|
+
never re-validate), so it stays a one-shot CLI front door.
|
|
112
|
+
|
|
113
|
+
For development, install editable with the test extra from a clone:
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
pip install -e ".[test]"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Command line
|
|
120
|
+
|
|
121
|
+
```sh
|
|
122
|
+
ofp-run run <workflow> --env <env>
|
|
123
|
+
[--boundary DOC] [--boundary-out FILE]
|
|
124
|
+
[--poll-interval D] [--margin M] [--seed N] [-o OUT]
|
|
125
|
+
ofp-run replay <plan> --env <env> [-o OUT]
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`run` drives a v0 workflow to completion by replanning as it goes: each tick it
|
|
129
|
+
renders the committed history as a status, calls the scheduler, and dispatches the
|
|
130
|
+
newly-runnable work. `--boundary` supplies the whole-workflow I/O as one document —
|
|
131
|
+
a `boundary:` mapping with a `{spot, view}` descriptor per entry input / final
|
|
132
|
+
output port. `spot` places a boundary Object on an environment spot (spec §6.8;
|
|
133
|
+
Object ports only); `view` supplies an input's view value (unsupplied entry views
|
|
134
|
+
default). The runner projects it into the scheduler's interface (spots only, so the
|
|
135
|
+
scheduler stays value-independent) and the seeded input values. `--boundary-out`
|
|
136
|
+
writes the result boundary — the same schema with each produced output's `view`
|
|
137
|
+
filled in — a run-local artifact, separate from the value-free status document. On
|
|
138
|
+
completion each pinned Object output is checked to have reached its declared spot.
|
|
139
|
+
`--poll-interval` sets the fixed polling interval (default 1). `replay` runs a plan
|
|
140
|
+
produced by `ofp-schedule` verbatim on the simulator (no value layer). Both write
|
|
141
|
+
the final execution status as YAML (`-o`, else stdout). Exit codes: `0` success,
|
|
142
|
+
`1` execution failed (an activity failed, or a replan is infeasible), `2`
|
|
143
|
+
usage/input error.
|
|
144
|
+
|
|
145
|
+
This tool is also intended to be exposed as the `run` subcommand of the umbrella
|
|
146
|
+
`ofp` CLI (a separate repository in the `ofplang` organization).
|
|
147
|
+
|
|
148
|
+
The package lives under the `ofplang` PEP 420 namespace (`ofplang.run`), shared
|
|
149
|
+
across the organization's tools.
|
|
150
|
+
|
|
151
|
+
## Tests
|
|
152
|
+
|
|
153
|
+
```sh
|
|
154
|
+
pytest
|
|
155
|
+
```
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Environment for count_chain.workflow.yaml: `inc` is a device-less Pure-Data-only
|
|
2
|
+
# process (no devices, no spots). The workflow uses no physical resources, but the
|
|
3
|
+
# scheduler requires at least one device to exist, so a single unused rack is
|
|
4
|
+
# declared (the pure-data ops never touch it).
|
|
5
|
+
time:
|
|
6
|
+
unit: second
|
|
7
|
+
|
|
8
|
+
devices:
|
|
9
|
+
- { id: rack, spots: [slot] }
|
|
10
|
+
transporters:
|
|
11
|
+
- { id: arm }
|
|
12
|
+
transports: []
|
|
13
|
+
|
|
14
|
+
processes:
|
|
15
|
+
inc:
|
|
16
|
+
modes:
|
|
17
|
+
- { id: v0, duration: 1 }
|
|
18
|
+
|
|
19
|
+
objective:
|
|
20
|
+
kind: makespan
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Pure-Data chain for the job / device-model example (see examples/render_job_run.py):
|
|
2
|
+
# a `Count` value (view {value: Int}) flows from the supplied job, through two
|
|
3
|
+
# device-less `inc` steps, to the returned output. With a device model that adds one
|
|
4
|
+
# each step, the entry value is computed through to the final output -- demonstrating
|
|
5
|
+
# a supplied input propagating through the backend. No object flow, so no interface
|
|
6
|
+
# is needed; it pairs with count_chain.env.yaml.
|
|
7
|
+
spec_version: "0.0"
|
|
8
|
+
|
|
9
|
+
types:
|
|
10
|
+
Count:
|
|
11
|
+
domain: data
|
|
12
|
+
view:
|
|
13
|
+
value: { type: Int }
|
|
14
|
+
|
|
15
|
+
processes:
|
|
16
|
+
inc:
|
|
17
|
+
kind: atomic
|
|
18
|
+
inputs:
|
|
19
|
+
x: { type: Count, phase: data }
|
|
20
|
+
outputs:
|
|
21
|
+
y: { type: Count, phase: data }
|
|
22
|
+
|
|
23
|
+
main:
|
|
24
|
+
kind: composite
|
|
25
|
+
inputs:
|
|
26
|
+
start: { type: Count, phase: data }
|
|
27
|
+
outputs:
|
|
28
|
+
result: { type: Count, phase: data }
|
|
29
|
+
body:
|
|
30
|
+
nodes:
|
|
31
|
+
- id: S1
|
|
32
|
+
process: inc
|
|
33
|
+
bind:
|
|
34
|
+
x: { from: inputs.start }
|
|
35
|
+
- id: S2
|
|
36
|
+
process: inc
|
|
37
|
+
bind:
|
|
38
|
+
x: { from: S1.y }
|
|
39
|
+
returns:
|
|
40
|
+
result: { from: S2.y }
|
|
41
|
+
|
|
42
|
+
entry: main
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Execution environment for pure_data.workflow.yaml (§5). The plate is loaded on
|
|
2
|
+
# `loader`, measured on `reader`, and finished on `printer`; `arm` moves it along
|
|
3
|
+
# the line. `analyze` is a device-less Pure-Data-only mode with a duration of 0
|
|
4
|
+
# (allowed for a device-less mode, §5.5) -- it holds nothing and takes no time.
|
|
5
|
+
time:
|
|
6
|
+
unit: second
|
|
7
|
+
|
|
8
|
+
devices:
|
|
9
|
+
- id: loader
|
|
10
|
+
spots: [stage]
|
|
11
|
+
- id: reader
|
|
12
|
+
spots: [stage]
|
|
13
|
+
- id: printer
|
|
14
|
+
spots: [stage]
|
|
15
|
+
|
|
16
|
+
transporters:
|
|
17
|
+
- id: arm
|
|
18
|
+
|
|
19
|
+
transports:
|
|
20
|
+
- { transporter: arm, from: loader.stage, to: reader.stage, duration: 2 }
|
|
21
|
+
- { transporter: arm, from: reader.stage, to: printer.stage, duration: 2 }
|
|
22
|
+
|
|
23
|
+
processes:
|
|
24
|
+
|
|
25
|
+
measure:
|
|
26
|
+
modes:
|
|
27
|
+
- devices: [reader]
|
|
28
|
+
duration: 4
|
|
29
|
+
input_spots: { plate: reader.stage }
|
|
30
|
+
output_spots: { plate_out: reader.stage } # in-place transform
|
|
31
|
+
|
|
32
|
+
# Device-less Pure-Data-only mode: no devices, no spots, zero duration.
|
|
33
|
+
analyze:
|
|
34
|
+
modes:
|
|
35
|
+
- { id: mean_v1, duration: 0 }
|
|
36
|
+
|
|
37
|
+
finish:
|
|
38
|
+
modes:
|
|
39
|
+
- devices: [printer]
|
|
40
|
+
duration: 3
|
|
41
|
+
input_spots: { plate_in: printer.stage }
|
|
42
|
+
|
|
43
|
+
objective:
|
|
44
|
+
kind: makespan
|