150 lines
4.8 KiB
TypeScript
150 lines
4.8 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { generateGrlProgram, type GrlProgramGenerationSpec } from "../../../src/grl/generator/index.js";
|
|
import { postProcessAllBrands } from "../../../src/grl/post/index.js";
|
|
import type { GrlOperationDeclaration, GrlPathDeclaration, GrlTargetDeclaration } from "../../../src/grl/ast/index.js";
|
|
import { parseGrl } from "../../../src/grl/parser/index.js";
|
|
import { compileSemanticProgram } from "../../../src/grl/semantic/index.js";
|
|
|
|
const SPEC: GrlProgramGenerationSpec = {
|
|
moduleName: "GeneratedCell",
|
|
speeds: {
|
|
v_linear: "linear(200 mm/s)",
|
|
v_joint: "joint(50 %)"
|
|
},
|
|
zones: {
|
|
z10: "z(10 mm)",
|
|
zf: "fine"
|
|
},
|
|
targets: [
|
|
{ name: "pick", kind: "pose", values: [500, 0, 0, 0, 0, 0] },
|
|
{ name: "home", kind: "joint", values: [0] },
|
|
{ name: "place", kind: "pose", values: [600, 0, 0, 0, 0, 0] }
|
|
],
|
|
path: {
|
|
name: "generated_path",
|
|
source: {
|
|
type: "cad_curve",
|
|
id: "edge_001",
|
|
sample_distance: 5
|
|
},
|
|
defaults: {
|
|
speed: "v_linear",
|
|
zone: "z10"
|
|
},
|
|
points: [
|
|
{ motion: "movej", target: "home", speed: "v_joint", zone: "zf" },
|
|
{ motion: "movel", target: "pick" },
|
|
{ id: "place_point", motion: "movel", target: "place", zone: "zf" }
|
|
]
|
|
},
|
|
operation: {
|
|
name: "generated_op",
|
|
kind: "handling",
|
|
path: "generated_path",
|
|
startAction: "io.do[1] = true",
|
|
endAction: "io.do[1] = false"
|
|
}
|
|
};
|
|
|
|
describe("GRL generator and roundtrip", () => {
|
|
it("generates stable expanded GRL with target/path/operation first", () => {
|
|
const first = generateGrlProgram(SPEC, "expanded");
|
|
const second = generateGrlProgram(SPEC, "expanded");
|
|
|
|
expect(first).toEqual(second);
|
|
expect(first.stableIds).toEqual({
|
|
targets: ["home", "pick", "place"],
|
|
points: ["p00", "p01", "place_point"],
|
|
path: "generated_path",
|
|
operation: "generated_op"
|
|
});
|
|
expect(first.text).toBe(`language grl 0.1
|
|
module GeneratedCell
|
|
const speed v_joint = joint(50 %)
|
|
const speed v_linear = linear(200 mm/s)
|
|
const zone z10 = z(10 mm)
|
|
const zone zf = fine
|
|
target home = joint_target {
|
|
joints: [0 deg]
|
|
}
|
|
target pick = pose_target {
|
|
pose: pose(500 mm, 0 mm, 0 mm, 0 deg, 0 deg, 0 deg)
|
|
}
|
|
target place = pose_target {
|
|
pose: pose(600 mm, 0 mm, 0 mm, 0 deg, 0 deg, 0 deg)
|
|
}
|
|
path generated_path {
|
|
source {
|
|
id: "edge_001"
|
|
sample_distance: 5
|
|
type: cad_curve
|
|
}
|
|
defaults {
|
|
speed: v_linear
|
|
zone: z10
|
|
}
|
|
point p00 movej home speed v_joint zone zf
|
|
point p01 movel pick
|
|
point place_point movel place zone zf
|
|
}
|
|
operation generated_op {
|
|
kind: handling
|
|
path: generated_path
|
|
start_action:
|
|
io.do[1] = true
|
|
end_action:
|
|
io.do[1] = false
|
|
}
|
|
proc main()
|
|
run_operation generated_op
|
|
end
|
|
end`);
|
|
});
|
|
|
|
it("supports compact output that remains parseable", () => {
|
|
const compact = generateGrlProgram(SPEC, "compact");
|
|
|
|
expect(compact.text).toContain("path generated_path { source { id: \"edge_001\"");
|
|
expect(parseGrl(compact.text).module.name).toBe("GeneratedCell");
|
|
});
|
|
|
|
it("roundtrips through parser, semantic IR, and postprocessors", () => {
|
|
const generated = generateGrlProgram(SPEC, "expanded");
|
|
const ast = parseGrl(generated.text);
|
|
const declarations = ast.module.declarations;
|
|
const targets = declarations.filter((decl): decl is GrlTargetDeclaration => decl.kind === "TargetDeclaration");
|
|
const path = declarations.find((decl): decl is GrlPathDeclaration => decl.kind === "PathDeclaration")!;
|
|
const operation = declarations.find((decl): decl is GrlOperationDeclaration => decl.kind === "OperationDeclaration")!;
|
|
const ir = compileSemanticProgram(ast, {
|
|
startJoints: [0],
|
|
sampleTime: 0.004
|
|
});
|
|
const post = postProcessAllBrands(ir);
|
|
|
|
expect(targets.map((target) => target.name)).toEqual(["home", "pick", "place"]);
|
|
expect(path.items).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({ kind: "PathSourceBlock" }),
|
|
expect.objectContaining({ kind: "PathDefaultsBlock" }),
|
|
expect.objectContaining({ kind: "PathPoint", id: "p00" }),
|
|
expect.objectContaining({ kind: "PathPoint", id: "p01" }),
|
|
expect.objectContaining({ kind: "PathPoint", id: "place_point" })
|
|
])
|
|
);
|
|
expect(operation).toMatchObject({
|
|
name: "generated_op",
|
|
operationKind: "handling",
|
|
pathName: "generated_path"
|
|
});
|
|
expect(ir.symbols).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({ kind: "path", name: "generated_path" }),
|
|
expect.objectContaining({ kind: "operation", name: "generated_op" })
|
|
])
|
|
);
|
|
expect(post.outputs.abb.text).toContain("MODULE GeneratedCell");
|
|
expect(post.outputs.fanuc.text).toContain("/PROG MAIN");
|
|
expect(post.outputs.kuka.text).toContain("DEF Main()");
|
|
});
|
|
});
|