clever-queue 0.1.1 → 0.2.0
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/.gitlab-ci.yml +80 -0
- package/README.md +66 -6
- package/dist/engine/index.d.ts +14 -10
- package/dist/engine/index.js +45 -46
- package/dist/engine/index.js.map +1 -1
- package/dist/engine/interfaces.d.ts +3 -2
- package/dist/engine/interfaces.js.map +1 -1
- package/dist/helpers/id.d.ts +2 -0
- package/dist/helpers/id.js +7 -0
- package/dist/helpers/id.js.map +1 -0
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +19 -8
- package/dist/helpers/index.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +25 -15
- package/dist/index.js.map +1 -1
- package/dist/{queue → queues}/index.d.ts +8 -5
- package/dist/{queue → queues}/index.js +27 -33
- package/dist/queues/index.js.map +1 -0
- package/dist/{queue → queues}/interfaces.d.ts +9 -4
- package/dist/{queue → queues}/interfaces.js +4 -2
- package/dist/queues/interfaces.js.map +1 -0
- package/dist/{runner → runners}/index.d.ts +7 -3
- package/dist/{runner → runners}/index.js +33 -15
- package/dist/runners/index.js.map +1 -0
- package/dist/{runner → runners}/interfaces.d.ts +2 -1
- package/dist/{runner → runners}/interfaces.js +1 -1
- package/dist/runners/interfaces.js.map +1 -0
- package/dist/{task → tasks}/index.d.ts +8 -2
- package/dist/{task → tasks}/index.js +35 -16
- package/dist/tasks/index.js.map +1 -0
- package/dist/{task → tasks}/interfaces.d.ts +2 -1
- package/dist/{task → tasks}/interfaces.js +4 -0
- package/dist/tasks/interfaces.js.map +1 -0
- package/documentation/demo_1E_1Q_1R_4T.svg +1 -0
- package/documentation/demo_1E_1Q_2R_4T.svg +1 -0
- package/documentation/demo_1E_2Q_1R_8T.svg +1 -0
- package/eslint.config.mjs +68 -68
- package/examples/package-lock.json +19 -0
- package/examples/package.json +15 -0
- package/examples/src/demo_1E_1Q_1R_4T.ts +37 -0
- package/examples/src/demo_1E_1Q_2R_4T.ts +36 -0
- package/examples/src/demo_1E_2Q_1R_8T.ts +40 -0
- package/examples/src/example00.ts +16 -0
- package/examples/src/helpers/animations.ts +295 -0
- package/examples/src/helpers/index.ts +2 -0
- package/examples/src/helpers/myAsyncTaskToExecute.ts +6 -0
- package/examples/src/helpers/svg.ts +155 -0
- package/examples/src/tsconfig.json +24 -0
- package/package.json +65 -55
- package/src/engine/index.ts +37 -48
- package/src/engine/interfaces.ts +3 -2
- package/src/helpers/id.ts +4 -0
- package/src/helpers/index.ts +1 -0
- package/src/index.ts +7 -7
- package/src/{queue → queues}/index.ts +11 -28
- package/src/{queue → queues}/interfaces.ts +4 -3
- package/src/{runner → runners}/index.ts +17 -12
- package/src/{runner → runners}/interfaces.ts +2 -1
- package/src/{task → tasks}/index.ts +18 -11
- package/src/{task → tasks}/interfaces.ts +6 -1
- package/test/issues/00001.mjs +35 -0
- package/test/miscellaneous/test.mjs +13 -13
- package/test/units/engine.mjs +5 -5
- package/test/units/queue.mjs +4 -4
- package/test/units/task.mjs +6 -6
- package/dist/errors.d.ts +0 -13
- package/dist/errors.js +0 -42
- package/dist/errors.js.map +0 -1
- package/dist/queue/index.js.map +0 -1
- package/dist/queue/interfaces.js.map +0 -1
- package/dist/runner/index.js.map +0 -1
- package/dist/runner/interfaces.js.map +0 -1
- package/dist/task/index.js.map +0 -1
- package/dist/task/interfaces.js.map +0 -1
- package/exemples/index01.js +0 -116
- package/exemples/index01.ts +0 -98
- package/exemples/index02.mjs +0 -13
- package/exemples/tsconfig.json +0 -24
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "clever-queue_examples",
|
|
3
|
+
"scripts": {
|
|
4
|
+
"build": "npx tsc -p src/ --declaration",
|
|
5
|
+
"build:watch": "npx tsc -p src/ -w",
|
|
6
|
+
"prestart": "npx npm run build",
|
|
7
|
+
"start": "node dist/index.js",
|
|
8
|
+
"debug": "node --nolazy --inspect-brk=9229 dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"author": "Olivier GROSJEANNE",
|
|
11
|
+
"license": "ISC",
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"clever-queue": "../dist"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { animations, myAsyncTaskToExecute } from "./helpers";
|
|
2
|
+
import * as cleverQueue from "clever-queue";
|
|
3
|
+
|
|
4
|
+
async function run(): Promise<void> {
|
|
5
|
+
|
|
6
|
+
const engine = cleverQueue.createEngine({ autostart: false });
|
|
7
|
+
const queueA = engine.createQueue();
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const tasks: cleverQueue.tasks.Task[] = [];
|
|
11
|
+
const promiseList: Promise<unknown>[] = [];
|
|
12
|
+
|
|
13
|
+
const tasksList: { id: string; timeout: number; queue: cleverQueue.queues.Queue }[] = [
|
|
14
|
+
{ id: "task A1", timeout: 2500, queue: queueA },
|
|
15
|
+
{ id: "task A2", timeout: 2500, queue: queueA },
|
|
16
|
+
{ id: "task A3", timeout: 2500, queue: queueA },
|
|
17
|
+
{ id: "task A4", timeout: 2500, queue: queueA },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
for (const taskInfo of tasksList) {
|
|
21
|
+
const task = engine.createTask(() => myAsyncTaskToExecute(taskInfo.id, taskInfo.timeout), { id: taskInfo.id });
|
|
22
|
+
tasks.push(task);
|
|
23
|
+
promiseList.push(taskInfo.queue.enqueue(task));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const anim = new animations.Animation(engine, tasks, "./documentation/" + __filename.slice(__dirname.length + 1, -3) + ".svg");
|
|
27
|
+
|
|
28
|
+
await anim.start()
|
|
29
|
+
engine.start();
|
|
30
|
+
await Promise.all(promiseList);
|
|
31
|
+
engine.stop();
|
|
32
|
+
await anim.stop();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
(async () => {
|
|
36
|
+
await run();
|
|
37
|
+
})();
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { animations, myAsyncTaskToExecute } from "./helpers";
|
|
2
|
+
import * as cleverQueue from "clever-queue";
|
|
3
|
+
|
|
4
|
+
async function run(): Promise<void> {
|
|
5
|
+
const engine = cleverQueue.createEngine({ autostart: false });
|
|
6
|
+
engine.createRunner({ priority: cleverQueue.queues.Priorities.BestEffort });
|
|
7
|
+
const queueA = engine.createQueue();
|
|
8
|
+
|
|
9
|
+
const tasks: cleverQueue.tasks.Task[] = [];
|
|
10
|
+
const promiseList: Promise<unknown>[] = [];
|
|
11
|
+
|
|
12
|
+
const tasksList: { id: string; timeout: number; queue: cleverQueue.queues.Queue }[] = [
|
|
13
|
+
{ id: "task A1", timeout: 2500, queue: queueA },
|
|
14
|
+
{ id: "task A2", timeout: 2500, queue: queueA },
|
|
15
|
+
{ id: "task A3", timeout: 2500, queue: queueA },
|
|
16
|
+
{ id: "task A4", timeout: 2500, queue: queueA },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
for (const taskInfo of tasksList) {
|
|
20
|
+
const task = engine.createTask(() => myAsyncTaskToExecute(taskInfo.id, taskInfo.timeout), { id: taskInfo.id });
|
|
21
|
+
tasks.push(task);
|
|
22
|
+
promiseList.push(taskInfo.queue.enqueue(task));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const anim = new animations.Animation(engine, tasks, "./documentation/" + __filename.slice(__dirname.length + 1, -3) + ".svg");
|
|
26
|
+
|
|
27
|
+
await anim.start()
|
|
28
|
+
engine.start();
|
|
29
|
+
await Promise.all(promiseList);
|
|
30
|
+
engine.stop();
|
|
31
|
+
await anim.stop();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
(async () => {
|
|
35
|
+
await run();
|
|
36
|
+
})();
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { animations, myAsyncTaskToExecute } from "./helpers";
|
|
2
|
+
import * as cleverQueue from "clever-queue";
|
|
3
|
+
|
|
4
|
+
async function run(): Promise<void> {
|
|
5
|
+
const engine = cleverQueue.createEngine({ autostart: false });
|
|
6
|
+
const queueA = engine.createQueue();
|
|
7
|
+
const queueB = engine.createQueue();
|
|
8
|
+
|
|
9
|
+
const tasks: cleverQueue.tasks.Task[] = [];
|
|
10
|
+
const promiseList: Promise<unknown>[] = [];
|
|
11
|
+
|
|
12
|
+
const tasksList: { id: string; timeout: number; queue: cleverQueue.queues.Queue }[] = [
|
|
13
|
+
{ id: "task A1", timeout: 2500, queue: queueA },
|
|
14
|
+
{ id: "task A2", timeout: 2500, queue: queueA },
|
|
15
|
+
{ id: "task A3", timeout: 2500, queue: queueA },
|
|
16
|
+
{ id: "task A4", timeout: 2500, queue: queueA },
|
|
17
|
+
{ id: "task B1", timeout: 2500, queue: queueB },
|
|
18
|
+
{ id: "task B2", timeout: 2500, queue: queueB },
|
|
19
|
+
{ id: "task B3", timeout: 2500, queue: queueB },
|
|
20
|
+
{ id: "task B4", timeout: 2500, queue: queueB },
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
for (const taskInfo of tasksList) {
|
|
24
|
+
const task = engine.createTask(() => myAsyncTaskToExecute(taskInfo.id, taskInfo.timeout), { id: taskInfo.id });
|
|
25
|
+
tasks.push(task);
|
|
26
|
+
promiseList.push(taskInfo.queue.enqueue(task));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const anim = new animations.Animation(engine, tasks, "./documentation/" + __filename.slice(__dirname.length + 1, -3) + ".svg");
|
|
30
|
+
|
|
31
|
+
await anim.start()
|
|
32
|
+
engine.start();
|
|
33
|
+
await Promise.all(promiseList);
|
|
34
|
+
engine.stop();
|
|
35
|
+
await anim.stop();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
(async () => {
|
|
39
|
+
await run();
|
|
40
|
+
})();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as cleverQueue from "clever-queue"; // import the library - no dependancies
|
|
2
|
+
|
|
3
|
+
const engine = cleverQueue.createEngine(); // create and start (by default) engine with a single best effort runner (by default)
|
|
4
|
+
const queue = engine.createQueue(); // create your first queue, with standard priority (by default) and default weight (by default)
|
|
5
|
+
|
|
6
|
+
const myAsyncTaskToExecute: cleverQueue.tasks.FunctionToExecute = async function (message: string, timeout: number) {
|
|
7
|
+
// do your stuff here - In this case, just wait for the timeout and return the message
|
|
8
|
+
await new Promise((resolve) => setTimeout(resolve, timeout));
|
|
9
|
+
return message;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
(async () => {
|
|
13
|
+
const result = await queue.createTaskAndEnqueue(() => myAsyncTaskToExecute("myValue", 1000));
|
|
14
|
+
console.log(result);
|
|
15
|
+
engine.stop(); // stop the engine when you want
|
|
16
|
+
})();
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
const fs = require("node:fs");
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
|
|
4
|
+
import * as svg from "./svg";
|
|
5
|
+
import * as cleverQueue from "clever-queue";
|
|
6
|
+
|
|
7
|
+
const WIDTH = 800;
|
|
8
|
+
const HEIGHT = 800;
|
|
9
|
+
const RADIUS = 20;
|
|
10
|
+
|
|
11
|
+
const TASK_WIDTH = 80;
|
|
12
|
+
const TASK_HEIGHT = 80;
|
|
13
|
+
const TASK_MARGIN = 10;
|
|
14
|
+
|
|
15
|
+
const TASKS_TO_FIT_IN_QUEUE = 4;
|
|
16
|
+
const QUEUE_WIDTH = TASK_WIDTH + 2 * TASK_MARGIN;;
|
|
17
|
+
const QUEUE_HEIGHT = TASK_HEIGHT * TASKS_TO_FIT_IN_QUEUE + (TASKS_TO_FIT_IN_QUEUE - 1) * (TASK_MARGIN);
|
|
18
|
+
const QUEUE_Y = 100;
|
|
19
|
+
|
|
20
|
+
const RUNNER_WIDTH = TASK_WIDTH + 2 * TASK_MARGIN;
|
|
21
|
+
const RUNNER_HEIGHT = TASK_HEIGHT + 2 * TASK_MARGIN;
|
|
22
|
+
const RUNNER_Y = QUEUE_Y + QUEUE_HEIGHT + 2 * TASK_HEIGHT;
|
|
23
|
+
|
|
24
|
+
const STARTINGZONE_Y = 10;
|
|
25
|
+
const RUNNEDZONE_Y = RUNNER_Y + RUNNER_HEIGHT + 2 * TASK_HEIGHT;
|
|
26
|
+
|
|
27
|
+
const INTERVAL = 1;
|
|
28
|
+
|
|
29
|
+
const svgFile: svg.SvgFile = {
|
|
30
|
+
svg: {
|
|
31
|
+
$xmlns: "http://www.w3.org/2000/svg",
|
|
32
|
+
$width: WIDTH,
|
|
33
|
+
$height: HEIGHT,
|
|
34
|
+
g: [
|
|
35
|
+
{
|
|
36
|
+
rect: [
|
|
37
|
+
{
|
|
38
|
+
$x: 10,
|
|
39
|
+
$y: 10,
|
|
40
|
+
$width: 10,
|
|
41
|
+
$height: 10,
|
|
42
|
+
// $fill: "lightgreen",
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
animateMotion: [
|
|
46
|
+
{
|
|
47
|
+
$id: "start",
|
|
48
|
+
$begin: "stop.end;0",
|
|
49
|
+
$dur: 2,
|
|
50
|
+
$path: "M" + WIDTH / 2 + ",-" + HEIGHT,
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
class Animation {
|
|
59
|
+
engine: cleverQueue.engine.Engine;
|
|
60
|
+
tasks: cleverQueue.tasks.Task[];
|
|
61
|
+
interval: NodeJS.Timeout | undefined = undefined;
|
|
62
|
+
tick: number = 0;
|
|
63
|
+
svgFileOutputPath: string;
|
|
64
|
+
|
|
65
|
+
constructor(cqEngine: cleverQueue.engine.Engine, tasks: cleverQueue.tasks.Task[], svgFileOutputPath: string) {
|
|
66
|
+
this.engine = cqEngine;
|
|
67
|
+
this.tasks = tasks;
|
|
68
|
+
this.drawQueues(this.engine.queues);
|
|
69
|
+
this.drawRunners(this.engine.runners);
|
|
70
|
+
this.drawTasks(tasks);
|
|
71
|
+
this.svgFileOutputPath = path.resolve(process.cwd(), svgFileOutputPath);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async start(): Promise<void> {
|
|
75
|
+
this.interval = setInterval(() => {
|
|
76
|
+
console.log(JSON.stringify(this.engine.statistics(), null, 2));
|
|
77
|
+
this.tick += INTERVAL;
|
|
78
|
+
this.animateMotionTasks(this.tasks);
|
|
79
|
+
}, INTERVAL * 1000);
|
|
80
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async stop(): Promise<void> {
|
|
84
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
85
|
+
if (this.interval) this.interval.close();
|
|
86
|
+
|
|
87
|
+
svgFile.svg.g?.push({
|
|
88
|
+
rect: [
|
|
89
|
+
{
|
|
90
|
+
$x: 10,
|
|
91
|
+
$y: 10,
|
|
92
|
+
$width: 10,
|
|
93
|
+
$height: 10,
|
|
94
|
+
// $fill: "lightgreen",
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
animateMotion: [
|
|
98
|
+
{
|
|
99
|
+
$id: "stop",
|
|
100
|
+
$begin: "start.end + " + this.tick.toString() + "s",
|
|
101
|
+
$dur: 2,
|
|
102
|
+
$path: "M" + WIDTH / 2 + ",-" + HEIGHT,
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
});
|
|
106
|
+
const builder = new svg.XMLBuilder(svg.options);
|
|
107
|
+
const xmlDataString = builder.build(svgFile);
|
|
108
|
+
|
|
109
|
+
fs.writeFileSync(this.svgFileOutputPath, xmlDataString);
|
|
110
|
+
console.log(xmlDataString);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
drawQueues(queues: cleverQueue.queues.Queue[]): void {
|
|
114
|
+
const queueCount = queues.length;
|
|
115
|
+
const freeSpace = WIDTH - queueCount * RUNNER_WIDTH;
|
|
116
|
+
const spacesCount = queueCount + 1;
|
|
117
|
+
const spaceWidth = freeSpace / spacesCount;
|
|
118
|
+
for (let q = 0; q < queues.length; q++) {
|
|
119
|
+
const queue = queues[q];
|
|
120
|
+
queue.tags["x"] = (q + 1) * spaceWidth + q * QUEUE_WIDTH;
|
|
121
|
+
queue.tags["y"] = QUEUE_Y;
|
|
122
|
+
queue.tags["width"] = QUEUE_WIDTH;
|
|
123
|
+
queue.tags["height"] = QUEUE_HEIGHT;
|
|
124
|
+
svgFile.svg.g?.push({
|
|
125
|
+
$id: queue.options.id ?? "",
|
|
126
|
+
g: [
|
|
127
|
+
{
|
|
128
|
+
rect: [
|
|
129
|
+
{
|
|
130
|
+
$x: queue.tags["x"],
|
|
131
|
+
$y: queue.tags["y"],
|
|
132
|
+
$width: queue.tags["width"],
|
|
133
|
+
$height: queue.tags["height"],
|
|
134
|
+
$rx: RADIUS,
|
|
135
|
+
$ry: RADIUS,
|
|
136
|
+
$fill: "lightblue",
|
|
137
|
+
$stroke: "blue",
|
|
138
|
+
"$stroke-width": 5,
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
drawRunners(runners: cleverQueue.runners.Runner[]): void {
|
|
148
|
+
const runnersCount = runners.length;
|
|
149
|
+
const freeSpace = WIDTH - runnersCount * RUNNER_WIDTH;
|
|
150
|
+
const spacesCount = runners.length + 1;
|
|
151
|
+
const spaceWidth = freeSpace / spacesCount;
|
|
152
|
+
// console.log(runnersCount, freeSpace, spacesCount, spaceWidth);
|
|
153
|
+
for (let r = 0; r < runners.length; r++) {
|
|
154
|
+
const runner = runners[r];
|
|
155
|
+
runner.tags["x"] = (r + 1) * spaceWidth + r * RUNNER_WIDTH;
|
|
156
|
+
runner.tags["y"] = RUNNER_Y;
|
|
157
|
+
runner.tags["width"] = RUNNER_WIDTH;
|
|
158
|
+
runner.tags["height"] = RUNNER_HEIGHT;
|
|
159
|
+
svgFile.svg.g?.push({
|
|
160
|
+
$id: runner.options.id ?? "",
|
|
161
|
+
g: [
|
|
162
|
+
{
|
|
163
|
+
rect: [
|
|
164
|
+
{
|
|
165
|
+
$x: runner.tags["x"],
|
|
166
|
+
$y: runner.tags["y"],
|
|
167
|
+
$width: runner.tags["width"],
|
|
168
|
+
$height: runner.tags["height"],
|
|
169
|
+
$rx: RADIUS,
|
|
170
|
+
$ry: RADIUS,
|
|
171
|
+
$fill: "lightgreen",
|
|
172
|
+
$stroke: "green",
|
|
173
|
+
"$stroke-width": 5,
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
drawTasks(tasks: cleverQueue.tasks.Task[]): void {
|
|
183
|
+
for (let t = 0; t < tasks.length; t++) {
|
|
184
|
+
const task = tasks[t];
|
|
185
|
+
svgFile.svg.g?.push({
|
|
186
|
+
$id: task.options.id ?? "",
|
|
187
|
+
g: [
|
|
188
|
+
{
|
|
189
|
+
rect: [
|
|
190
|
+
{
|
|
191
|
+
$width: TASK_WIDTH,
|
|
192
|
+
$height: TASK_HEIGHT,
|
|
193
|
+
$rx: 20,
|
|
194
|
+
$ry: 20,
|
|
195
|
+
$fill: "blue",
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
$width: 20,
|
|
199
|
+
$height: 10,
|
|
200
|
+
$rx: 20,
|
|
201
|
+
$ry: 20,
|
|
202
|
+
$fill: "green",
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
text: [{ $dx: TASK_WIDTH / 2, $dy: TASK_HEIGHT / 2, "$dominant-baseline": "middle", "$text-anchor": "middle", $fill: "white", $$content: task.options.id ?? "" }],
|
|
206
|
+
},
|
|
207
|
+
],
|
|
208
|
+
animateMotion: [
|
|
209
|
+
{
|
|
210
|
+
$begin: 0,
|
|
211
|
+
$dur: 0,
|
|
212
|
+
$path: "M" + (WIDTH / 2 - TASK_WIDTH / 2) + ",-" + TASK_HEIGHT,
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
getLastPoint(animateMotion: svg.AnimateMotion): svg.Point | null {
|
|
220
|
+
if (!animateMotion) return null;
|
|
221
|
+
const path = svg.parsePath(animateMotion.$path);
|
|
222
|
+
console.log(path, path.length);
|
|
223
|
+
const lastPointInPath = path[path.length - 1];
|
|
224
|
+
if (!lastPointInPath) return null;
|
|
225
|
+
console.log(typeof lastPointInPath, "x" in lastPointInPath, lastPointInPath);
|
|
226
|
+
if ("x" in lastPointInPath && "y" in lastPointInPath) return { x: lastPointInPath.x, y: lastPointInPath.y };
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
getTaskPosition(task: cleverQueue.tasks.Task): { x: number; y: number } {
|
|
231
|
+
console.log(JSON.stringify({ statistics: task.statistics() }));
|
|
232
|
+
let x = 0;
|
|
233
|
+
let y = 0;
|
|
234
|
+
if (task.status === "queued" && task.queueId) {
|
|
235
|
+
// task is in a queue
|
|
236
|
+
for (let q = 0; q < this.engine.queues.length; q++) {
|
|
237
|
+
const queue = this.engine.queues[q];
|
|
238
|
+
if (queue.options.id === task.queueId) {
|
|
239
|
+
x = (queue.tags["x"] as number) + QUEUE_WIDTH / 2 - TASK_WIDTH / 2;
|
|
240
|
+
const positionInQueue = queue.tasks.findIndex((t) => t.options.id === task.options.id);
|
|
241
|
+
const availableHeight = QUEUE_HEIGHT - (TASK_HEIGHT + 2 * TASK_MARGIN);
|
|
242
|
+
let dy = availableHeight / (queue.tasks.length - 1);
|
|
243
|
+
if (dy > TASK_HEIGHT + TASK_MARGIN) dy = TASK_HEIGHT + TASK_MARGIN;
|
|
244
|
+
y = (queue.tags["y"] as number) + QUEUE_HEIGHT - (TASK_HEIGHT + TASK_MARGIN) - dy * positionInQueue;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (task.status === "running" && task.runnerId) {
|
|
250
|
+
// task is running
|
|
251
|
+
for (let r = 0; r < this.engine.runners.length; r++) {
|
|
252
|
+
const runner = this.engine.runners[r];
|
|
253
|
+
if (runner.options.id === task.runnerId) {
|
|
254
|
+
x = (runner.tags["x"] as number) + RUNNER_WIDTH / 2 - TASK_WIDTH / 2;
|
|
255
|
+
y = (runner.tags["y"] as number) + RUNNER_HEIGHT / 2 - TASK_HEIGHT / 2;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (task.status === "runned") {
|
|
260
|
+
// task has runned
|
|
261
|
+
x = WIDTH / 2 - TASK_WIDTH / 2;
|
|
262
|
+
y = RUNNEDZONE_Y;
|
|
263
|
+
}
|
|
264
|
+
return { x: Math.round(x), y: Math.round(y) };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
animateMotionTasks(tasks: cleverQueue.tasks.Task[]): void {
|
|
268
|
+
console.log("animateMotionTasks");
|
|
269
|
+
for (let t = 0; t < tasks.length; t++) {
|
|
270
|
+
const task = tasks[t];
|
|
271
|
+
const svgTask = svgFile.svg.g?.find((element) => {
|
|
272
|
+
return element.$id === task.options.id;
|
|
273
|
+
}) as svg.G;
|
|
274
|
+
if (svgTask) {
|
|
275
|
+
if (!svgTask.animateMotion) svgTask.animateMotion = [];
|
|
276
|
+
const animateMotion = svgTask.animateMotion[svgTask.animateMotion.length - 1];
|
|
277
|
+
const startPoint = this.getLastPoint(animateMotion);
|
|
278
|
+
if (startPoint) {
|
|
279
|
+
const endPoint: svg.Point = this.getTaskPosition(task);
|
|
280
|
+
console.log({ startPoint, endPoint });
|
|
281
|
+
if (startPoint && endPoint) {
|
|
282
|
+
svgTask.animateMotion.push({
|
|
283
|
+
//$begin: "id.begin+" + (animateMotion.$begin + INTERVAL).toString + "s",
|
|
284
|
+
$begin: "start.end+" + this.tick.toString() + "s",
|
|
285
|
+
$dur: INTERVAL,
|
|
286
|
+
//$end: animateMotion.$end + INTERVAL,
|
|
287
|
+
$path: "M" + startPoint.x + "," + startPoint.y + " L" + endPoint.x + "," + endPoint.y,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
export { Animation };
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
export { XMLBuilder } from "fast-xml-parser";
|
|
2
|
+
|
|
3
|
+
const options = {
|
|
4
|
+
ignoreAttributes: false,
|
|
5
|
+
attributeNamePrefix: "$",
|
|
6
|
+
textNodeName: "$$content",
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
interface Element {
|
|
10
|
+
$id?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface G extends Element {
|
|
14
|
+
g?: G[];
|
|
15
|
+
rect?: Rect[];
|
|
16
|
+
text?: Text[];
|
|
17
|
+
animateMotion?: AnimateMotion[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface Svg extends Element {
|
|
21
|
+
$xmlns: "http://www.w3.org/2000/svg";
|
|
22
|
+
$width: number;
|
|
23
|
+
$height: number;
|
|
24
|
+
g?: G[];
|
|
25
|
+
rect?: Rect[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type Stroke = string;
|
|
29
|
+
|
|
30
|
+
interface Rect extends Element {
|
|
31
|
+
$width: number;
|
|
32
|
+
$height: number;
|
|
33
|
+
$x?: number;
|
|
34
|
+
$y?: number;
|
|
35
|
+
$rx?: number;
|
|
36
|
+
$ry?: number;
|
|
37
|
+
$fill?: string;
|
|
38
|
+
$stroke?: Stroke;
|
|
39
|
+
"$stroke-width"?: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface Text extends Element {
|
|
43
|
+
$dx: number;
|
|
44
|
+
$dy: number;
|
|
45
|
+
"$dominant-baseline": string;
|
|
46
|
+
"$text-anchor": string;
|
|
47
|
+
$fill: string;
|
|
48
|
+
$$content: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface MoveTo extends Point {
|
|
52
|
+
command: "M" | "m";
|
|
53
|
+
}
|
|
54
|
+
interface LineTo extends Point {
|
|
55
|
+
command: "L" | "l" | "H" | "h" | "V" | "v";
|
|
56
|
+
}
|
|
57
|
+
interface CubicBezierCurve extends Point {
|
|
58
|
+
command: "C" | "c" | "S" | "s";
|
|
59
|
+
x1: number;
|
|
60
|
+
y1: number;
|
|
61
|
+
x2: number;
|
|
62
|
+
y2: number;
|
|
63
|
+
}
|
|
64
|
+
interface QuadraticBezierCurve extends Point {
|
|
65
|
+
command: "Q" | "q" | "T" | "t";
|
|
66
|
+
}
|
|
67
|
+
interface EllipticalArcCurve extends Point {
|
|
68
|
+
command: "A" | "a";
|
|
69
|
+
}
|
|
70
|
+
interface ClosePath {
|
|
71
|
+
command: "Z" | "z";
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
type PathElement =
|
|
75
|
+
| MoveTo // : M, m
|
|
76
|
+
| LineTo // : L, l, H, h, V, v
|
|
77
|
+
| CubicBezierCurve // : C, c, S, s
|
|
78
|
+
| QuadraticBezierCurve // : Q, q, T, t
|
|
79
|
+
| EllipticalArcCurve // : A, a
|
|
80
|
+
| ClosePath; // : Z, z
|
|
81
|
+
|
|
82
|
+
type Path = PathElement[];
|
|
83
|
+
|
|
84
|
+
interface Point {
|
|
85
|
+
x: number;
|
|
86
|
+
y: number;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface AnimateMotion extends Element {
|
|
90
|
+
$begin: string | number;
|
|
91
|
+
// $end: number;
|
|
92
|
+
$dur: number;
|
|
93
|
+
$repeatCount?: number | "indefinite";
|
|
94
|
+
$path: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
interface SvgFile {
|
|
98
|
+
svg: Svg;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function parseNextPointInPath(path: string, start: number): { x: number; y: number; end: number } {
|
|
102
|
+
let end = start;
|
|
103
|
+
while (end < path.length && path[end] !== " ") end++;
|
|
104
|
+
const coordList = path.substring(start, end).split(",");
|
|
105
|
+
if (coordList.length != 2) throw "parse error parseNextPointInPath " + path + " " + start;
|
|
106
|
+
return { x: parseInt(coordList[0]), y: parseInt(coordList[1]), end };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function parsePath(path: string): Path {
|
|
110
|
+
const result: Path = [];
|
|
111
|
+
let i = 0;
|
|
112
|
+
while (i < path.length) {
|
|
113
|
+
const command = path[i];
|
|
114
|
+
if (command === "M" || command === "m") {
|
|
115
|
+
const { x, y, end } = parseNextPointInPath(path, i + 1);
|
|
116
|
+
i = end;
|
|
117
|
+
const element: MoveTo = { command, x, y };
|
|
118
|
+
result.push(element);
|
|
119
|
+
}
|
|
120
|
+
if (command === "L" || command === "l" || command === "H" || command === "h" || command === "V" || command === "v") {
|
|
121
|
+
const { x, y, end } = parseNextPointInPath(path, i + 1);
|
|
122
|
+
i = end;
|
|
123
|
+
const element: LineTo = { command, x, y };
|
|
124
|
+
result.push(element);
|
|
125
|
+
}
|
|
126
|
+
if (command === "C" || command === "c" || command === "S" || command === "s") {
|
|
127
|
+
const { x: x1, y: y1, end: end1 } = parseNextPointInPath(path, i + 1);
|
|
128
|
+
const { x: x2, y: y2, end: end2 } = parseNextPointInPath(path, end1 + 1);
|
|
129
|
+
const { x, y, end } = parseNextPointInPath(path, end2 + 1);
|
|
130
|
+
i = end;
|
|
131
|
+
const element: CubicBezierCurve = { command, x, y, x1, y1, x2, y2 };
|
|
132
|
+
result.push(element);
|
|
133
|
+
}
|
|
134
|
+
if (command === "Q" || command === "q" || command === "T" || command === "t") {
|
|
135
|
+
const { x, y, end } = parseNextPointInPath(path, i + 1);
|
|
136
|
+
i = end;
|
|
137
|
+
const element: QuadraticBezierCurve = { command, x, y };
|
|
138
|
+
result.push(element);
|
|
139
|
+
}
|
|
140
|
+
if (command === "A" || command === "a") {
|
|
141
|
+
const { x, y, end } = parseNextPointInPath(path, i + 1);
|
|
142
|
+
i = end;
|
|
143
|
+
const element: EllipticalArcCurve = { command, x, y };
|
|
144
|
+
result.push(element);
|
|
145
|
+
}
|
|
146
|
+
if (command === "Z" || command === "z") {
|
|
147
|
+
const element: ClosePath = { command };
|
|
148
|
+
result.push(element);
|
|
149
|
+
}
|
|
150
|
+
i++;
|
|
151
|
+
}
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export { SvgFile, options, G, AnimateMotion, Point, parsePath };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "CommonJS",
|
|
4
|
+
"esModuleInterop": true,
|
|
5
|
+
"target": "es2022",
|
|
6
|
+
"moduleResolution": "node",
|
|
7
|
+
"outDir": "../dist",
|
|
8
|
+
"lib": [
|
|
9
|
+
"es2022",
|
|
10
|
+
],
|
|
11
|
+
"strict": true,
|
|
12
|
+
"useUnknownInCatchVariables": true,
|
|
13
|
+
"noImplicitAny": true,
|
|
14
|
+
"strictNullChecks": true,
|
|
15
|
+
"strictFunctionTypes": true,
|
|
16
|
+
"strictBindCallApply": true,
|
|
17
|
+
"strictPropertyInitialization": true,
|
|
18
|
+
"noImplicitThis": true,
|
|
19
|
+
"alwaysStrict": true,
|
|
20
|
+
"forceConsistentCasingInFileNames": true
|
|
21
|
+
},
|
|
22
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
23
|
+
"display": "Recommended"
|
|
24
|
+
}
|