@woosh/meep-engine 2.49.6 → 2.49.7

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.
Files changed (31) hide show
  1. package/package.json +1 -1
  2. package/src/core/cache/LoadingCache.d.ts +2 -0
  3. package/src/core/cache/LoadingCache.js +45 -18
  4. package/src/core/math/computeGreatestCommonDivisor.spec.js +9 -0
  5. package/src/core/math/statistics/computeStatisticalMean.spec.js +9 -0
  6. package/src/core/math/statistics/halton_sequence.spec.js +6 -1
  7. package/src/core/model/node-graph/node/Port.spec.js +10 -1
  8. package/src/core/model/reactive/evaluation/MultiPredicateEvaluator.js +5 -8
  9. package/src/core/model/reactive/evaluation/MultiPredicateEvaluator.spec.js +41 -0
  10. package/src/core/model/reactive/trigger/BlackboardTrigger.js +5 -0
  11. package/src/core/model/reactive/trigger/BlackboardTrigger.spec.js +24 -0
  12. package/src/core/parser/simple/readBooleanToken.js +38 -0
  13. package/src/core/parser/simple/readHexToken.js +95 -0
  14. package/src/core/parser/simple/readHexToken.spec.js +21 -0
  15. package/src/core/parser/simple/readIdentifierToken.js +43 -0
  16. package/src/core/parser/simple/readIdentifierToken.spec.js +32 -0
  17. package/src/core/parser/simple/readLiteralToken.js +96 -0
  18. package/src/core/parser/simple/readNumberToken.js +73 -0
  19. package/src/core/parser/simple/readNumberToken.spec.js +17 -0
  20. package/src/core/parser/simple/readReferenceToken.js +48 -0
  21. package/src/core/parser/simple/readReferenceToken.spec.js +18 -0
  22. package/src/core/parser/simple/readStringToken.js +94 -0
  23. package/src/core/parser/simple/readStringToken.spec.js +57 -0
  24. package/src/core/parser/simple/{readUnsignedInteger.js → readUnsignedIntegerToken.js} +1 -1
  25. package/src/core/parser/simple/readUnsignedIntegerToken.spec.js +6 -0
  26. package/src/core/process/executor/ConcurrentExecutor.js +147 -234
  27. package/src/engine/intelligence/blackboard/Blackboard.d.ts +4 -0
  28. package/src/engine/intelligence/blackboard/Blackboard.js +11 -0
  29. package/src/view/tooltip/gml/parser/readReferenceValueToken.js +2 -2
  30. package/src/core/parser/simple/SimpleParser.js +0 -464
  31. package/src/core/parser/simple/SimpleParser.spec.js +0 -105
@@ -7,7 +7,6 @@ import { IllegalStateException } from "../../fsm/exceptions/IllegalStateExceptio
7
7
  import { objectKeyByValue } from "../../model/object/objectKeyByValue.js";
8
8
  import { TaskSignal } from "../task/TaskSignal.js";
9
9
  import TaskState from "../task/TaskState.js";
10
- import { clamp } from "../../math/clamp.js";
11
10
 
12
11
  /**
13
12
  *
@@ -22,13 +21,20 @@ function isGroup(t) {
22
21
  * @class
23
22
  */
24
23
  class ConcurrentExecutor {
24
+ #cycle_count = 0;
25
+ /**
26
+ * Handle of the last scheduled `setTimeout`
27
+ * @type {number}
28
+ */
29
+ #timeout_handle = -1;
30
+
25
31
  /**
26
32
  *
27
33
  * @param {number} [quietTime] in milliseconds
28
34
  * @param {number} [workTime] in milliseconds
29
35
  * @constructor
30
36
  */
31
- constructor(quietTime=1, workTime=15) {
37
+ constructor(quietTime = 1, workTime = 15) {
32
38
  /**
33
39
  *
34
40
  * @type {number}
@@ -58,6 +64,8 @@ class ConcurrentExecutor {
58
64
  completed: new Signal()
59
65
  };
60
66
 
67
+ this.busy = false;
68
+
61
69
  /**
62
70
  *
63
71
  * @type {number|SchedulingPolicy}
@@ -298,142 +306,188 @@ class ConcurrentExecutor {
298
306
  }
299
307
 
300
308
  /**
301
- * kicks the scheduler into action, this is an internal method and should not be called from outside
302
- * @private
309
+ *
310
+ * @return {Task|undefined}
303
311
  */
304
- prod() {
305
- const self = this;
312
+ #pick_next_task() {
313
+ const ready = this.queueReady;
314
+
315
+ switch (this.policy) {
316
+ case ConcurrentExecutor.POLICY.ROUND_ROBIN:
317
+ return ready[this.#cycle_count % ready.length];
318
+ default:
319
+ console.warn('Unknown scheduling policy: ', this.policy, 'Defaulting to sequential');
320
+ // fallthrough
321
+ case ConcurrentExecutor.POLICY.SEQUENTIAL:
322
+ return ready[0];
323
+ }
324
+ }
325
+
326
+ /**
327
+ *
328
+ * @param {Task} task
329
+ */
330
+ #complete_task(task) {
331
+ const readyTasks = this.queueReady;
332
+
333
+ const taskIndex = readyTasks.indexOf(task);
334
+
335
+ if (taskIndex !== -1) {
336
+ readyTasks.splice(taskIndex, 1);
337
+ } else {
338
+ console.error("Failed to remove ready task, not found in the ready queue", task, readyTasks.slice());
339
+ }
340
+
306
341
 
307
- let i = 0;
342
+ task.state.set(TaskState.SUCCEEDED);
343
+ task.on.completed.send1(this);
308
344
 
309
345
  this.resolveTasks();
310
346
 
347
+ this.on.task_completed.send1(task);
348
+ // console.warn(`Task complete '${task.name}', cycles=${task.__executedCycleCount}, time=${task.__executedCpuTime}`);
349
+
350
+ }
351
+
352
+ /**
353
+ *
354
+ * @param {Task} task
355
+ * @param {*} [reason]
356
+ */
357
+ #fail_task(task, reason) {
311
358
  const readyTasks = this.queueReady;
312
- let readyTaskCount;
359
+ const taskIndex = readyTasks.indexOf(task);
313
360
 
314
- function updateTaskCount() {
315
- readyTaskCount = readyTasks.length;
361
+ if (taskIndex !== -1) {
362
+ readyTasks.splice(taskIndex, 1);
363
+ } else {
364
+ console.error("Failed to remove ready task, not found in the ready queue", task, readyTasks.slice());
316
365
  }
317
366
 
318
- function pickNextTaskRoundRobin() {
319
- return readyTasks[i++ % readyTaskCount];
320
- }
367
+ task.state.set(TaskState.FAILED);
368
+ task.on.failed.send1(reason);
321
369
 
322
- function pickNextTaskSequential() {
323
- return readyTasks[0];
324
- }
370
+ this.resolveTasks();
325
371
 
326
- /**
327
- * @type {function():Task}
328
- */
329
- let pickNextTask;
372
+ this.on.task_completed.send1(task);
373
+ }
330
374
 
331
- switch (this.policy) {
332
- case ConcurrentExecutor.POLICY.ROUND_ROBIN:
333
- pickNextTask = pickNextTaskRoundRobin;
334
- break;
335
- case ConcurrentExecutor.POLICY.SEQUENTIAL:
336
- pickNextTask = pickNextTaskSequential;
337
- break;
338
- default:
339
- console.warn('Unknown scheduling policy: ', this.policy, 'Defaulting to sequential');
340
- pickNextTask = pickNextTaskSequential;
341
- break;
342
- }
375
+ /**
376
+ *
377
+ * @param {Task} task
378
+ * @param {number} time in milliseconds
379
+ * @param {function} completionCallback
380
+ * @param {function} failureCallback
381
+ */
382
+ #runTaskForTimeMonitored(task, time) {
383
+ let cycle_count = 0;
343
384
 
344
385
  /**
345
386
  *
346
- * @param {Task} task
387
+ * @type {function(): TaskSignal}
347
388
  */
348
- function completeTask(task) {
389
+ const cycle = task.cycle;
349
390
 
350
- const taskIndex = readyTasks.indexOf(task);
391
+ const startTime = performance.now();
351
392
 
352
- if (taskIndex !== -1) {
353
- readyTasks.splice(taskIndex, 1);
354
- } else {
355
- console.error("Failed to remove ready task, not found in the ready queue", task, readyTasks.slice());
356
- }
393
+ const endTime = startTime + time;
357
394
 
395
+ //We use tiny delta to avoid problems with timer accuracy on some systems that result in 0 measured execution time
396
+ let t = startTime + 0.000001;
358
397
 
359
- task.state.set(TaskState.SUCCEEDED);
360
- task.on.completed.send1(self);
398
+ let signal;
361
399
 
362
- self.resolveTasks();
363
- updateTaskCount();
400
+ while (t < endTime) {
401
+ cycle_count++;
364
402
 
365
- self.on.task_completed.send1(task);
366
- // console.warn(`Task complete '${task.name}', cycles=${task.__executedCycleCount}, time=${task.__executedCpuTime}`);
367
- }
403
+ signal = cycle();
404
+ t = performance.now();
368
405
 
369
- /**
370
- *
371
- * @param {Task} task
372
- * @param {*} reason
373
- */
374
- function failTask(task, reason) {
375
- const taskIndex = readyTasks.indexOf(task);
406
+ if (signal === TaskSignal.Continue) {
407
+ continue;
408
+ }
409
+
410
+ if (signal === TaskSignal.Yield) {
411
+ //give up current quanta
412
+ break;
413
+ }
376
414
 
377
- if (taskIndex !== -1) {
378
- readyTasks.splice(taskIndex, 1);
415
+ if (signal === TaskSignal.EndSuccess) {
416
+ break;
417
+ } else if (signal === TaskSignal.EndFailure) {
418
+ break;
379
419
  } else {
380
- console.error("Failed to remove ready task, not found in the ready queue", task, readyTasks.slice());
420
+ throw new Error(`Task '${task.name}' produced unknown signal: ` + signal);
381
421
  }
422
+ }
382
423
 
383
- task.state.set(TaskState.FAILED);
384
- task.on.failed.dispatch(reason);
424
+ const executionDuration = t - startTime;
385
425
 
386
- self.resolveTasks();
387
- updateTaskCount();
426
+ task.__executedCpuTime += executionDuration;
427
+ task.__executedCycleCount += cycle_count;
388
428
 
389
- self.on.task_completed.send1(task);
429
+ if (signal === TaskSignal.EndSuccess) {
430
+ this.#complete_task(task);
431
+ } else if (signal === TaskSignal.EndFailure) {
432
+ this.#fail_task(task, "Task signalled failure");
390
433
  }
391
434
 
392
- function executeTimeSlice() {
393
- let sliceTimeLeft = self.workTime;
435
+ return executionDuration;
436
+ }
394
437
 
395
- let executionTime = 0;
438
+ #bound_executeTimeSlice = this.#executeTimeSlice.bind(this);
396
439
 
397
- while (readyTaskCount > 0) {
398
- const task = pickNextTask();
440
+ #executeTimeSlice() {
441
+ let sliceTimeLeft = this.workTime;
399
442
 
400
- if (task === undefined) {
401
- console.warn('Next task not found, likely result of removing task mid-execution');
402
- break;
403
- }
443
+ let executionTime = 0;
404
444
 
405
- try {
406
- executionTime = runTaskForTimeMonitored(task, sliceTimeLeft, completeTask, failTask);
407
- } catch (e) {
408
- console.error(`Task threw an exception`, task, e);
409
- failTask(task, e);
410
- }
445
+ const queueReady = this.queueReady;
411
446
 
412
- //make sure that execution time that we subtract from current CPU slice is always reducing the slice
413
- sliceTimeLeft -= Math.max(executionTime, 1);
447
+ while (queueReady.length > 0) {
448
+ const task = this.#pick_next_task();
414
449
 
415
- if (sliceTimeLeft <= 0) {
416
- break;
417
- }
450
+ if (task === undefined) {
451
+ console.warn('Next task not found, likely result of removing task mid-execution');
452
+ break;
418
453
  }
419
454
 
420
- //update task count
421
- updateTaskCount();
455
+ try {
456
+ executionTime = this.#runTaskForTimeMonitored(task, sliceTimeLeft);
457
+ } catch (e) {
458
+ console.error(`Task threw an exception`, task, e);
459
+ this.#fail_task(task, e);
460
+ }
422
461
 
423
- if (readyTaskCount > 0) {
424
- //schedule next time slice
425
- setTimeout(executeTimeSlice, self.quietTime);
426
- } else {
427
- self.busy = false;
428
- self.on.completed.send0();
462
+ this.#cycle_count++;
463
+
464
+ //make sure that execution time that we subtract from current CPU slice is always reducing the slice
465
+ sliceTimeLeft -= Math.max(executionTime, 1);
466
+
467
+ if (sliceTimeLeft <= 0) {
468
+ break;
429
469
  }
430
470
  }
431
471
 
432
- updateTaskCount();
472
+ if (this.queueReady.length === 0) {
473
+ this.busy = false;
474
+ this.on.completed.send0();
475
+ } else {
476
+ //schedule next time slice
477
+ this.#timeout_handle = setTimeout(this.#bound_executeTimeSlice, this.quietTime);
478
+ }
479
+ }
480
+
481
+ /**
482
+ * kicks the scheduler into action, this is an internal method and should not be called from outside
483
+ * @private
484
+ */
485
+ prod() {
486
+ this.resolveTasks();
433
487
 
434
- if (!this.busy && readyTaskCount > 0) {
488
+ if (!this.busy && this.queueReady.length > 0) {
435
489
  this.busy = true;
436
- executeTimeSlice();
490
+ this.#executeTimeSlice();
437
491
  }
438
492
 
439
493
  }
@@ -459,147 +513,6 @@ const SchedulingPolicy = {
459
513
 
460
514
  ConcurrentExecutor.POLICY = SchedulingPolicy;
461
515
 
462
- /**
463
- *
464
- * @param {Task} task
465
- * @param {number} time in milliseconds
466
- * @param {function} completionCallback
467
- * @param {function} failureCallback
468
- */
469
- function runTaskForTimeEstimated(task, time, completionCallback, failureCallback) {
470
- let cycle_count = 0;
471
-
472
- /**
473
- *
474
- * @type {function(): TaskSignal}
475
- */
476
- const cycle = task.cycle;
477
-
478
- const startTime = performance.now();
479
-
480
- let signal;
481
-
482
- const cycle_time = task.__executedCpuTime / task.__executedCycleCount;
483
-
484
- const estimated_cycles = clamp(Math.ceil(time / cycle_time), 1, 10000);
485
-
486
- while (cycle_count < estimated_cycles) {
487
- cycle_count++
488
-
489
- signal = cycle();
490
-
491
- if (signal === TaskSignal.Continue) {
492
- continue;
493
- }
494
-
495
- if (signal === TaskSignal.Yield) {
496
- //give up current quanta
497
- break;
498
- }
499
-
500
- if (signal === TaskSignal.EndSuccess) {
501
- break;
502
- } else if (signal === TaskSignal.EndFailure) {
503
- break;
504
- } else {
505
- throw new Error(`Task '${task.name}' produced unknown signal: ` + signal);
506
- }
507
- }
508
-
509
- //We use tiny delta to avoid problems with timer accuracy on some systems that result in 0 measured execution time
510
- const endTime = performance.now() + 0.000001;
511
-
512
- const executionDuration = endTime - startTime;
513
-
514
- task.__executedCpuTime += executionDuration;
515
- task.__executedCycleCount += cycle_count;
516
-
517
- if (signal === TaskSignal.EndSuccess) {
518
- completionCallback(task);
519
- } else if (signal === TaskSignal.EndFailure) {
520
- failureCallback(task, "Task signalled failure");
521
- }
522
-
523
- return executionDuration;
524
- }
525
-
526
- /**
527
- *
528
- * @param {Task} task
529
- * @param {number} time in milliseconds
530
- * @param {function} completionCallback
531
- * @param {function} failureCallback
532
- */
533
- function runTaskForTime2(task, time, completionCallback, failureCallback) {
534
- if (task.__executedCycleCount > 1000) {
535
- return runTaskForTimeEstimated(task, time, completionCallback, failureCallback);
536
- } else {
537
- return runTaskForTimeMonitored(task, time, completionCallback, failureCallback);
538
- }
539
- }
540
-
541
- /**
542
- *
543
- * @param {Task} task
544
- * @param {number} time in milliseconds
545
- * @param {function} completionCallback
546
- * @param {function} failureCallback
547
- */
548
- function runTaskForTimeMonitored(task, time, completionCallback, failureCallback) {
549
- let cycle_count = 0;
550
-
551
- /**
552
- *
553
- * @type {function(): TaskSignal}
554
- */
555
- const cycle = task.cycle;
556
-
557
- const startTime = performance.now();
558
-
559
- const endTime = startTime + time;
560
-
561
- //We use tiny delta to avoid problems with timer accuracy on some systems that result in 0 measured execution time
562
- let t = startTime + 0.000001;
563
-
564
- let signal;
565
-
566
- while (t < endTime) {
567
- cycle_count++;
568
-
569
- signal = cycle();
570
- t = performance.now();
571
-
572
- if (signal === TaskSignal.Continue) {
573
- continue;
574
- }
575
-
576
- if (signal === TaskSignal.Yield) {
577
- //give up current quanta
578
- break;
579
- }
580
-
581
- if (signal === TaskSignal.EndSuccess) {
582
- break;
583
- } else if (signal === TaskSignal.EndFailure) {
584
- break;
585
- } else {
586
- throw new Error(`Task '${task.name}' produced unknown signal: ` + signal);
587
- }
588
- }
589
-
590
- const executionDuration = t - startTime;
591
-
592
- task.__executedCpuTime += executionDuration;
593
- task.__executedCycleCount += cycle_count;
594
-
595
- if (signal === TaskSignal.EndSuccess) {
596
- completionCallback(task);
597
- } else if (signal === TaskSignal.EndFailure) {
598
- failureCallback(task, "Task signalled failure");
599
- }
600
-
601
- return executionDuration;
602
- }
603
516
 
604
517
  const ResolutionType = {
605
518
  READY: 0,
@@ -1,5 +1,9 @@
1
1
  import {AbstractBlackboard} from "./AbstractBlackboard";
2
2
 
3
3
  export class Blackboard extends AbstractBlackboard {
4
+ toJSON(): { [key: string]: (number | boolean | string) }
4
5
 
6
+ fromJSON(json: any): void
7
+
8
+ static fromJSON(json: any): Blackboard
5
9
  }
@@ -215,6 +215,17 @@ export class Blackboard extends AbstractBlackboard {
215
215
 
216
216
  }
217
217
  }
218
+
219
+ /**
220
+ *
221
+ * @param {any} json
222
+ * @return {Blackboard}
223
+ */
224
+ static fromJSON(json) {
225
+ const bb = new Blackboard();
226
+ bb.fromJSON(json);
227
+ return bb;
228
+ }
218
229
  }
219
230
 
220
231
  Blackboard.typeName = 'Blackboard';
@@ -1,8 +1,8 @@
1
1
  import ParserError from "../../../../core/parser/simple/ParserError.js";
2
- import { readLiteral } from "../../../../core/parser/simple/SimpleParser.js";
3
2
  import { KeyValuePair } from "../../../../core/collection/KeyValuePair.js";
4
3
  import Token from "../../../../core/parser/simple/Token.js";
5
4
  import { TooltipTokenType } from "./TooltipTokenType.js";
5
+ import { readLiteralToken } from "../../../../core/parser/simple/readLiteralToken.js";
6
6
 
7
7
  /**
8
8
  *
@@ -41,7 +41,7 @@ export function readReferenceValueToken(text, cursor, length) {
41
41
  throw new ParserError(i, `Input underflow while reading reference value, value so far='${text.substring(valueStart, length)}'`, text);
42
42
  }
43
43
 
44
- const literal = readLiteral(text, i, length);
44
+ const literal = readLiteralToken(text, i, length);
45
45
 
46
46
  i = literal.end;
47
47