@horizon36596/zenith-core 0.1.1
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/LICENSE +21 -0
- package/NOTICE +18 -0
- package/README.md +16 -0
- package/dist/canonicalize.d.ts +25 -0
- package/dist/canonicalize.d.ts.map +1 -0
- package/dist/canonicalize.js +199 -0
- package/dist/canonicalize.js.map +1 -0
- package/dist/check.d.ts +26 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +231 -0
- package/dist/check.js.map +1 -0
- package/dist/checks/heading.d.ts +17 -0
- package/dist/checks/heading.d.ts.map +1 -0
- package/dist/checks/heading.js +78 -0
- package/dist/checks/heading.js.map +1 -0
- package/dist/checks/intake.d.ts +28 -0
- package/dist/checks/intake.d.ts.map +1 -0
- package/dist/checks/intake.js +131 -0
- package/dist/checks/intake.js.map +1 -0
- package/dist/checks/motion.d.ts +28 -0
- package/dist/checks/motion.d.ts.map +1 -0
- package/dist/checks/motion.js +252 -0
- package/dist/checks/motion.js.map +1 -0
- package/dist/checks/schedule.d.ts +29 -0
- package/dist/checks/schedule.d.ts.map +1 -0
- package/dist/checks/schedule.js +166 -0
- package/dist/checks/schedule.js.map +1 -0
- package/dist/checks/season.d.ts +6 -0
- package/dist/checks/season.d.ts.map +1 -0
- package/dist/checks/season.js +126 -0
- package/dist/checks/season.js.map +1 -0
- package/dist/continuity.d.ts +38 -0
- package/dist/continuity.d.ts.map +1 -0
- package/dist/continuity.js +69 -0
- package/dist/continuity.js.map +1 -0
- package/dist/diff.d.ts +37 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +258 -0
- package/dist/diff.js.map +1 -0
- package/dist/edit/command.d.ts +4 -0
- package/dist/edit/command.d.ts.map +1 -0
- package/dist/edit/command.js +16 -0
- package/dist/edit/command.js.map +1 -0
- package/dist/edit/errors.d.ts +9 -0
- package/dist/edit/errors.d.ts.map +1 -0
- package/dist/edit/errors.js +12 -0
- package/dist/edit/errors.js.map +1 -0
- package/dist/edit/fields.d.ts +40 -0
- package/dist/edit/fields.d.ts.map +1 -0
- package/dist/edit/fields.js +150 -0
- package/dist/edit/fields.js.map +1 -0
- package/dist/edit/finish.d.ts +15 -0
- package/dist/edit/finish.d.ts.map +1 -0
- package/dist/edit/finish.js +36 -0
- package/dist/edit/finish.js.map +1 -0
- package/dist/edit/groups.d.ts +66 -0
- package/dist/edit/groups.d.ts.map +1 -0
- package/dist/edit/groups.js +277 -0
- package/dist/edit/groups.js.map +1 -0
- package/dist/edit/ids.d.ts +39 -0
- package/dist/edit/ids.d.ts.map +1 -0
- package/dist/edit/ids.js +76 -0
- package/dist/edit/ids.js.map +1 -0
- package/dist/edit/index.d.ts +22 -0
- package/dist/edit/index.d.ts.map +1 -0
- package/dist/edit/index.js +22 -0
- package/dist/edit/index.js.map +1 -0
- package/dist/edit/meta.d.ts +15 -0
- package/dist/edit/meta.d.ts.map +1 -0
- package/dist/edit/meta.js +13 -0
- package/dist/edit/meta.js.map +1 -0
- package/dist/edit/path.d.ts +36 -0
- package/dist/edit/path.d.ts.map +1 -0
- package/dist/edit/path.js +135 -0
- package/dist/edit/path.js.map +1 -0
- package/dist/edit/steps.d.ts +53 -0
- package/dist/edit/steps.d.ts.map +1 -0
- package/dist/edit/steps.js +0 -0
- package/dist/edit/steps.js.map +1 -0
- package/dist/edit/template.d.ts +18 -0
- package/dist/edit/template.d.ts.map +1 -0
- package/dist/edit/template.js +30 -0
- package/dist/edit/template.js.map +1 -0
- package/dist/edit/timing.d.ts +11 -0
- package/dist/edit/timing.d.ts.map +1 -0
- package/dist/edit/timing.js +23 -0
- package/dist/edit/timing.js.map +1 -0
- package/dist/edit/tree.d.ts +32 -0
- package/dist/edit/tree.d.ts.map +1 -0
- package/dist/edit/tree.js +90 -0
- package/dist/edit/tree.js.map +1 -0
- package/dist/estimate.d.ts +48 -0
- package/dist/estimate.d.ts.map +1 -0
- package/dist/estimate.js +443 -0
- package/dist/estimate.js.map +1 -0
- package/dist/findingHelp.d.ts +18 -0
- package/dist/findingHelp.d.ts.map +1 -0
- package/dist/findingHelp.js +114 -0
- package/dist/findingHelp.js.map +1 -0
- package/dist/fix.d.ts +21 -0
- package/dist/fix.d.ts.map +1 -0
- package/dist/fix.js +180 -0
- package/dist/fix.js.map +1 -0
- package/dist/footprint.d.ts +33 -0
- package/dist/footprint.d.ts.map +1 -0
- package/dist/footprint.js +71 -0
- package/dist/footprint.js.map +1 -0
- package/dist/geometry/angle.d.ts +13 -0
- package/dist/geometry/angle.d.ts.map +1 -0
- package/dist/geometry/angle.js +21 -0
- package/dist/geometry/angle.js.map +1 -0
- package/dist/geometry/curve.d.ts +24 -0
- package/dist/geometry/curve.d.ts.map +1 -0
- package/dist/geometry/curve.js +78 -0
- package/dist/geometry/curve.js.map +1 -0
- package/dist/geometry/path.d.ts +29 -0
- package/dist/geometry/path.d.ts.map +1 -0
- package/dist/geometry/path.js +61 -0
- package/dist/geometry/path.js.map +1 -0
- package/dist/geometry/quadrature.d.ts +13 -0
- package/dist/geometry/quadrature.d.ts.map +1 -0
- package/dist/geometry/quadrature.js +57 -0
- package/dist/geometry/quadrature.js.map +1 -0
- package/dist/geometry/sat.d.ts +40 -0
- package/dist/geometry/sat.d.ts.map +1 -0
- package/dist/geometry/sat.js +86 -0
- package/dist/geometry/sat.js.map +1 -0
- package/dist/geometry/vec.d.ts +27 -0
- package/dist/geometry/vec.d.ts.map +1 -0
- package/dist/geometry/vec.js +30 -0
- package/dist/geometry/vec.js.map +1 -0
- package/dist/heading.d.ts +77 -0
- package/dist/heading.d.ts.map +1 -0
- package/dist/heading.js +164 -0
- package/dist/heading.js.map +1 -0
- package/dist/headingRanges.d.ts +48 -0
- package/dist/headingRanges.d.ts.map +1 -0
- package/dist/headingRanges.js +132 -0
- package/dist/headingRanges.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/kinematics.d.ts +63 -0
- package/dist/kinematics.d.ts.map +1 -0
- package/dist/kinematics.js +71 -0
- package/dist/kinematics.js.map +1 -0
- package/dist/ledger.d.ts +49 -0
- package/dist/ledger.d.ts.map +1 -0
- package/dist/ledger.js +136 -0
- package/dist/ledger.js.map +1 -0
- package/dist/load.d.ts +12 -0
- package/dist/load.d.ts.map +1 -0
- package/dist/load.js +12 -0
- package/dist/load.js.map +1 -0
- package/dist/mirror.d.ts +81 -0
- package/dist/mirror.d.ts.map +1 -0
- package/dist/mirror.js +282 -0
- package/dist/mirror.js.map +1 -0
- package/dist/plan.d.ts +29 -0
- package/dist/plan.d.ts.map +1 -0
- package/dist/plan.js +159 -0
- package/dist/plan.js.map +1 -0
- package/dist/prBody.d.ts +24 -0
- package/dist/prBody.d.ts.map +1 -0
- package/dist/prBody.js +112 -0
- package/dist/prBody.js.map +1 -0
- package/dist/provenance.d.ts +21 -0
- package/dist/provenance.d.ts.map +1 -0
- package/dist/provenance.js +46 -0
- package/dist/provenance.js.map +1 -0
- package/dist/render.d.ts +18 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +464 -0
- package/dist/render.js.map +1 -0
- package/dist/resolve.d.ts +39 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +172 -0
- package/dist/resolve.js.map +1 -0
- package/dist/season.d.ts +65 -0
- package/dist/season.d.ts.map +1 -0
- package/dist/season.js +16 -0
- package/dist/season.js.map +1 -0
- package/dist/sim/commands.d.ts +97 -0
- package/dist/sim/commands.d.ts.map +1 -0
- package/dist/sim/commands.js +185 -0
- package/dist/sim/commands.js.map +1 -0
- package/dist/sim/curves.d.ts +37 -0
- package/dist/sim/curves.d.ts.map +1 -0
- package/dist/sim/curves.js +187 -0
- package/dist/sim/curves.js.map +1 -0
- package/dist/sim/follower.d.ts +124 -0
- package/dist/sim/follower.d.ts.map +1 -0
- package/dist/sim/follower.js +472 -0
- package/dist/sim/follower.js.map +1 -0
- package/dist/sim/ideal.d.ts +103 -0
- package/dist/sim/ideal.d.ts.map +1 -0
- package/dist/sim/ideal.js +393 -0
- package/dist/sim/ideal.js.map +1 -0
- package/dist/sim/index.d.ts +11 -0
- package/dist/sim/index.d.ts.map +1 -0
- package/dist/sim/index.js +10 -0
- package/dist/sim/index.js.map +1 -0
- package/dist/sim/params.d.ts +91 -0
- package/dist/sim/params.d.ts.map +1 -0
- package/dist/sim/params.js +195 -0
- package/dist/sim/params.js.map +1 -0
- package/dist/sim/pedroPath.d.ts +41 -0
- package/dist/sim/pedroPath.d.ts.map +1 -0
- package/dist/sim/pedroPath.js +191 -0
- package/dist/sim/pedroPath.js.map +1 -0
- package/dist/sim/plant.d.ts +59 -0
- package/dist/sim/plant.d.ts.map +1 -0
- package/dist/sim/plant.js +128 -0
- package/dist/sim/plant.js.map +1 -0
- package/dist/sim/residuals.d.ts +36 -0
- package/dist/sim/residuals.d.ts.map +1 -0
- package/dist/sim/residuals.js +86 -0
- package/dist/sim/residuals.js.map +1 -0
- package/dist/sim/simulate.d.ts +27 -0
- package/dist/sim/simulate.d.ts.map +1 -0
- package/dist/sim/simulate.js +924 -0
- package/dist/sim/simulate.js.map +1 -0
- package/dist/sim/types.d.ts +209 -0
- package/dist/sim/types.d.ts.map +1 -0
- package/dist/sim/types.js +2 -0
- package/dist/sim/types.js.map +1 -0
- package/dist/tokens.d.ts +93 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +93 -0
- package/dist/tokens.js.map +1 -0
- package/dist/trace.d.ts +89 -0
- package/dist/trace.d.ts.map +1 -0
- package/dist/trace.js +187 -0
- package/dist/trace.js.map +1 -0
- package/dist/types.d.ts +219 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/unimplemented.d.ts +8 -0
- package/dist/unimplemented.d.ts.map +1 -0
- package/dist/unimplemented.js +11 -0
- package/dist/unimplemented.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { radToDeg } from "../geometry/angle.js";
|
|
2
|
+
import { HALF_TURN_TOLERANCE_RAD, linearHeadingPieces, rangeProblem } from "../heading.js";
|
|
3
|
+
/**
|
|
4
|
+
* HEADING: where the file asks for a heading the robot will not hold.
|
|
5
|
+
*
|
|
6
|
+
* - warning: a `linear` heading that asks one segment for more than half a turn. The runtime hands
|
|
7
|
+
* Pedro that segment's two headings and Pedro turns the short way, so the robot goes round the
|
|
8
|
+
* other way from the one the file wrote. The preview already draws the short way; this says so.
|
|
9
|
+
* - info: a `facePoint` heading with a non-zero `offsetRad`, which the robot runtime does not pass
|
|
10
|
+
* to Pedro yet.
|
|
11
|
+
*
|
|
12
|
+
* HEADING_RANGES (error): a `piecewise` heading whose ranges do not run from 0 to 1 in order with no
|
|
13
|
+
* gap and no overlap. Pedro's `PiecewiseInterpolator` throws on the robot for exactly these, so the
|
|
14
|
+
* file cannot run. Inside ranges that are fine, a `linear` range over half a turn and a `facePoint`
|
|
15
|
+
* range with an offset raise the same HEADING findings as a whole-step heading.
|
|
16
|
+
*/
|
|
17
|
+
export function checkHeading(steps) {
|
|
18
|
+
const findings = [];
|
|
19
|
+
for (const planned of steps) {
|
|
20
|
+
if (planned.step.kind !== "path")
|
|
21
|
+
continue;
|
|
22
|
+
const heading = planned.step.heading;
|
|
23
|
+
if (heading === undefined)
|
|
24
|
+
continue;
|
|
25
|
+
if (heading.mode === "piecewise") {
|
|
26
|
+
const problem = rangeProblem(heading.ranges);
|
|
27
|
+
if (problem !== null) {
|
|
28
|
+
findings.push({ severity: "error", stepId: planned.id, code: "HEADING_RANGES", message: problem });
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
for (const [index, range] of heading.ranges.entries()) {
|
|
32
|
+
const inner = range.heading;
|
|
33
|
+
if (inner.mode === "linear" && Math.abs(inner.toRad - inner.fromRad) > Math.PI + HALF_TURN_TOLERANCE_RAD) {
|
|
34
|
+
findings.push({
|
|
35
|
+
severity: "warning",
|
|
36
|
+
stepId: planned.id,
|
|
37
|
+
t: range.startT,
|
|
38
|
+
code: "HEADING",
|
|
39
|
+
message: `Range ${String(index + 1)} asks for over half a turn; the robot takes the short way. Split the range if you want the long way.`,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
if (inner.mode === "facePoint" && (inner.offsetRad ?? 0) !== 0) {
|
|
43
|
+
findings.push({
|
|
44
|
+
severity: "info",
|
|
45
|
+
stepId: planned.id,
|
|
46
|
+
t: range.startT,
|
|
47
|
+
code: "HEADING",
|
|
48
|
+
message: `The robot runtime ignores offsetRad for now (range ${String(index + 1)}).`,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (heading.mode === "linear" && planned.geometry !== undefined) {
|
|
55
|
+
const pieces = linearHeadingPieces(heading, planned.geometry);
|
|
56
|
+
if (!pieces.some((piece) => piece.reversed))
|
|
57
|
+
continue;
|
|
58
|
+
const turnRad = pieces.reduce((sum, piece) => sum + piece.turnRad, 0);
|
|
59
|
+
findings.push({
|
|
60
|
+
severity: "warning",
|
|
61
|
+
stepId: planned.id,
|
|
62
|
+
code: "HEADING",
|
|
63
|
+
message: `This turn is over half a turn; the robot takes the short way (${String(Math.round(radToDeg(turnRad)))}°). Split the path or use a waypoint heading if you want the long way.`,
|
|
64
|
+
});
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (heading.mode === "facePoint" && (heading.offsetRad ?? 0) !== 0) {
|
|
68
|
+
findings.push({
|
|
69
|
+
severity: "info",
|
|
70
|
+
stepId: planned.id,
|
|
71
|
+
code: "HEADING",
|
|
72
|
+
message: "The robot runtime ignores offsetRad for now.",
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return findings;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=heading.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heading.js","sourceRoot":"","sources":["../../src/checks/heading.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { CommandCall, MarkerAt, Mouth, Robot } from "@horizon36596/zenith-schema";
|
|
2
|
+
import type { PlanStep } from "../types.js";
|
|
3
|
+
interface IntakeCall {
|
|
4
|
+
/** The mouths the call addresses, by id. */
|
|
5
|
+
mouthIds: string[];
|
|
6
|
+
running: boolean;
|
|
7
|
+
}
|
|
8
|
+
/** Reads a command call as an intake instruction, or returns null when it is not one. */
|
|
9
|
+
export declare function intakeCall(robot: Robot, call: CommandCall): IntakeCall | null;
|
|
10
|
+
/** Where along a path step each mouth starts running: 0 when it already was, or a marker's t. */
|
|
11
|
+
export type RunningFrom = ReadonlyMap<string, number>;
|
|
12
|
+
export interface IntakeTimeline {
|
|
13
|
+
/** For each path step id, the mouths running on it and where they started. */
|
|
14
|
+
byStepId: ReadonlyMap<string, RunningFrom>;
|
|
15
|
+
}
|
|
16
|
+
/** Where a marker sits as a fraction of the step, whichever of the three ways it was written. */
|
|
17
|
+
export declare const markerT: (at: MarkerAt, lengthIn: number) => number;
|
|
18
|
+
/**
|
|
19
|
+
* Walks the auto once and works out, for every path step, which mouths are running over it: those
|
|
20
|
+
* a previous command turned on and never stopped, those a marker turns on part way through, and,
|
|
21
|
+
* inside a parallel group, those any other member of the group turns on, because that member runs
|
|
22
|
+
* alongside the drive (a deadline drive with an intake sequence beside it is the common case).
|
|
23
|
+
*/
|
|
24
|
+
export declare function intakeTimeline(steps: readonly PlanStep[], robot: Robot): IntakeTimeline;
|
|
25
|
+
/** Which way a mouth points in the field frame when the robot is at this heading. */
|
|
26
|
+
export declare function mouthHeadingRad(mouth: Mouth, robotHeadingRad: number): number;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=intake.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intake.d.ts","sourceRoot":"","sources":["../../src/checks/intake.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACvF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAc5C,UAAU,UAAU;IAClB,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,yFAAyF;AACzF,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,GAAG,IAAI,CAoB7E;AAED,iGAAiG;AACjG,MAAM,MAAM,WAAW,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC7B,8EAA8E;IAC9E,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CAC5C;AAED,iGAAiG;AACjG,eAAO,MAAM,OAAO,GAAI,IAAI,QAAQ,EAAE,UAAU,MAAM,KAAG,MAKxD,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,EAAE,KAAK,EAAE,KAAK,GAAG,cAAc,CA6DvF;AAED,qFAAqF;AACrF,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,GAAG,MAAM,CAQ7E"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which mouths are running while a path step is driven.
|
|
3
|
+
*
|
|
4
|
+
* Nothing here knows the name `setIntake`: a command counts as an intake command when its registry
|
|
5
|
+
* entry in `robot.json` requires the intake and takes a `state` parameter, which is the contract
|
|
6
|
+
* site/docs/file-format.md sets out. That way a robot that calls the command something else, or
|
|
7
|
+
* has three mouths, still gets the MOUTH_LEADING and SWEEP_SPEED checks.
|
|
8
|
+
*/
|
|
9
|
+
const RUNNING_STATES = new Set(["FORWARD", "REVERSE", "ON", "IN", "OUT"]);
|
|
10
|
+
const STOPPED_STATES = new Set(["STOP", "OFF", "IDLE"]);
|
|
11
|
+
/** Reads a command call as an intake instruction, or returns null when it is not one. */
|
|
12
|
+
export function intakeCall(robot, call) {
|
|
13
|
+
const spec = robot.commands.find((command) => command.name === call.name);
|
|
14
|
+
if (spec === undefined)
|
|
15
|
+
return null;
|
|
16
|
+
if (!(spec.requires ?? []).includes("intake"))
|
|
17
|
+
return null;
|
|
18
|
+
const state = call.args?.["state"] ?? spec.params?.["state"]?.default;
|
|
19
|
+
if (typeof state !== "string")
|
|
20
|
+
return null;
|
|
21
|
+
const running = RUNNING_STATES.has(state)
|
|
22
|
+
? true
|
|
23
|
+
: STOPPED_STATES.has(state)
|
|
24
|
+
? false
|
|
25
|
+
: null;
|
|
26
|
+
if (running === null)
|
|
27
|
+
return null;
|
|
28
|
+
const side = call.args?.["side"] ?? spec.params?.["side"]?.default;
|
|
29
|
+
const mouths = robot.mouths ?? [];
|
|
30
|
+
const mouthIds = typeof side === "string" && side !== "BOTH" && side !== "ALL"
|
|
31
|
+
? mouths.filter((mouth) => mouth.side === side).map((mouth) => mouth.id)
|
|
32
|
+
: mouths.map((mouth) => mouth.id);
|
|
33
|
+
return { mouthIds, running };
|
|
34
|
+
}
|
|
35
|
+
/** Where a marker sits as a fraction of the step, whichever of the three ways it was written. */
|
|
36
|
+
export const markerT = (at, lengthIn) => {
|
|
37
|
+
if ("t" in at)
|
|
38
|
+
return at.t;
|
|
39
|
+
if (lengthIn <= 0)
|
|
40
|
+
return 0;
|
|
41
|
+
if ("distanceIn" in at)
|
|
42
|
+
return Math.min(at.distanceIn / lengthIn, 1);
|
|
43
|
+
return Math.max(1 - at.distanceFromEndIn / lengthIn, 0);
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Walks the auto once and works out, for every path step, which mouths are running over it: those
|
|
47
|
+
* a previous command turned on and never stopped, those a marker turns on part way through, and,
|
|
48
|
+
* inside a parallel group, those any other member of the group turns on, because that member runs
|
|
49
|
+
* alongside the drive (a deadline drive with an intake sequence beside it is the common case).
|
|
50
|
+
*/
|
|
51
|
+
export function intakeTimeline(steps, robot) {
|
|
52
|
+
const running = new Set();
|
|
53
|
+
const byStepId = new Map();
|
|
54
|
+
const turnedOn = (list, into) => {
|
|
55
|
+
for (const planned of list) {
|
|
56
|
+
const step = planned.step;
|
|
57
|
+
if (step.kind === "command") {
|
|
58
|
+
const call = intakeCall(robot, { name: step.name, ...(step.args === undefined ? {} : { args: step.args }) });
|
|
59
|
+
if (call?.running === true)
|
|
60
|
+
for (const id of call.mouthIds)
|
|
61
|
+
into.add(id);
|
|
62
|
+
}
|
|
63
|
+
if (planned.children !== undefined)
|
|
64
|
+
turnedOn(planned.children, into);
|
|
65
|
+
}
|
|
66
|
+
return into;
|
|
67
|
+
};
|
|
68
|
+
const walk = (list, alongside = new Set()) => {
|
|
69
|
+
for (const planned of list) {
|
|
70
|
+
const step = planned.step;
|
|
71
|
+
if (step.kind === "command") {
|
|
72
|
+
const call = intakeCall(robot, { name: step.name, ...(step.args === undefined ? {} : { args: step.args }) });
|
|
73
|
+
if (call !== null) {
|
|
74
|
+
for (const id of call.mouthIds) {
|
|
75
|
+
if (call.running)
|
|
76
|
+
running.add(id);
|
|
77
|
+
else
|
|
78
|
+
running.delete(id);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (step.kind === "path") {
|
|
84
|
+
const from = new Map();
|
|
85
|
+
for (const id of running)
|
|
86
|
+
from.set(id, 0);
|
|
87
|
+
for (const id of alongside)
|
|
88
|
+
from.set(id, 0);
|
|
89
|
+
for (const marker of step.markers ?? []) {
|
|
90
|
+
const call = intakeCall(robot, marker.command);
|
|
91
|
+
if (call === null)
|
|
92
|
+
continue;
|
|
93
|
+
const t = markerT(marker.at, planned.lengthIn);
|
|
94
|
+
for (const id of call.mouthIds) {
|
|
95
|
+
if (call.running) {
|
|
96
|
+
from.set(id, Math.min(from.get(id) ?? 1, t));
|
|
97
|
+
running.add(id);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
from.delete(id);
|
|
101
|
+
running.delete(id);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
byStepId.set(planned.id, from);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (planned.children === undefined)
|
|
109
|
+
continue;
|
|
110
|
+
if (step.kind === "parallel") {
|
|
111
|
+
walk(planned.children, turnedOn(planned.children, new Set(alongside)));
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
walk(planned.children, alongside);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
walk(steps);
|
|
119
|
+
return { byStepId };
|
|
120
|
+
}
|
|
121
|
+
/** Which way a mouth points in the field frame when the robot is at this heading. */
|
|
122
|
+
export function mouthHeadingRad(mouth, robotHeadingRad) {
|
|
123
|
+
const offsets = {
|
|
124
|
+
FRONT: 0,
|
|
125
|
+
BACK: Math.PI,
|
|
126
|
+
LEFT: Math.PI / 2,
|
|
127
|
+
RIGHT: -Math.PI / 2,
|
|
128
|
+
};
|
|
129
|
+
return robotHeadingRad + offsets[mouth.side];
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=intake.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intake.js","sourceRoot":"","sources":["../../src/checks/intake.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Field, Robot } from "@horizon36596/zenith-schema";
|
|
2
|
+
import type { Estimate, Finding, Plan, PlanStep } from "../types.js";
|
|
3
|
+
import { type RunningFrom } from "./intake.js";
|
|
4
|
+
/**
|
|
5
|
+
* KEEPOUT: the footprint enters a zone whose rule applies during autonomous.
|
|
6
|
+
*
|
|
7
|
+
* A zone counts only when it says `appliesInAuto: true`. The BIOBUZZ loading zones say "no start
|
|
8
|
+
* here", which START_ILLEGAL already covers, and warning about driving through them would be a
|
|
9
|
+
* false alarm; the flag is how a field file asks for this check.
|
|
10
|
+
*/
|
|
11
|
+
export declare function checkKeepout(steps: readonly PlanStep[], field: Field): Finding[];
|
|
12
|
+
/**
|
|
13
|
+
* STRAFE_FRACTION: more of a leg driven sideways than the robot file's threshold allows. The
|
|
14
|
+
* finding carries both the fraction and what it costs in seconds, because `03` section 5 is
|
|
15
|
+
* explicit that a percentage alone does not let anyone accept the cost knowingly.
|
|
16
|
+
*/
|
|
17
|
+
export declare function checkStrafeFraction(plan: Plan, estimate: Estimate | null, robot: Robot): Finding[];
|
|
18
|
+
/**
|
|
19
|
+
* MOUTH_LEADING: a leg driven with an intake running where the running mouth is not within 30
|
|
20
|
+
* degrees of the travel direction. A body wider than the mouth shoves game pieces aside instead of
|
|
21
|
+
* taking them in, which is a quiet way to lose a cycle.
|
|
22
|
+
*/
|
|
23
|
+
export declare function checkMouthLeading(steps: readonly PlanStep[], robot: Robot, running: ReadonlyMap<string, RunningFrom>): Finding[];
|
|
24
|
+
/** SWEEP_SPEED: a leg driven faster than the robot's sweep speed with an intake running. */
|
|
25
|
+
export declare function checkSweepSpeed(steps: readonly PlanStep[], robot: Robot, running: ReadonlyMap<string, RunningFrom>): Finding[];
|
|
26
|
+
/** Runs every motion check over a plan. */
|
|
27
|
+
export declare function checkMotion(plan: Plan, estimate: Estimate | null, robot: Robot, field: Field, steps: readonly PlanStep[]): Finding[];
|
|
28
|
+
//# sourceMappingURL=motion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"motion.d.ts","sourceRoot":"","sources":["../../src/checks/motion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAQ,MAAM,6BAA6B,CAAC;AAStE,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAO,IAAI,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAmC,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAUhF;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,EAAE,CA6BhF;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,QAAQ,GAAG,IAAI,EACzB,KAAK,EAAE,KAAK,GACX,OAAO,EAAE,CAuBX;AAsED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,QAAQ,EAAE,EAC1B,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,GACxC,OAAO,EAAE,CA+DX;AAED,4FAA4F;AAC5F,wBAAgB,eAAe,CAC7B,KAAK,EAAE,SAAS,QAAQ,EAAE,EAC1B,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,GACxC,OAAO,EAAE,CA6BX;AAED,2CAA2C;AAC3C,wBAAgB,WAAW,CACzB,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,QAAQ,GAAG,IAAI,EACzB,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,SAAS,QAAQ,EAAE,GACzB,OAAO,EAAE,CAQX"}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { wrapAngle } from "../geometry/angle.js";
|
|
2
|
+
import { polygonOverlapsBox } from "../geometry/sat.js";
|
|
3
|
+
import { MOUTH_LEADING_TOLERANCE_RAD, robotLimits, strafeFractionWarn, sweepSpeedFraction, } from "../kinematics.js";
|
|
4
|
+
import { intakeTimeline, mouthHeadingRad } from "./intake.js";
|
|
5
|
+
/**
|
|
6
|
+
* The checks that are about how the robot moves: KEEPOUT, STRAFE_FRACTION, MOUTH_LEADING and
|
|
7
|
+
* SWEEP_SPEED (site/docs/checks-and-findings.md).
|
|
8
|
+
*/
|
|
9
|
+
const degrees = (rad) => `${((rad * 180) / Math.PI).toFixed(0)} degrees`;
|
|
10
|
+
const percent = (fraction) => `${(fraction * 100).toFixed(0)} %`;
|
|
11
|
+
/**
|
|
12
|
+
* KEEPOUT: the footprint enters a zone whose rule applies during autonomous.
|
|
13
|
+
*
|
|
14
|
+
* A zone counts only when it says `appliesInAuto: true`. The BIOBUZZ loading zones say "no start
|
|
15
|
+
* here", which START_ILLEGAL already covers, and warning about driving through them would be a
|
|
16
|
+
* false alarm; the flag is how a field file asks for this check.
|
|
17
|
+
*/
|
|
18
|
+
export function checkKeepout(steps, field) {
|
|
19
|
+
const findings = [];
|
|
20
|
+
const zones = (field.zones ?? []).filter((zone) => zone.appliesInAuto === true);
|
|
21
|
+
if (zones.length === 0)
|
|
22
|
+
return findings;
|
|
23
|
+
for (const step of steps) {
|
|
24
|
+
for (const zone of zones) {
|
|
25
|
+
let worst = null;
|
|
26
|
+
for (const sample of step.samples) {
|
|
27
|
+
for (const polygon of [sample.footprintIn, ...sample.mouthsIn]) {
|
|
28
|
+
const overlap = polygonOverlapsBox(polygon, zone);
|
|
29
|
+
if (!overlap.overlaps)
|
|
30
|
+
continue;
|
|
31
|
+
if (worst === null || overlap.penetrationIn > worst.penetrationIn) {
|
|
32
|
+
worst = { t: sample.t, penetrationIn: overlap.penetrationIn };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (worst === null)
|
|
37
|
+
continue;
|
|
38
|
+
findings.push({
|
|
39
|
+
severity: "warning",
|
|
40
|
+
stepId: step.id,
|
|
41
|
+
t: worst.t,
|
|
42
|
+
code: "KEEPOUT",
|
|
43
|
+
message: `The footprint enters ${zone.id}, which applies during autonomous${zone.rule === undefined ? "" : ` (${zone.rule})`}.`,
|
|
44
|
+
geometry: { zoneId: zone.id, penetrationIn: worst.penetrationIn },
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return findings;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* STRAFE_FRACTION: more of a leg driven sideways than the robot file's threshold allows. The
|
|
52
|
+
* finding carries both the fraction and what it costs in seconds, because `03` section 5 is
|
|
53
|
+
* explicit that a percentage alone does not let anyone accept the cost knowingly.
|
|
54
|
+
*/
|
|
55
|
+
export function checkStrafeFraction(plan, estimate, robot) {
|
|
56
|
+
if (estimate === null)
|
|
57
|
+
return [];
|
|
58
|
+
const threshold = strafeFractionWarn(robot);
|
|
59
|
+
const findings = [];
|
|
60
|
+
// Every leg, including the ones inside a parallel group or a branch arm: the estimate already
|
|
61
|
+
// carries their strafeFraction, and a sideways leg costs the same seconds wherever it sits.
|
|
62
|
+
for (const { step, siblings, index } of sited(plan.steps)) {
|
|
63
|
+
const timing = estimate.byStepId[step.id];
|
|
64
|
+
if (timing?.strafeFraction == null || step.kind !== "path")
|
|
65
|
+
continue;
|
|
66
|
+
if (timing.strafeFraction <= threshold)
|
|
67
|
+
continue;
|
|
68
|
+
const costS = timing.strafeCostS ?? 0;
|
|
69
|
+
findings.push({
|
|
70
|
+
severity: "warning",
|
|
71
|
+
stepId: step.id,
|
|
72
|
+
code: "STRAFE_FRACTION",
|
|
73
|
+
message: `This leg is driven sideways for ${percent(timing.strafeFraction)} of its length, which costs ${costS.toFixed(2)} s against driving it nose-first; the warning threshold is ${percent(threshold)}.`,
|
|
74
|
+
geometry: { polygonIn: step.samples.map((sample) => sample.pose) },
|
|
75
|
+
fixes: strafeFixes(siblings, step, index),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return findings;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Every step of a plan with the list that holds it: the top level, one parallel group's members,
|
|
82
|
+
* or one arm of a branch. A branch's two arms are separate lists, because the last step of `then`
|
|
83
|
+
* does not run before the first step of `else`.
|
|
84
|
+
*/
|
|
85
|
+
function sited(steps) {
|
|
86
|
+
const out = [];
|
|
87
|
+
steps.forEach((step, index) => {
|
|
88
|
+
out.push({ step, siblings: steps, index });
|
|
89
|
+
const children = step.children;
|
|
90
|
+
if (children === undefined)
|
|
91
|
+
return;
|
|
92
|
+
if (step.step.kind === "branch") {
|
|
93
|
+
const thenCount = step.step.then.length;
|
|
94
|
+
out.push(...sited(children.slice(0, thenCount)));
|
|
95
|
+
out.push(...sited(children.slice(thenCount)));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
out.push(...sited(children));
|
|
99
|
+
});
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
/** The two fixes `03` section 5 names for a strafing leg: drive it nose-first, or turn beforehand. */
|
|
103
|
+
function strafeFixes(siblings, step, index) {
|
|
104
|
+
const fixes = [
|
|
105
|
+
{
|
|
106
|
+
kind: "makeTangent",
|
|
107
|
+
stepId: step.id,
|
|
108
|
+
label: "Point the nose along the path",
|
|
109
|
+
params: {},
|
|
110
|
+
},
|
|
111
|
+
];
|
|
112
|
+
const entryHeadingRad = step.samples[0]?.pose.headingRad;
|
|
113
|
+
const tangent = step.geometry?.tangentAtDistance(0);
|
|
114
|
+
if (entryHeadingRad === undefined || tangent === undefined)
|
|
115
|
+
return fixes;
|
|
116
|
+
const headingRad = wrapAngle(Math.atan2(tangent.yIn, tangent.xIn));
|
|
117
|
+
let previous;
|
|
118
|
+
for (let before = index - 1; before >= 0; before -= 1) {
|
|
119
|
+
const candidate = siblings[before];
|
|
120
|
+
if (candidate !== undefined && candidate.kind === "path") {
|
|
121
|
+
previous = candidate;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (previous === undefined)
|
|
126
|
+
return fixes;
|
|
127
|
+
fixes.push({
|
|
128
|
+
kind: "turnAtPreviousStop",
|
|
129
|
+
stepId: step.id,
|
|
130
|
+
label: `Turn to ${degrees(headingRad)} at the end of ${previous.id} instead`,
|
|
131
|
+
params: {
|
|
132
|
+
headingRad,
|
|
133
|
+
fromRad: previous.samples[0]?.pose.headingRad ?? headingRad,
|
|
134
|
+
turnInStepId: previous.id,
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
return fixes;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* MOUTH_LEADING: a leg driven with an intake running where the running mouth is not within 30
|
|
141
|
+
* degrees of the travel direction. A body wider than the mouth shoves game pieces aside instead of
|
|
142
|
+
* taking them in, which is a quiet way to lose a cycle.
|
|
143
|
+
*/
|
|
144
|
+
export function checkMouthLeading(steps, robot, running) {
|
|
145
|
+
const findings = [];
|
|
146
|
+
const mouths = robot.mouths ?? [];
|
|
147
|
+
if (mouths.length === 0)
|
|
148
|
+
return findings;
|
|
149
|
+
for (const step of steps) {
|
|
150
|
+
const active = running.get(step.id);
|
|
151
|
+
if (active === undefined || active.size === 0)
|
|
152
|
+
continue;
|
|
153
|
+
const geometry = step.geometry;
|
|
154
|
+
if (geometry === undefined)
|
|
155
|
+
continue;
|
|
156
|
+
let runningIn = 0;
|
|
157
|
+
let badIn = 0;
|
|
158
|
+
let worst = { deviationRad: 0, t: 0, mouthId: "" };
|
|
159
|
+
for (let i = 1; i < step.samples.length; i += 1) {
|
|
160
|
+
const before = step.samples[i - 1];
|
|
161
|
+
const after = step.samples[i];
|
|
162
|
+
if (before === undefined || after === undefined)
|
|
163
|
+
continue;
|
|
164
|
+
const ds = after.sIn - before.sIn;
|
|
165
|
+
if (ds <= 0)
|
|
166
|
+
continue;
|
|
167
|
+
const midT = (before.t + after.t) / 2;
|
|
168
|
+
const live = mouths.filter((mouth) => {
|
|
169
|
+
const from = active.get(mouth.id);
|
|
170
|
+
return from !== undefined && midT >= from;
|
|
171
|
+
});
|
|
172
|
+
if (live.length === 0)
|
|
173
|
+
continue;
|
|
174
|
+
const tangent = geometry.tangentAtDistance((before.sIn + after.sIn) / 2);
|
|
175
|
+
const travelRad = Math.atan2(tangent.yIn, tangent.xIn);
|
|
176
|
+
const headingRad = (before.pose.headingRad + after.pose.headingRad) / 2;
|
|
177
|
+
let best = { deviationRad: Infinity, mouthId: "" };
|
|
178
|
+
for (const mouth of live) {
|
|
179
|
+
const deviationRad = Math.abs(wrapAngle(mouthHeadingRad(mouth, headingRad) - travelRad));
|
|
180
|
+
if (deviationRad < best.deviationRad)
|
|
181
|
+
best = { deviationRad, mouthId: mouth.id };
|
|
182
|
+
}
|
|
183
|
+
runningIn += ds;
|
|
184
|
+
if (best.deviationRad > MOUTH_LEADING_TOLERANCE_RAD)
|
|
185
|
+
badIn += ds;
|
|
186
|
+
if (best.deviationRad > worst.deviationRad) {
|
|
187
|
+
worst = { deviationRad: best.deviationRad, t: midT, mouthId: best.mouthId };
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (runningIn === 0 || badIn <= runningIn / 2)
|
|
191
|
+
continue;
|
|
192
|
+
findings.push({
|
|
193
|
+
severity: "warning",
|
|
194
|
+
stepId: step.id,
|
|
195
|
+
t: worst.t,
|
|
196
|
+
code: "MOUTH_LEADING",
|
|
197
|
+
message: `The ${worst.mouthId} mouth is running but points up to ${degrees(worst.deviationRad)} away from the direction of travel over ${percent(badIn / runningIn)} of this leg; the limit is ${degrees(MOUTH_LEADING_TOLERANCE_RAD)}, and a body wider than the mouth shoves game pieces aside.`,
|
|
198
|
+
fixes: [
|
|
199
|
+
{ kind: "makeTangent", stepId: step.id, label: "Point the nose along the path", params: {} },
|
|
200
|
+
{
|
|
201
|
+
kind: "makeTangent",
|
|
202
|
+
stepId: step.id,
|
|
203
|
+
label: "Point the back mouth along the path",
|
|
204
|
+
params: { reversed: true },
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
return findings;
|
|
210
|
+
}
|
|
211
|
+
/** SWEEP_SPEED: a leg driven faster than the robot's sweep speed with an intake running. */
|
|
212
|
+
export function checkSweepSpeed(steps, robot, running) {
|
|
213
|
+
const limits = robotLimits(robot);
|
|
214
|
+
const sweep = sweepSpeedFraction(robot);
|
|
215
|
+
const findings = [];
|
|
216
|
+
for (const step of steps) {
|
|
217
|
+
const active = running.get(step.id);
|
|
218
|
+
if (active === undefined || active.size === 0)
|
|
219
|
+
continue;
|
|
220
|
+
if (step.step.kind !== "path")
|
|
221
|
+
continue;
|
|
222
|
+
const speedFraction = step.step.speedFraction ?? limits.defaultPathSpeedFraction;
|
|
223
|
+
if (speedFraction <= sweep)
|
|
224
|
+
continue;
|
|
225
|
+
findings.push({
|
|
226
|
+
severity: "warning",
|
|
227
|
+
stepId: step.id,
|
|
228
|
+
code: "SWEEP_SPEED",
|
|
229
|
+
message: `This leg intakes at ${percent(speedFraction)} of full speed, above the ${percent(sweep)} the robot file sets for a sweep.`,
|
|
230
|
+
fixes: [
|
|
231
|
+
{
|
|
232
|
+
kind: "slowSweep",
|
|
233
|
+
stepId: step.id,
|
|
234
|
+
label: `Slow this sweep to ${percent(sweep)}`,
|
|
235
|
+
params: { speedFraction: sweep },
|
|
236
|
+
},
|
|
237
|
+
],
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
return findings;
|
|
241
|
+
}
|
|
242
|
+
/** Runs every motion check over a plan. */
|
|
243
|
+
export function checkMotion(plan, estimate, robot, field, steps) {
|
|
244
|
+
const running = intakeTimeline(plan.steps, robot).byStepId;
|
|
245
|
+
return [
|
|
246
|
+
...checkKeepout(steps, field),
|
|
247
|
+
...checkStrafeFraction(plan, estimate, robot),
|
|
248
|
+
...checkMouthLeading(steps, robot, running),
|
|
249
|
+
...checkSweepSpeed(steps, robot, running),
|
|
250
|
+
];
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=motion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"motion.js","sourceRoot":"","sources":["../../src/checks/motion.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Field, Robot } from "@horizon36596/zenith-schema";
|
|
2
|
+
import type { Estimate, Finding, PlanStep } from "../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* The checks about the shape of the routine rather than its geometry: TIME_BUDGET, TIMEOUT_TIGHT,
|
|
5
|
+
* STATIONARY_MARKER, MOVES_ROBOT and PROVENANCE
|
|
6
|
+
* (site/docs/checks-and-findings.md).
|
|
7
|
+
*/
|
|
8
|
+
/** The label an inline pose is taken to carry when it has none (`03` section 4, PROVENANCE). */
|
|
9
|
+
export declare const INHERITED_PROVENANCE = "SET FROM EDITOR";
|
|
10
|
+
/** TIME_BUDGET: the routine over the period. Nominal over is a warning, optimistic over an error. */
|
|
11
|
+
export declare function checkTimeBudget(estimate: Estimate | null, field: Field): Finding[];
|
|
12
|
+
/** TIMEOUT_TIGHT: a step whose timeout leaves it almost no room over its own estimate. */
|
|
13
|
+
export declare function checkTimeouts(steps: readonly PlanStep[], estimate: Estimate | null): Finding[];
|
|
14
|
+
/** STATIONARY_MARKER: a marker that fires a command the robot is meant to be standing still for. */
|
|
15
|
+
export declare function checkStationaryMarkers(steps: readonly PlanStep[], robot: Robot): Finding[];
|
|
16
|
+
/**
|
|
17
|
+
* MOVES_ROBOT: a command whose registry entry says it drives the robot leaves the end pose unknown,
|
|
18
|
+
* so whatever follows has to start from wherever it actually ended.
|
|
19
|
+
*
|
|
20
|
+
* It takes the flattened list: a command that drives the robot does so inside a parallel group or a
|
|
21
|
+
* branch arm too, and the step after it is no better placed to name a fixed pose for having been
|
|
22
|
+
* nested.
|
|
23
|
+
*/
|
|
24
|
+
export declare function checkMovesRobot(steps: readonly PlanStep[], robot: Robot): Finding[];
|
|
25
|
+
/** PROVENANCE: an inline pose with no label, which is taken to be the editor's own. */
|
|
26
|
+
export declare function checkProvenance(steps: readonly PlanStep[]): Finding[];
|
|
27
|
+
/** Runs every scheduling check over a plan. */
|
|
28
|
+
export declare function checkSchedule(estimate: Estimate | null, robot: Robot, field: Field, steps: readonly PlanStep[]): Finding[];
|
|
29
|
+
//# sourceMappingURL=schedule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedule.d.ts","sourceRoot":"","sources":["../../src/checks/schedule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,KAAK,EAAQ,MAAM,6BAA6B,CAAC;AAEtE,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE/D;;;;GAIG;AAEH,gGAAgG;AAChG,eAAO,MAAM,oBAAoB,oBAAoB,CAAC;AAEtD,qGAAqG;AACrG,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,EAAE,CAsClF;AAED,0FAA0F;AAC1F,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,GAAG,OAAO,EAAE,CAgB9F;AAED,oGAAoG;AACpG,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,EAAE,CAiB1F;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,EAAE,CA0BnF;AAED,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,OAAO,EAAE,CA2BrE;AAED,+CAA+C;AAC/C,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,QAAQ,GAAG,IAAI,EACzB,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,SAAS,QAAQ,EAAE,GACzB,OAAO,EAAE,CAQX"}
|