mirror of
https://codeberg.org/vlw/monkeydo.git
synced 2025-09-13 15:53:40 +02:00
0.1.0
This commit contains the first working concept of Monkeydo.
This commit is contained in:
parent
b6ab338e07
commit
19fa7ed3af
5 changed files with 123 additions and 43 deletions
29
Monkeydo.mjs
29
Monkeydo.mjs
|
@ -1,4 +1,4 @@
|
||||||
import { default as MonkeyWorker } from "./worker/MonkeyWorker.mjs";
|
import { default as MonkeyWorker } from "./worker/TaskManager.mjs";
|
||||||
|
|
||||||
export default class Monkeydo extends MonkeyWorker {
|
export default class Monkeydo extends MonkeyWorker {
|
||||||
constructor(manifest = false) {
|
constructor(manifest = false) {
|
||||||
|
@ -6,22 +6,25 @@ export default class Monkeydo extends MonkeyWorker {
|
||||||
this.monkeydo = {
|
this.monkeydo = {
|
||||||
version: "0.1",
|
version: "0.1",
|
||||||
debugLevel: 0,
|
debugLevel: 0,
|
||||||
|
// Flag if debugging is enabled, regardless of level
|
||||||
get debug() {
|
get debug() {
|
||||||
return this.debugLevel > 0 ? true : false;
|
return this.debugLevel > 0 ? true : false;
|
||||||
},
|
},
|
||||||
set debug(flag = 1) {
|
// Set debug level. Non-verbose debugging by default
|
||||||
this.debugLevel = flag;
|
set debug(level = 1) {
|
||||||
|
this.debugLevel = level;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Object.seal(this.monkeydo);
|
Object.seal(this.monkeydo);
|
||||||
|
|
||||||
|
// Monkeydo manifest parsed with load()
|
||||||
this.manifest = {
|
this.manifest = {
|
||||||
header: null,
|
header: null,
|
||||||
body: null
|
body: null
|
||||||
};
|
};
|
||||||
|
|
||||||
if(!window.Worker) {
|
if(!window.Worker) {
|
||||||
this.except("JavaScript Workers aren't supported by your browser");
|
throw new Error("JavaScript Workers aren't supported by your browser");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(manifest) {
|
if(manifest) {
|
||||||
|
@ -29,17 +32,17 @@ export default class Monkeydo extends MonkeyWorker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug(attachment = "ATTACHMENT_EMPTY") {
|
debug(attachment = "DEBUG_EMPTY") {
|
||||||
if(this.monkeydo.debug) {
|
if(this.monkeydo.debug) {
|
||||||
console.warn("-- Monkeydo debug -->",attachment);
|
console.warn("-- Monkeydo debug -->",attachment);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load a Monkeydo manifest from JSON via string or URL
|
||||||
async load(manifest) {
|
async load(manifest) {
|
||||||
const errorPrefix = "MANIFEST_IMPORT_FAILED: ";
|
const errorPrefix = "MANIFEST_IMPORT_FAILED: ";
|
||||||
let data;
|
let data;
|
||||||
// Monkeydo can only load a JSON string or URL to a JSON file
|
|
||||||
if(typeof manifest !== "string") {
|
if(typeof manifest !== "string") {
|
||||||
this.debug(manifest);
|
this.debug(manifest);
|
||||||
throw new TypeError(errorPrefix + "Expected JSON or URL");
|
throw new TypeError(errorPrefix + "Expected JSON or URL");
|
||||||
|
@ -70,6 +73,7 @@ export default class Monkeydo extends MonkeyWorker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure the parsed JSON is a valid Monkeydo manifest
|
||||||
if(!data.hasOwnProperty("header") || !data.hasOwnProperty("body")) {
|
if(!data.hasOwnProperty("header") || !data.hasOwnProperty("body")) {
|
||||||
this.debug(data);
|
this.debug(data);
|
||||||
throw new Error(errorPrefix + "Expected 'header' and 'body' properties in object");
|
throw new Error(errorPrefix + "Expected 'header' and 'body' properties in object");
|
||||||
|
@ -80,12 +84,21 @@ export default class Monkeydo extends MonkeyWorker {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
do() {
|
// Execute tasks from Monkeydo manifest
|
||||||
|
async do() {
|
||||||
const errorPrefix = "DO_FAILED: ";
|
const errorPrefix = "DO_FAILED: ";
|
||||||
|
// Abort if the manifest object doesn't contain any header data
|
||||||
if(!this.manifest.header) {
|
if(!this.manifest.header) {
|
||||||
this.debug(this.manifest.header);
|
this.debug(this.manifest.header);
|
||||||
throw new Error(errorPrefix + `Expected header object from contructed property`);
|
throw new Error(errorPrefix + `Expected header object from contructed property`);
|
||||||
}
|
}
|
||||||
this.giveManifest();
|
|
||||||
|
// Hand over the loaded manifest to the MonkeyWorker task manager
|
||||||
|
const monkey = this.giveManifest();
|
||||||
|
monkey.then(() => this.play())
|
||||||
|
.catch(error => {
|
||||||
|
this.debug(error);
|
||||||
|
throw new Error(errorPrefix + "Failed to post manifest to worker thread");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
64
worker/Monkey.js
Normal file
64
worker/Monkey.js
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
// Task scheduler and iterator of Monkeydo manifests
|
||||||
|
|
||||||
|
class Monkey {
|
||||||
|
constructor(manifest) {
|
||||||
|
this.data = manifest.body;
|
||||||
|
this.dataLength = this.data.length - 1;
|
||||||
|
|
||||||
|
this.i = 0;
|
||||||
|
this.queue = {
|
||||||
|
task: null,
|
||||||
|
next: null
|
||||||
|
}
|
||||||
|
Object.seal(this.queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
run(data) {
|
||||||
|
this.i++;
|
||||||
|
postMessage(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
queueNext() {
|
||||||
|
const data = this.data[this.i];
|
||||||
|
this.queue.task = setTimeout(() => this.run(data.do),data.wait);
|
||||||
|
|
||||||
|
// Schedule next task if it's not the last
|
||||||
|
if(this.i >= this.dataLength) {
|
||||||
|
this.i = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.queue.next = setTimeout(() => this.queueNext(),data.wait);
|
||||||
|
}
|
||||||
|
|
||||||
|
interrupt() {
|
||||||
|
clearTimeout(this.queue.task);
|
||||||
|
clearTimeout(this.queue.next);
|
||||||
|
this.queue.task = null;
|
||||||
|
this.queue.next = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global event handler for this worker
|
||||||
|
onmessage = (message) => {
|
||||||
|
const type = message.data[0] ? message.data[0] : null;
|
||||||
|
const data = message.data[1];
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case "GIVE_MANIFEST":
|
||||||
|
try {
|
||||||
|
this.monkey = new Monkey(data);
|
||||||
|
postMessage("OK");
|
||||||
|
}
|
||||||
|
catch(error) {
|
||||||
|
postMessage(["MANIFEST_ERROR",error]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "PLAYING":
|
||||||
|
this.monkey.queueNext();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: return; // No op
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,30 +0,0 @@
|
||||||
// Spawn a dedicated worker for scheduling events from manifest
|
|
||||||
|
|
||||||
export default class MonkeyWorker {
|
|
||||||
constructor() {
|
|
||||||
// Get location of this file
|
|
||||||
this.ready = false;
|
|
||||||
let location = new URL(import.meta.url);
|
|
||||||
location = location.pathname.replace("MonkeyWorker.mjs","");
|
|
||||||
|
|
||||||
// Spawn worker from file relative to this file
|
|
||||||
this.worker = new Worker(location + "Sequencer.js");
|
|
||||||
this.worker.addEventListener("message",message => this.message(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
play() {
|
|
||||||
this.worker.postMessage(["PLAYSTATE",true]);
|
|
||||||
}
|
|
||||||
|
|
||||||
pause() {
|
|
||||||
this.worker.postMessage(["PLAYSTATE",false]);
|
|
||||||
}
|
|
||||||
|
|
||||||
giveManifest() {
|
|
||||||
this.worker.postMessage(["GIVE_MANIFEST",this.manifest]);
|
|
||||||
}
|
|
||||||
|
|
||||||
message(message) {
|
|
||||||
console.log(message);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
postMessage("MONKEDO_THREAD_SPAWNED");
|
|
||||||
|
|
||||||
onmessage = (message) => {
|
|
||||||
console.log("Message received",message);
|
|
||||||
}
|
|
38
worker/TaskManager.mjs
Normal file
38
worker/TaskManager.mjs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// Task manager for Monkeydo dedicated workers
|
||||||
|
|
||||||
|
export default class TaskManager {
|
||||||
|
constructor() {
|
||||||
|
// Get path of this file
|
||||||
|
this.ready = false;
|
||||||
|
let location = new URL(import.meta.url);
|
||||||
|
location = location.pathname.replace("TaskManager.mjs",""); // Get parent directory
|
||||||
|
|
||||||
|
// Spawn a dedicated worker for scheduling events from manifest
|
||||||
|
this.worker = new Worker(location + "Monkey.js");
|
||||||
|
}
|
||||||
|
|
||||||
|
play() {
|
||||||
|
this.worker.postMessage(["PLAYING",true]);
|
||||||
|
this.worker.addEventListener("message",message => eval(message.data));
|
||||||
|
}
|
||||||
|
|
||||||
|
pause() {
|
||||||
|
this.worker.postMessage(["PLAYING",false]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass manifest to worker and await response
|
||||||
|
async giveManifest() {
|
||||||
|
this.worker.postMessage(["GIVE_MANIFEST",this.manifest]);
|
||||||
|
|
||||||
|
// Wait for the worker to install the manifest
|
||||||
|
const ack = await new Promise((resolve,reject) => {
|
||||||
|
this.worker.addEventListener("message",message => {
|
||||||
|
if(message.data !== "OK") {
|
||||||
|
reject(message.data);
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return ack;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue