@vention/vention-skills 0.9.18 → 0.9.20
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.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/skills/vention-design/SKILL.md +1 -1
- package/skills/vention-machine-logic/SKILL.md +1 -1
- package/skills/vention-machine-logic-hmi/SKILL.md +2 -2
- package/skills/vention-machine-logic-hmi/examples/indexing-station-hmi/customui/package.json +2 -2
- package/skills/vention-machine-logic-hmi/examples/indexing-station-hmi/customui/src/pages/operate-page.tsx +130 -11
- package/skills/vention-machine-logic-hmi/examples/indexing-station-hmi/customui/src/station.ts +9 -0
- package/skills/vention-machine-logic-hmi/examples/indexing-station-hmi/proto/app.proto +4 -2
- package/skills/vention-machine-logic-hmi/examples/indexing-station-hmi/server.py +18 -3
- package/skills/vention-monitoring/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Vention skills
|
|
2
2
|
|
|
3
|
-
Version 0.9.
|
|
3
|
+
Version 0.9.20. Skills for coding agents that work with Vention machines, packaged as one plugin, `vention`, in the `vention-skills` marketplace.
|
|
4
4
|
|
|
5
5
|
| Skill | What it does |
|
|
6
6
|
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "vention",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.20",
|
|
5
5
|
"description": "Vention skills for coding agents: write MachineLogic applications, build machine designs, and diagnose deployed machines.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Vention"
|
|
@@ -3,7 +3,7 @@ name: vention-design
|
|
|
3
3
|
description: Create, build and edit Vention machine designs and design graphs for agentic layout. Use when the user wants to start a new design, lay out an automation cell, add or reposition components in a Vention design, generate a machine layout from a description, add or place a floor plan image under a design, list a design's versions, or inspect an existing design graph. Trigger on MachineBuilder designs, design graphs, assembly graphs, extrusions, actuators, robot mounting, cell footprints, floor plans, or a machine-builder URL.
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
|
-
version: "0.9.
|
|
6
|
+
version: "0.9.20"
|
|
7
7
|
author: Vention
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@ name: vention-machine-logic
|
|
|
3
3
|
description: Write, edit, validate, and deploy MachineLogic Python applications for Vention machines with the machine-logic-sdk and the Vention CLI. Load it before writing or changing any Python that talks to a Vention machine, however short, since device names and version-specific SDK calls come from here. Use to program a Vention machine or MachineMotion controller, drive actuators, conveyors, robots, pneumatics, or digital I/O, change an existing application, build a state machine, debug from logs, or pull, push, or link with the Vention CLI (vention or vn). Trigger on MachineLogic, machine-logic-sdk, MachineMotion, MachineBuilder applications, the Vention CLI, or a directory holding .machine-code-app-directory-info.json. Printing what the machine or robot holds now (positions, taught targets, calibration, I/O) is a program: files on disk hold only as-designed values. Not for explaining an existing machine or its configuration in words when no program is asked for, nor for Python that never touches the machine.
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
|
-
version: "0.9.
|
|
6
|
+
version: "0.9.20"
|
|
7
7
|
machine-logic-sdk: ">=3.0,<4"
|
|
8
8
|
author: Vention
|
|
9
9
|
---
|
|
@@ -3,7 +3,7 @@ name: vention-machine-logic-hmi
|
|
|
3
3
|
description: Build or edit the custom operator HMI of a MachineLogic application, the React touch screen in customui/ that runs in MachineBuilder and on the MachineMotion pendant, wired to the application's Python process through vention-communication. Use when the user asks for an operator screen, a custom HMI or UI, a touch panel, start and stop buttons, live machine state on screen, or recipes an operator picks, for a Vention machine or inside a directory holding .machine-code-app-directory-info.json. Uses @vention/machine-ui, @vention/machine-apps-components and @vention/machine-logic-ui-sdk. Do not use for the no-code HMI Builder, or for the Python motion logic alone (that is vention-machine-logic).
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
|
-
version: "0.9.
|
|
6
|
+
version: "0.9.20"
|
|
7
7
|
author: Vention
|
|
8
8
|
---
|
|
9
9
|
|
|
@@ -152,7 +152,7 @@ The machine-apps-components README is behind its code: `NavigationBar` takes `Na
|
|
|
152
152
|
|
|
153
153
|
- Wrap the app in `machineUiHmiTheme`.
|
|
154
154
|
- Give the app an Operate page at `/`, any setup pages it needs (such as Recipes), and a Logs page: a `LogsPanel` fed by the Events stream, as the example's `logs-page.tsx` does, where the operator finds out why a cycle faulted. Nav labels name the page and never echo a top-bar action, and their icons don't suggest one. Never "Run", "Start" or a play icon: beside the top bar's Start, the operator reads it as a second Start button.
|
|
155
|
-
- The Operate page is what the operator watches during a run, so it reads from across the cell. Copy the example's `operate-page.tsx` and adapt it rather than starting from a blank page.
|
|
155
|
+
- The Operate page is what the operator watches during a run, so it reads from across the cell. Copy the example's `operate-page.tsx` and adapt it rather than starting from a blank page. While a run is on, show from top to bottom: the step in progress named inside a ring that fills against the last cycle's time, beside the recipe and the cycles done and rate; the cycle's steps as a row of tiles, the current one lit; and the run's progress bar. Copy the example's StepRing, StepRail and CycleProgress as they are, and publish the step and the cycle's elapsed time on the status stream as its enter_step method does. A single `VentionProgressBar` cannot mark the cycles a Stop cancels. Below them go the settings for the next run (disabled while one runs) and one short paragraph on what Start and Stop do. Leave out what the operator can neither change nor act on, such as axis stops, taught positions or device names: they are the program's constants, not the run's. Lay it out with `Box` or `Stack` gaps, each readout a label above its value in theme text variants. Never set `fontSize` or `lineHeight`: a value sized by hand overlaps its label.
|
|
156
156
|
- Show each piece of information in one place only. The `StatusTopBar` already shows the state, so a page never repeats it, not even as a status indicator or an alert titled with the same word. A page adds only what the bar cannot say: the cause of a fault, what happens next, what to do.
|
|
157
157
|
- The screen fits the pendant without scrolling: keep `body { margin: 0 }` from the example's `index.html`, or the full-viewport root overflows it. Only the page area between the two bars scrolls.
|
|
158
158
|
- Exactly one way back to Control Center: the `NavigationBar`'s own button, which confirms before leaving. Keep `ui.showDefaultBackToControlCenterButton` `false` in `project.json`, or the pendant lays a second one over the screen.
|
package/skills/vention-machine-logic-hmi/examples/indexing-station-hmi/customui/package.json
CHANGED
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"@emotion/styled": "11.14.1",
|
|
16
16
|
"@mui/material": "5.16.7",
|
|
17
17
|
"@tabler/icons-react": "3.44.0",
|
|
18
|
-
"@vention/machine-apps-components": "1.
|
|
18
|
+
"@vention/machine-apps-components": "1.30.13",
|
|
19
19
|
"@vention/machine-logic-ui-sdk": "0.43.17",
|
|
20
|
-
"@vention/machine-ui": "
|
|
20
|
+
"@vention/machine-ui": "6.6.12",
|
|
21
21
|
"react": "17.0.2",
|
|
22
22
|
"react-dom": "17.0.2",
|
|
23
23
|
"react-router-dom": "6.30.3",
|
|
@@ -3,7 +3,7 @@ import { COLORS, VentionAlert, VentionSelect } from "@vention/machine-ui"
|
|
|
3
3
|
import { useEffect, useState } from "react"
|
|
4
4
|
import { client } from "../client"
|
|
5
5
|
import type { Recipe, StationStatus } from "../gen/app_pb"
|
|
6
|
-
import { canStart, errorText, isStopping } from "../station"
|
|
6
|
+
import { STEPS, canStart, errorText, isStopping } from "../station"
|
|
7
7
|
|
|
8
8
|
interface OperatePageProps {
|
|
9
9
|
status: StationStatus | undefined
|
|
@@ -14,6 +14,108 @@ interface OperatePageProps {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
const HATCHED = `repeating-linear-gradient(45deg, ${COLORS.slate[100]} 0 8px, ${COLORS.slate[200]} 8px 16px)`
|
|
17
|
+
const RING_RADIUS = 52
|
|
18
|
+
const RING_LENGTH = 2 * Math.PI * RING_RADIUS
|
|
19
|
+
const TICK_MS = 250
|
|
20
|
+
|
|
21
|
+
/** Seconds into the cycle, counted on from the last status so the ring moves between heartbeats. */
|
|
22
|
+
function useCycleElapsed(status: StationStatus | undefined): number {
|
|
23
|
+
const [received, setReceived] = useState({ seconds: 0, at: 0 })
|
|
24
|
+
const [now, setNow] = useState(() => performance.now())
|
|
25
|
+
const running = status !== undefined && status.step !== ""
|
|
26
|
+
useEffect(() => setReceived({ seconds: status?.cycleElapsedSeconds ?? 0, at: performance.now() }), [status])
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (!running) return
|
|
29
|
+
const timer = setInterval(() => setNow(performance.now()), TICK_MS)
|
|
30
|
+
return () => clearInterval(timer)
|
|
31
|
+
}, [running])
|
|
32
|
+
return running ? received.seconds + Math.max(0, now - received.at) / 1000 : 0
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The step in progress, inside a ring that fills as the cycle runs against the last cycle's time. */
|
|
36
|
+
function StepRing({ step, elapsed, expected, stopping }: { step: string; elapsed: number; expected: number; stopping: boolean }) {
|
|
37
|
+
const current = STEPS.find(candidate => candidate.name === step)
|
|
38
|
+
const StepIcon = current?.icon
|
|
39
|
+
const filled = expected > 0 ? Math.min(elapsed / expected, 1) : 0
|
|
40
|
+
const color = stopping ? COLORS.amber[500] : COLORS.blue[500]
|
|
41
|
+
return (
|
|
42
|
+
<Box sx={{ position: "relative", width: 220, height: 220, flexShrink: 0 }}>
|
|
43
|
+
<svg viewBox="0 0 120 120" width={220} height={220} aria-hidden="true">
|
|
44
|
+
<circle cx={60} cy={60} r={RING_RADIUS} fill="none" stroke={COLORS.slate[100]} strokeWidth={8} />
|
|
45
|
+
<circle
|
|
46
|
+
cx={60}
|
|
47
|
+
cy={60}
|
|
48
|
+
r={RING_RADIUS}
|
|
49
|
+
fill="none"
|
|
50
|
+
stroke={color}
|
|
51
|
+
strokeWidth={8}
|
|
52
|
+
strokeLinecap="round"
|
|
53
|
+
strokeDasharray={`${filled * RING_LENGTH} ${RING_LENGTH}`}
|
|
54
|
+
transform="rotate(-90 60 60)"
|
|
55
|
+
/>
|
|
56
|
+
</svg>
|
|
57
|
+
<Box
|
|
58
|
+
sx={{
|
|
59
|
+
position: "absolute",
|
|
60
|
+
inset: 0,
|
|
61
|
+
display: "flex",
|
|
62
|
+
flexDirection: "column",
|
|
63
|
+
alignItems: "center",
|
|
64
|
+
justifyContent: "center",
|
|
65
|
+
gap: 1,
|
|
66
|
+
}}
|
|
67
|
+
>
|
|
68
|
+
{StepIcon && <StepIcon size={36} color={color} />}
|
|
69
|
+
<Typography variant="heading18SemiBold">{current?.label ?? step}</Typography>
|
|
70
|
+
<Typography variant="paragraph18Medium">
|
|
71
|
+
{Math.floor(elapsed)} s{expected > 0 && ` of ~${Math.round(expected)} s`}
|
|
72
|
+
</Typography>
|
|
73
|
+
</Box>
|
|
74
|
+
</Box>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** The cycle's steps in order: done ones green, the current one lit, the rest grey. */
|
|
79
|
+
function StepRail({ step, stopping }: { step: string; stopping: boolean }) {
|
|
80
|
+
const currentIndex = STEPS.findIndex(candidate => candidate.name === step)
|
|
81
|
+
return (
|
|
82
|
+
<Box sx={{ display: "grid", gridTemplateColumns: `repeat(${STEPS.length}, minmax(0, 1fr))`, gap: 4 }}>
|
|
83
|
+
{STEPS.map(({ name, icon: StepIcon }, index) => {
|
|
84
|
+
const done = index < currentIndex
|
|
85
|
+
const current = index === currentIndex
|
|
86
|
+
const background = current ? (stopping ? COLORS.amber[500] : COLORS.blue[500]) : done ? COLORS.green[100] : COLORS.slate[100]
|
|
87
|
+
const iconColor = current ? "white" : done ? COLORS.green[700] : COLORS.slate[400]
|
|
88
|
+
return (
|
|
89
|
+
<Box key={name} sx={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 2 }}>
|
|
90
|
+
<Box
|
|
91
|
+
sx={{
|
|
92
|
+
width: "100%",
|
|
93
|
+
height: 64,
|
|
94
|
+
borderRadius: 2,
|
|
95
|
+
background,
|
|
96
|
+
display: "flex",
|
|
97
|
+
alignItems: "center",
|
|
98
|
+
justifyContent: "center",
|
|
99
|
+
}}
|
|
100
|
+
>
|
|
101
|
+
<StepIcon size={32} color={iconColor} />
|
|
102
|
+
</Box>
|
|
103
|
+
<Typography variant={current ? "heading18SemiBold" : "paragraph18Medium"}>{name}</Typography>
|
|
104
|
+
</Box>
|
|
105
|
+
)
|
|
106
|
+
})}
|
|
107
|
+
</Box>
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function Counter({ label, value }: { label: string; value: string }) {
|
|
112
|
+
return (
|
|
113
|
+
<Box sx={{ display: "flex", flexDirection: "column", gap: 1, padding: 4, borderRadius: 2, background: COLORS.slate[50] }}>
|
|
114
|
+
<Typography variant="paragraph18Medium">{label}</Typography>
|
|
115
|
+
<Typography variant="heading24SemiBold">{value}</Typography>
|
|
116
|
+
</Box>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
17
119
|
|
|
18
120
|
/** Done cycles green, the current one amber while stopping, and the cycles Stop cancels hatched. */
|
|
19
121
|
function CycleProgress({ cycle, cycleCount, stopping }: { cycle: number; cycleCount: number; stopping: boolean }) {
|
|
@@ -37,6 +139,8 @@ function CycleProgress({ cycle, cycleCount, stopping }: { cycle: number; cycleCo
|
|
|
37
139
|
export function OperatePage({ status, connected, recipe, onRecipeChange, onError }: OperatePageProps) {
|
|
38
140
|
const [recipes, setRecipes] = useState<Recipe[]>([])
|
|
39
141
|
const stopping = isStopping(status, connected)
|
|
142
|
+
const elapsed = useCycleElapsed(status)
|
|
143
|
+
const running = status !== undefined && status.state.startsWith("Station_") && status.cycle > 0
|
|
40
144
|
|
|
41
145
|
useEffect(() => {
|
|
42
146
|
if (!connected) return
|
|
@@ -61,18 +165,33 @@ export function OperatePage({ status, connected, recipe, onRecipeChange, onError
|
|
|
61
165
|
onPrimaryButtonClick={reset}
|
|
62
166
|
/>
|
|
63
167
|
)}
|
|
64
|
-
|
|
65
|
-
<Box sx={{ display: "flex",
|
|
168
|
+
{running ? (
|
|
169
|
+
<Box sx={{ display: "flex", flexDirection: "column", gap: 5 }}>
|
|
170
|
+
<Box sx={{ display: "flex", alignItems: "center", gap: 5 }}>
|
|
171
|
+
<StepRing step={status.step} elapsed={elapsed} expected={status.lastCycleSeconds} stopping={stopping} />
|
|
172
|
+
<Box sx={{ flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: 4 }}>
|
|
173
|
+
<Typography variant="heading24SemiBold">{status.recipeName}</Typography>
|
|
174
|
+
{stopping && <Typography variant="paragraph18Medium">The emergency stop halts at once.</Typography>}
|
|
175
|
+
<Box sx={{ display: "grid", gridTemplateColumns: "repeat(2, minmax(0, 1fr))", gap: 4 }}>
|
|
176
|
+
<Counter label="Cycles done" value={`${status.cycle - 1} of ${status.cycleCount}`} />
|
|
177
|
+
<Counter
|
|
178
|
+
label="Rate"
|
|
179
|
+
value={status.lastCycleSeconds > 0 ? `${Math.round(3600 / status.lastCycleSeconds)} parts/h` : "After cycle 1"}
|
|
180
|
+
/>
|
|
181
|
+
</Box>
|
|
182
|
+
</Box>
|
|
183
|
+
</Box>
|
|
184
|
+
<StepRail step={status.step} stopping={stopping} />
|
|
185
|
+
<CycleProgress cycle={status.cycle} cycleCount={status.cycleCount} stopping={stopping} />
|
|
186
|
+
</Box>
|
|
187
|
+
) : (
|
|
188
|
+
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
|
66
189
|
<Typography variant="heading24SemiBold">{status?.recipeName || "No recipe running"}</Typography>
|
|
67
|
-
|
|
190
|
+
<Typography variant="paragraph18Medium">
|
|
191
|
+
Last cycle: {status && status.lastCycleSeconds > 0 ? `${status.lastCycleSeconds.toFixed(1)} s` : "none yet"}
|
|
192
|
+
</Typography>
|
|
68
193
|
</Box>
|
|
69
|
-
|
|
70
|
-
<CycleProgress cycle={status.cycle} cycleCount={status.cycleCount} stopping={stopping} />
|
|
71
|
-
)}
|
|
72
|
-
<Typography variant="paragraph18Medium">
|
|
73
|
-
Last cycle: {status && status.lastCycleSeconds > 0 ? `${status.lastCycleSeconds.toFixed(1)} s` : "none yet"}
|
|
74
|
-
</Typography>
|
|
75
|
-
</Box>
|
|
194
|
+
)}
|
|
76
195
|
<VentionSelect
|
|
77
196
|
size="xx-large"
|
|
78
197
|
variant="outlined"
|
package/skills/vention-machine-logic-hmi/examples/indexing-station-hmi/customui/src/station.ts
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import { ConnectError } from "@connectrpc/connect"
|
|
2
|
+
import { IconArrowRight, IconHourglass, IconLock, IconLockOpen, type Icon } from "@tabler/icons-react"
|
|
2
3
|
import { COLORS } from "@vention/machine-ui"
|
|
3
4
|
import type { StationStatus } from "./gen/app_pb"
|
|
4
5
|
|
|
5
6
|
// The storage RPCs record who changed each recipe.
|
|
6
7
|
export const ACTOR = "operator"
|
|
7
8
|
|
|
9
|
+
// One per enter_step call in server.py, in the same order and under the same name.
|
|
10
|
+
export const STEPS: { name: string; label: string; icon: Icon }[] = [
|
|
11
|
+
{ name: "Index", label: "Indexing the part", icon: IconArrowRight },
|
|
12
|
+
{ name: "Clamp", label: "Clamping", icon: IconLock },
|
|
13
|
+
{ name: "Dwell", label: "Dwelling", icon: IconHourglass },
|
|
14
|
+
{ name: "Release", label: "Releasing", icon: IconLockOpen },
|
|
15
|
+
]
|
|
16
|
+
|
|
8
17
|
type StateDisplay = { label: string; color: string }
|
|
9
18
|
|
|
10
19
|
const STATE_DISPLAY: Record<string, StateDisplay> = {
|
|
@@ -158,8 +158,10 @@ message StationStatus {
|
|
|
158
158
|
string recipe_name = 2;
|
|
159
159
|
int32 cycle = 3;
|
|
160
160
|
int32 cycle_count = 4;
|
|
161
|
-
|
|
162
|
-
|
|
161
|
+
string step = 5;
|
|
162
|
+
double cycle_elapsed_seconds = 6;
|
|
163
|
+
double last_cycle_seconds = 7;
|
|
164
|
+
string fault = 8;
|
|
163
165
|
}
|
|
164
166
|
|
|
165
167
|
message Event {
|
|
@@ -63,6 +63,8 @@ class StationStatus(BaseModel):
|
|
|
63
63
|
recipe_name: str
|
|
64
64
|
cycle: int
|
|
65
65
|
cycle_count: int
|
|
66
|
+
step: str
|
|
67
|
+
cycle_elapsed_seconds: float
|
|
66
68
|
last_cycle_seconds: float
|
|
67
69
|
fault: str
|
|
68
70
|
|
|
@@ -89,6 +91,8 @@ class IndexingStation(StateMachine):
|
|
|
89
91
|
self.machine: Optional[Machine] = None
|
|
90
92
|
self.recipe: Optional[Recipe] = None
|
|
91
93
|
self.cycle = 0
|
|
94
|
+
self.step = ""
|
|
95
|
+
self.cycle_started = 0.0
|
|
92
96
|
self.last_cycle_seconds = 0.0
|
|
93
97
|
self.fault_message = ""
|
|
94
98
|
self.stop_requested = False
|
|
@@ -100,6 +104,8 @@ class IndexingStation(StateMachine):
|
|
|
100
104
|
recipe_name=recipe.name if recipe else "",
|
|
101
105
|
cycle=self.cycle,
|
|
102
106
|
cycle_count=recipe.cycle_count if recipe else 0,
|
|
107
|
+
step=self.step,
|
|
108
|
+
cycle_elapsed_seconds=time.monotonic() - self.cycle_started if self.step else 0.0,
|
|
103
109
|
last_cycle_seconds=self.last_cycle_seconds,
|
|
104
110
|
fault=self.fault_message,
|
|
105
111
|
)
|
|
@@ -111,6 +117,7 @@ class IndexingStation(StateMachine):
|
|
|
111
117
|
@on_enter_state(BaseStates.READY.value)
|
|
112
118
|
def enter_ready(self, _: object) -> None:
|
|
113
119
|
self.fault_message = ""
|
|
120
|
+
self.step = ""
|
|
114
121
|
|
|
115
122
|
@on_enter_state(States.station.running)
|
|
116
123
|
def enter_running(self, _: object) -> None:
|
|
@@ -151,6 +158,11 @@ class IndexingStation(StateMachine):
|
|
|
151
158
|
except Exception as error:
|
|
152
159
|
await publish_event("error", "Clamp release failed", f"{error}. Check the clamp is open before the next run.")
|
|
153
160
|
|
|
161
|
+
async def enter_step(self, step: str) -> None:
|
|
162
|
+
# The HMI's STEPS in station.ts lists the same names, in this order.
|
|
163
|
+
self.step = step
|
|
164
|
+
await publish_status()
|
|
165
|
+
|
|
154
166
|
async def run_cycles(self) -> None:
|
|
155
167
|
recipe = self.recipe
|
|
156
168
|
if recipe is None:
|
|
@@ -159,18 +171,21 @@ class IndexingStation(StateMachine):
|
|
|
159
171
|
for cycle in range(1, recipe.cycle_count + 1):
|
|
160
172
|
if self.stop_requested:
|
|
161
173
|
break
|
|
162
|
-
started = time.monotonic()
|
|
163
174
|
self.cycle = cycle
|
|
164
|
-
|
|
175
|
+
self.cycle_started = time.monotonic()
|
|
176
|
+
await self.enter_step("Index")
|
|
165
177
|
await asyncio.to_thread(self.index_part, recipe)
|
|
166
178
|
if self.stop_requested:
|
|
167
179
|
break
|
|
180
|
+
await self.enter_step("Clamp")
|
|
168
181
|
await asyncio.to_thread(self.clamp_part)
|
|
169
182
|
if self.stop_requested:
|
|
170
183
|
break
|
|
184
|
+
await self.enter_step("Dwell")
|
|
171
185
|
await asyncio.sleep(recipe.dwell_ms / 1000)
|
|
186
|
+
await self.enter_step("Release")
|
|
172
187
|
await asyncio.to_thread(self.release)
|
|
173
|
-
self.last_cycle_seconds = time.monotonic() -
|
|
188
|
+
self.last_cycle_seconds = time.monotonic() - self.cycle_started
|
|
174
189
|
await publish_status()
|
|
175
190
|
await publish_event("info", f"Cycle {cycle}/{recipe.cycle_count} done", f"Took {self.last_cycle_seconds:.1f} s.")
|
|
176
191
|
await asyncio.to_thread(self.release)
|
|
@@ -3,7 +3,7 @@ name: vention-monitoring
|
|
|
3
3
|
description: Diagnose deployed Vention machines from controller state, history, health, and activity data. Use when the user reports a machine that is down, faulted, offline, slow, or underperforming, asks why a cycle time regressed, wants to interpret MachineMotion event data or drive and brake status, asks about OEE or uptime, or wants to know which machine in a fleet needs attention. Trigger on a MachineMotion serial number plus any complaint.
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
|
-
version: "0.9.
|
|
6
|
+
version: "0.9.20"
|
|
7
7
|
author: Vention
|
|
8
8
|
---
|
|
9
9
|
|