180 lines
4.6 KiB
TypeScript
180 lines
4.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { createKdlWorkerRuntime } from "../../src/kdl/runtime.js";
|
|
import { dispatchKdlRpcRequest } from "../../src/kdl/workerRpc.js";
|
|
import type { CycleTimeResult, PathPlanResult, TrajectoryResult } from "../../src/kdl/types.js";
|
|
|
|
function pose(x: number, y: number, z: number) {
|
|
return {
|
|
position: [x, y, z] as [number, number, number],
|
|
quaternion: [0, 0, 0, 1] as [number, number, number, number]
|
|
};
|
|
}
|
|
|
|
function trajectory(overrides: Partial<TrajectoryResult> = {}): TrajectoryResult {
|
|
return {
|
|
ok: true,
|
|
motion: "MOVEJ",
|
|
duration: 1,
|
|
sampleTime: 0.5,
|
|
events: [],
|
|
diagnostics: [],
|
|
points: [
|
|
{
|
|
index: 0,
|
|
time: 0,
|
|
dt: 0,
|
|
s: 0,
|
|
sd: 0,
|
|
sdd: 0,
|
|
joints: [0, 0],
|
|
jointVelocity: [0, 0],
|
|
jointAcceleration: [0, 0],
|
|
flange: pose(0, 0, 0),
|
|
tcp: pose(0, 0, 0),
|
|
motion: "MOVEJ",
|
|
segmentId: "s1",
|
|
diagnostics: []
|
|
},
|
|
{
|
|
index: 1,
|
|
time: 1,
|
|
dt: 1,
|
|
s: 1,
|
|
sd: 0,
|
|
sdd: 0,
|
|
joints: [1, 2],
|
|
jointVelocity: [0, 0],
|
|
jointAcceleration: [0, 0],
|
|
flange: pose(1, 0, 0),
|
|
tcp: pose(1, 2, 0),
|
|
motion: "MOVEJ",
|
|
segmentId: "s1",
|
|
diagnostics: []
|
|
}
|
|
],
|
|
...overrides
|
|
};
|
|
}
|
|
|
|
async function createRuntime() {
|
|
const runtime = createKdlWorkerRuntime();
|
|
await dispatchKdlRpcRequest(runtime, { id: 1, method: "init", payload: [{}] });
|
|
return runtime;
|
|
}
|
|
|
|
describe("cycle-time, resample, and diagnostics utilities", () => {
|
|
it("estimates cycle time for a trajectory and a path plan", async () => {
|
|
const runtime = await createRuntime();
|
|
const singleResponse = await dispatchKdlRpcRequest(runtime, {
|
|
id: 2,
|
|
method: "estimateCycleTime",
|
|
payload: [trajectory()]
|
|
});
|
|
expect(singleResponse.result).toMatchObject({
|
|
ok: true,
|
|
motionTime: 1,
|
|
totalTime: 1,
|
|
segmentTimes: [
|
|
{
|
|
segmentId: "s1",
|
|
motion: "MOVEJ",
|
|
duration: 1
|
|
}
|
|
],
|
|
diagnostics: []
|
|
});
|
|
|
|
const path: PathPlanResult = {
|
|
ok: true,
|
|
duration: 3,
|
|
segments: [
|
|
trajectory(),
|
|
trajectory({
|
|
motion: "MOVEL",
|
|
duration: 2,
|
|
points: trajectory().points.map((point) => ({ ...point, motion: "MOVEL", segmentId: "s2" }))
|
|
})
|
|
],
|
|
points: [],
|
|
diagnostics: []
|
|
};
|
|
const pathResponse = await dispatchKdlRpcRequest(runtime, {
|
|
id: 3,
|
|
method: "estimateCycleTime",
|
|
payload: [path]
|
|
});
|
|
expect(pathResponse.result).toMatchObject({
|
|
ok: true,
|
|
motionTime: 3,
|
|
totalTime: 3,
|
|
segmentTimes: [
|
|
{ segmentId: "s1", motion: "MOVEJ", duration: 1 },
|
|
{ segmentId: "s2", motion: "MOVEL", duration: 2 }
|
|
]
|
|
});
|
|
});
|
|
|
|
it("resamples a trajectory with stable time and point ordering", async () => {
|
|
const runtime = await createRuntime();
|
|
const response = await dispatchKdlRpcRequest(runtime, {
|
|
id: 4,
|
|
method: "resampleTrajectory",
|
|
payload: [trajectory(), 0.25]
|
|
});
|
|
|
|
expect(response.ok).toBe(true);
|
|
const result = response.result as TrajectoryResult;
|
|
expect(result.sampleTime).toBe(0.25);
|
|
expect(result.points.map((point) => point.time)).toEqual([0, 0.25, 0.5, 0.75, 1]);
|
|
expect(result.points.map((point) => point.index)).toEqual([0, 1, 2, 3, 4]);
|
|
expect(result.points[2]?.joints).toEqual([0.5, 1]);
|
|
expect(result.points[2]?.tcp.position).toEqual([0.5, 1, 0]);
|
|
expect(result.diagnostics).toContainEqual(
|
|
expect.objectContaining({
|
|
severity: "info",
|
|
code: "KDL_TRAJECTORY_RESAMPLED"
|
|
})
|
|
);
|
|
});
|
|
|
|
it("keeps structured diagnostics for warning and error cases", async () => {
|
|
const runtime = await createRuntime();
|
|
const emptyResponse = await dispatchKdlRpcRequest(runtime, {
|
|
id: 5,
|
|
method: "resampleTrajectory",
|
|
payload: [
|
|
trajectory({
|
|
points: []
|
|
}),
|
|
0.1
|
|
]
|
|
});
|
|
expect(emptyResponse.result).toMatchObject({
|
|
diagnostics: [
|
|
{
|
|
severity: "warning",
|
|
code: "KDL_RESAMPLE_EMPTY_TRAJECTORY"
|
|
}
|
|
]
|
|
});
|
|
|
|
const invalidResponse = await dispatchKdlRpcRequest(runtime, {
|
|
id: 6,
|
|
method: "resampleTrajectory",
|
|
payload: [trajectory(), 0]
|
|
});
|
|
expect(invalidResponse).toMatchObject({
|
|
ok: false,
|
|
error: {
|
|
code: "KDL_INVALID_SAMPLE_TIME",
|
|
diagnostics: [
|
|
{
|
|
severity: "error",
|
|
code: "KDL_INVALID_SAMPLE_TIME"
|
|
}
|
|
]
|
|
}
|
|
});
|
|
});
|
|
});
|