diff --git a/app/lib/arduino/index.js.map b/app/lib/arduino/index.js.map new file mode 100644 index 0000000..8e9c98c --- /dev/null +++ b/app/lib/arduino/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/arduino/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/lib/arduino/Readme.md b/lib/arduino/Readme.md new file mode 100644 index 0000000..498f041 --- /dev/null +++ b/lib/arduino/Readme.md @@ -0,0 +1,82 @@ +## Functions + +
Promise
Pause the process for X milliseconds in async/await functions
+Promise
Send a command to an Arduino using async/await
+Promise
Send a string to an Arduino using async/await
+Promise
Connect to an Arduino using async/await
+Promise
Close a connection to an Arduino using async/await
+Promise
+Pause the process for X milliseconds in async/await functions
+
+**Kind**: global function
+**Returns**: Promise
- Resolves after wait
+
+| Param | Type | Description |
+| --- | --- | --- |
+| ms | integer
| milliseconds |
+
+
+
+## send(device, cmd) ⇒ Promise
+Send a command to an Arduino using async/await
+
+**Kind**: global function
+**Returns**: Promise
- Resolves after sending
+
+| Param | Type | Description |
+| --- | --- | --- |
+| device | string
| Arduino identifier |
+| cmd | string
| Single character command to send |
+
+
+
+## write(device, str) ⇒ Promise
+Send a string to an Arduino using async/await
+
+**Kind**: global function
+**Returns**: Promise
- Resolves after sending
+
+| Param | Type | Description |
+| --- | --- | --- |
+| device | string
| Arduino identifier |
+| str | string
| String to send |
+
+
+
+## open(device) ⇒ Promise
+Connect to an Arduino using async/await
+
+**Kind**: global function
+**Returns**: Promise
- Resolves after opening
+
+| Param | Type | Description |
+| --- | --- | --- |
+| device | string
| Arduino identifier |
+
+
+
+## close(device) ⇒ Promise
+Close a connection to an Arduino using async/await
+
+**Kind**: global function
+**Returns**: Promise
- Resolves after closing
+
+| Param | Type | Description |
+| --- | --- | --- |
+| device | string
| Arduino identifier |
+
diff --git a/lib/arduino/index.js b/lib/arduino/index.js
new file mode 100644
index 0000000..992bebe
--- /dev/null
+++ b/lib/arduino/index.js
@@ -0,0 +1,372 @@
+'use strict';
+const SerialPort = require('serialport');
+const Readline = SerialPort.parsers.Readline;
+const exec = require('child_process').exec;
+const parser = new Readline('');
+const newlineRe = new RegExp('\n', 'g');
+const returnRe = new RegExp('\r', 'g');
+let eventEmitter;
+let cfg;
+/**
+ * Pause the process for X milliseconds in async/await functions
+ *
+ * @param {integer} ms milliseconds
+ *
+ * @returns {Promise} Resolves after wait
+ **/
+async function delay(ms) {
+ return new Promise(resolve => {
+ return setTimeout(resolve, ms);
+ });
+}
+/**
+ * Send a command to an Arduino using async/await
+ *
+ * @param {string} device Arduino identifier
+ * @param {string} cmd Single character command to send
+ *
+ * @returns {Promise} Resolves after sending
+ **/
+async function send(device, cmd) {
+ return new Promise((resolve, reject) => {
+ arduino.queue[cmd] = (ms) => {
+ return resolve(ms);
+ };
+ return arduino.serial[device].write(cmd, (err, results) => {
+ if (err) {
+ //console.error(err)
+ return reject(err);
+ }
+ //
+ });
+ });
+}
+/**
+ * Send a string to an Arduino using async/await
+ *
+ * @param {string} device Arduino identifier
+ * @param {string} str String to send
+ *
+ * @returns {Promise} Resolves after sending
+ **/
+async function write(device, str) {
+ return new Promise((resolve, reject) => {
+ arduino.serial[device].write(str, function (err, results) {
+ if (err) {
+ return reject(err);
+ }
+ //console.log('sent: ' + str)
+ return resolve(results);
+ });
+ });
+}
+/**
+ * Connect to an Arduino using async/await
+ *
+ * @param {string} device Arduino identifier
+ *
+ * @returns {Promise} Resolves after opening
+ **/
+async function openArduino(device) {
+ return new Promise((resolve, reject) => {
+ return arduino.serial[device].open(error => {
+ if (error) {
+ return reject(error);
+ }
+ return resolve(true);
+ });
+ });
+}
+/**
+ * Close a connection to an Arduino using async/await
+ *
+ * @param {string} device Arduino identifier
+ *
+ * @returns {Promise} Resolves after closing
+ **/
+async function closeArduino(device) {
+ return new Promise((resolve, reject) => {
+ return arduino.serial[device].close((err) => {
+ if (err) {
+ return reject(err);
+ }
+ return resolve(true);
+ });
+ });
+}
+/******
+ Arduino handlers
+*******/
+const arduino = {
+ path: {},
+ known: [
+ '/dev/tty.usbmodem1a161',
+ '/dev/tty.usbserial-A800f8dk',
+ '/dev/tty.usbserial-A900cebm',
+ '/dev/tty.usbmodem1a131',
+ '/dev/tty.usbserial-a900f6de',
+ '/dev/tty.usbmodem1a141',
+ '/dev/ttyACM0',
+ 'COM3'
+ ],
+ alias: {},
+ serial: {
+ connect: {},
+ projector: {},
+ camera: {},
+ light: {}
+ },
+ baud: 57600,
+ queue: {},
+ timer: 0,
+ lock: false,
+ locks: {}
+};
+arduino.enumerate = async function () {
+ return new Promise((resolve, reject) => {
+ return SerialPort.list((err, ports) => {
+ let matches = [];
+ if (err) {
+ return reject(err);
+ }
+ ports.forEach(port => {
+ if (arduino.known.indexOf(port.comName) !== -1) {
+ matches.push(port.comName);
+ }
+ else if ((port.manufacturer + '').toLowerCase().indexOf('arduino') !== -1) {
+ matches.push(port.comName);
+ }
+ else if ((port.comName + '').toLowerCase().indexOf('usbserial') !== -1) {
+ matches.push(port.comName);
+ }
+ else if ((port.comName + '').toLowerCase().indexOf('usbmodem') !== -1) {
+ matches.push(port.comName);
+ }
+ });
+ if (matches.length === 0) {
+ return reject('No USB devices found');
+ }
+ else if (matches.length > 0) {
+ return resolve(matches);
+ }
+ });
+ });
+};
+//commands which respond to a sent char
+arduino.send = async function (serial, cmd, res) {
+ const device = arduino.alias[serial];
+ let results;
+ if (arduino.locks[serial]) {
+ return false;
+ }
+ arduino.locks[serial] = true;
+ await delay(cfg.arduino.serialDelay);
+ try {
+ results = await send(device, cmd);
+ }
+ catch (e) {
+ return console.error(e);
+ }
+ arduino.locks[serial] = false;
+ arduino.timer = new Date().getTime();
+ return await eventEmitter.emit('arduino_send', cmd);
+};
+//send strings, after char triggers firmware to accept
+arduino.string = async function (serial, str) {
+ const device = arduino.alias[serial];
+ let writeSuccess;
+ await delay(cfg.arduino.serialDelay);
+ if (typeof arduino.serial[device].fake !== 'undefined'
+ && arduino.serial[device].fake) {
+ return arduino.serial[device].string(str);
+ }
+ else {
+ try {
+ writeSuccess = await write(device, str);
+ }
+ catch (e) {
+ return console.error(e);
+ }
+ return writeSuccess;
+ }
+};
+//respond with same char over serial when done
+arduino.end = async function (serial, data) {
+ const end = new Date().getTime();
+ const ms = end - arduino.timer;
+ let complete;
+ if (arduino.queue[data] !== undefined) {
+ arduino.locks[serial] = false;
+ //console.log('Command ' + data + ' took ' + ms + 'ms');
+ complete = arduino.queue[data](ms); //execute callback
+ eventEmitter.emit('arduino_end', data);
+ delete arduino.queue[data];
+ }
+ else {
+ //console.log('Received stray "' + data + '"'); //silent to user
+ }
+ return complete;
+};
+arduino.alias = function (serial, device) {
+ console.log(`Making "${serial}" an alias of ${device}`);
+ arduino.alias[serial] = device;
+};
+arduino.connect = async function (serial, device, confirm) {
+ return new Promise(async (resolve, reject) => {
+ let connectSuccess;
+ arduino.path[serial] = device;
+ arduino.alias[serial] = device;
+ arduino.serial[device] = new SerialPort(arduino.path[serial], {
+ autoOpen: false,
+ baudRate: cfg.arduino.baud,
+ parser: parser
+ });
+ arduino.locks[device] = false;
+ try {
+ connectSuccess = await openArduino(device);
+ }
+ catch (e) {
+ console.error('failed to open: ' + e);
+ return reject(e);
+ }
+ console.log(`Opened connection with ${arduino.path[serial]} as ${serial}`);
+ if (!confirm) {
+ arduino.serial[device].on('data', async (data) => {
+ let d = data.toString('utf8');
+ d = d.replace(newlineRe, '').replace(returnRe, '');
+ return await arduino.end(serial, d);
+ });
+ }
+ else {
+ arduino.serial[device].on('data', async (data) => {
+ let d = data.toString('utf8');
+ d = d.replace(newlineRe, '').replace(returnRe, '');
+ return await arduino.confirmEnd(d);
+ });
+ }
+ return resolve(arduino.path[serial]);
+ });
+};
+arduino.confirmExec = {};
+arduino.confirmEnd = function (data) {
+ //console.dir(data)
+ if (data === cfg.arduino.cmd.connect
+ || data === cfg.arduino.cmd.proj_identifier
+ || data === cfg.arduino.cmd.cam_identifier
+ || data === cfg.arduino.cmd.light_identifier
+ || data === cfg.arduino.cmd.proj_light_identifier
+ || data === cfg.arduino.cmd.proj_cam_light_identifier
+ || data === cfg.arduino.cmd.proj_cam_identifier) {
+ arduino.confirmExec(null, data);
+ arduino.confirmExec = {};
+ }
+};
+arduino.verify = async function () {
+ return new Promise(async (resolve, reject) => {
+ const device = arduino.alias['connect'];
+ let writeSuccess;
+ arduino.confirmExec = function (err, data) {
+ if (data === cfg.arduino.cmd.connect) {
+ return resolve(true);
+ }
+ else {
+ return reject('Wrong data returned');
+ }
+ };
+ await delay(cfg.arduino.serialDelay);
+ try {
+ writeSuccess = await send(device, cfg.arduino.cmd.connect);
+ }
+ catch (e) {
+ return reject(e);
+ }
+ return resolve(writeSuccess);
+ });
+};
+arduino.distinguish = async function () {
+ return new Promise(async (resolve, reject) => {
+ const device = arduino.alias['connect'];
+ let writeSuccess;
+ let type;
+ arduino.confirmExec = function (err, data) {
+ if (data === cfg.arduino.cmd.proj_identifier) {
+ type = 'projector';
+ }
+ else if (data === cfg.arduino.cmd.cam_identifier) {
+ type = 'camera';
+ }
+ else if (data === cfg.arduino.cmd.light_identifier) {
+ type = 'light';
+ }
+ else if (data === cfg.arduino.cmd.proj_light_identifier) {
+ type = 'projector,light';
+ }
+ else if (data === cfg.arduino.cmd.proj_cam_light_identifier) {
+ type = 'projector,camera,light';
+ }
+ else if (data === cfg.arduino.cmd.proj_cam_identifier) {
+ type = 'projector,camera';
+ }
+ else if (data === cfg.ardino.cmd.proj_second_identifier) {
+ type = 'projector_second';
+ }
+ return resolve(type);
+ };
+ await delay(cfg.arduino.serialDelay);
+ try {
+ writeSuccess = await send(device, cfg.arduino.cmd.mcopy_identifier);
+ }
+ catch (e) {
+ console.error(e);
+ return reject(e);
+ }
+ });
+};
+arduino.close = async function (callback) {
+ const device = arduino.alias['connect'];
+ let closeSuccess;
+ try {
+ closeSuccess = await closeArduino(device);
+ }
+ catch (e) {
+ return console.error(e);
+ }
+ return closeSuccess;
+};
+arduino.fakeConnect = async function (serial) {
+ //console.log('Connecting to fake arduino...');
+ const device = '/dev/fake';
+ arduino.alias[serial] = device;
+ arduino.serial[device] = {
+ write: function (cmd, cb) {
+ const t = {
+ c: cfg.arduino.cam.time + cfg.arduino.cam.delay,
+ p: cfg.arduino.proj.time + cfg.arduino.proj.delay
+ };
+ let timeout = t[cmd];
+ let end;
+ if (typeof timeout === 'undefined')
+ timeout = 10;
+ arduino.timer = +new Date();
+ setTimeout(() => {
+ arduino.end(serial, cmd);
+ return cb();
+ }, timeout);
+ },
+ string: async function (str) {
+ //do nothing
+ return true;
+ },
+ fake: true
+ };
+ //console.log('Connected to fake arduino! Not real! Doesn\'t exist!');
+ return true;
+};
+if (typeof module !== 'undefined' && module.parent) {
+ module.exports = function (c, ee) {
+ eventEmitter = ee;
+ cfg = c;
+ return arduino;
+ };
+}
+//# sourceMappingURL=index.js.map
\ No newline at end of file
diff --git a/lib/arduino/index.js.map b/lib/arduino/index.js.map
new file mode 100644
index 0000000..8e9c98c
--- /dev/null
+++ b/lib/arduino/index.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/arduino/index.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/lib/arduino/package.json b/lib/arduino/package.json
new file mode 100644
index 0000000..43708c1
--- /dev/null
+++ b/lib/arduino/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "arduino",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "",
+ "license": "ISC"
+}
diff --git a/lib/mscript/Readme.md b/lib/mscript/Readme.md
new file mode 100644
index 0000000..3112f09
--- /dev/null
+++ b/lib/mscript/Readme.md
@@ -0,0 +1,186 @@
+
+
+## lib/mscript
+
+* [lib/mscript](#module_lib/mscript)
+ * [~Mscript](#module_lib/mscript..Mscript)
+ * [.clear()](#module_lib/mscript..Mscript+clear)
+ * [.interpret()](#module_lib/mscript..Mscript+interpret)
+ * [.basic_cmd()](#module_lib/mscript..Mscript+basic_cmd)
+ * [.new_loop()](#module_lib/mscript..Mscript+new_loop)
+ * [.end_loop()](#module_lib/mscript..Mscript+end_loop)
+ * [.move_cam()](#module_lib/mscript..Mscript+move_cam)
+ * [.move_proj()](#module_lib/mscript..Mscript+move_proj)
+ * [.set_state()](#module_lib/mscript..Mscript+set_state)
+ * [.last_loop()](#module_lib/mscript..Mscript+last_loop)
+ * [.parent_loop()](#module_lib/mscript..Mscript+parent_loop)
+ * [.loop_count()](#module_lib/mscript..Mscript+loop_count)
+ * [.fade()](#module_lib/mscript..Mscript+fade)
+ * [.fade_count()](#module_lib/mscript..Mscript+fade_count)
+ * [.fade_start()](#module_lib/mscript..Mscript+fade_start)
+ * [.fade_end()](#module_lib/mscript..Mscript+fade_end)
+ * [.update()](#module_lib/mscript..Mscript+update)
+ * [.str_to_arr()](#module_lib/mscript..Mscript+str_to_arr)
+ * [.light_to_arr()](#module_lib/mscript..Mscript+light_to_arr)
+ * [.light_state()](#module_lib/mscript..Mscript+light_state)
+ * [.fail()](#module_lib/mscript..Mscript+fail)
+ * [~startsWith()](#module_lib/mscript..startsWith)
+
+
+
+### lib/mscript~Mscript
+class Mscript
+
+**Kind**: inner class of [lib/mscript
](#module_lib/mscript)
+
+* [~Mscript](#module_lib/mscript..Mscript)
+ * [.clear()](#module_lib/mscript..Mscript+clear)
+ * [.interpret()](#module_lib/mscript..Mscript+interpret)
+ * [.basic_cmd()](#module_lib/mscript..Mscript+basic_cmd)
+ * [.new_loop()](#module_lib/mscript..Mscript+new_loop)
+ * [.end_loop()](#module_lib/mscript..Mscript+end_loop)
+ * [.move_cam()](#module_lib/mscript..Mscript+move_cam)
+ * [.move_proj()](#module_lib/mscript..Mscript+move_proj)
+ * [.set_state()](#module_lib/mscript..Mscript+set_state)
+ * [.last_loop()](#module_lib/mscript..Mscript+last_loop)
+ * [.parent_loop()](#module_lib/mscript..Mscript+parent_loop)
+ * [.loop_count()](#module_lib/mscript..Mscript+loop_count)
+ * [.fade()](#module_lib/mscript..Mscript+fade)
+ * [.fade_count()](#module_lib/mscript..Mscript+fade_count)
+ * [.fade_start()](#module_lib/mscript..Mscript+fade_start)
+ * [.fade_end()](#module_lib/mscript..Mscript+fade_end)
+ * [.update()](#module_lib/mscript..Mscript+update)
+ * [.str_to_arr()](#module_lib/mscript..Mscript+str_to_arr)
+ * [.light_to_arr()](#module_lib/mscript..Mscript+light_to_arr)
+ * [.light_state()](#module_lib/mscript..Mscript+light_state)
+ * [.fail()](#module_lib/mscript..Mscript+fail)
+
+
+
+#### mscript.clear()
+Clear the state of the script
+
+**Kind**: instance method of [Mscript
](#module_lib/mscript..Mscript)
+
+
+#### mscript.interpret()
+Main function, accepts multi-line string, parses into lines
+and interprets the instructions from the text. Returns an array
+of steps to be fed into the mcopy.
+
+**Kind**: instance method of [Mscript
](#module_lib/mscript..Mscript)
+
+
+#### mscript.basic\_cmd()
+Apply a basic two character command
+
+**Kind**: instance method of [Mscript
](#module_lib/mscript..Mscript)
+
+
+#### mscript.new\_loop()
+Start a new loop
+
+**Kind**: instance method of [Mscript
](#module_lib/mscript..Mscript)
+
+
+#### mscript.end\_loop()
+Close the most recent loop
+
+**Kind**: instance method of [Mscript
](#module_lib/mscript..Mscript)
+
+
+#### mscript.move\_cam()
+Move camera to explicitly-defined frame
+
+**Kind**: instance method of [Mscript
](#module_lib/mscript..Mscript)
+
+
+#### mscript.move\_proj()
+Move projector to explicitly-defined frame
+
+**Kind**: instance method of [Mscript
](#module_lib/mscript..Mscript)
+
+
+#### mscript.set\_state()
+Set the state of either the cam or projector
+
+**Kind**: instance method of [Mscript
](#module_lib/mscript..Mscript)
+
+
+#### mscript.last\_loop()
+Return the last loop
+
+**Kind**: instance method of [Mscript
](#module_lib/mscript..Mscript)
+
+
+#### mscript.parent\_loop()
+Return the second-last loop
+
+**Kind**: instance method of [Mscript
](#module_lib/mscript..Mscript)
+
+
+#### mscript.loop\_count()
+Extract the loop count integer from a LOOP cmd
+
+**Kind**: instance method of [Mscript
](#module_lib/mscript..Mscript)
+
+
+#### mscript.fade()
+Execute a fade of frame length, from color to another color
+
+**Kind**: instance method of [Mscript
](#module_lib/mscript..Mscript)
+
+
+#### mscript.fade\_count()
+Extract the fade length integer from a FADE cmd
+
+**Kind**: instance method of [Mscript
](#module_lib/mscript..Mscript)
+
+
+#### mscript.fade\_start()
+Extract the start color from a string
+
+**Kind**: instance method of [Mscript
](#module_lib/mscript..Mscript)
+
+
+#### mscript.fade\_end()
+Extract the end color from a string
+
+**Kind**: instance method of [Mscript
](#module_lib/mscript..Mscript)
+
+
+#### mscript.update()
+Increase the state of a specific object, such as the camera/projector,
+by the value defined in val
+
+**Kind**: instance method of [Mscript
](#module_lib/mscript..Mscript)
+
+
+#### mscript.str\_to\_arr()
+Split string on command, extract any integers from string
+
+**Kind**: instance method of [Mscript
](#module_lib/mscript..Mscript)
+
+
+#### mscript.light\_to\_arr()
+Split a string on a command to extract data for light array
+
+**Kind**: instance method of [Mscript
](#module_lib/mscript..Mscript)
+
+
+#### mscript.light\_state()
+Split a string to extract an rgb color value
+
+**Kind**: instance method of [Mscript
](#module_lib/mscript..Mscript)
+
+
+#### mscript.fail()
+Throw an error with specific message
+
+**Kind**: instance method of [Mscript
](#module_lib/mscript..Mscript)
+
+
+### lib/mscript~startsWith()
+startswith function from lodash, do not want the entire lib for this
+
+**Kind**: inner method of [lib/mscript
](#module_lib/mscript)
diff --git a/lib/mscript/TODO.md b/lib/mscript/TODO.md
new file mode 100644
index 0000000..b0f824f
--- /dev/null
+++ b/lib/mscript/TODO.md
@@ -0,0 +1,13 @@
+# TODO - mscript
+
+* Add variables and simple evaluation
+* Add "Light" feature
+
+Bash-like variables?
+Similar to LESS/SASS?
+Makes a tokenization easier
+
+@ is better than $
+
+RangeError: Invalid array length
+ at Mscript.str_to_arr (./mcopy/app/lib/mscript/index.js:474:9)
diff --git a/lib/mscript/index.js b/lib/mscript/index.js
new file mode 100644
index 0000000..e2fac5e
--- /dev/null
+++ b/lib/mscript/index.js
@@ -0,0 +1,545 @@
+'use strict';
+
+/** @module lib/mscript */
+
+const BLACK = '0,0,0';
+const WHITE = '255,255,255';
+const CMD = [
+ 'CF',
+ 'PF',
+ 'BF',
+ 'CB',
+ 'PB',
+ 'BB'
+];
+const ALTS = {
+ 'CF' : ['CAMERA FORWARD', 'CAM FORWARD'],
+ 'PF' : ['PROJECTOR FORWARD', 'PROJ FORWARD'],
+ 'BF' : ['BLACK FORWARD', 'BLACK', 'BLANK FORWARD', 'BLANK'],
+ 'CB' : ['CAMERA BACKWARD', 'CAM BACKWARD', 'CAMERA BACK', 'CAM BACK'],
+ 'PB' : ['PROJECTOR FORWARD', 'PROJ FORWARD', 'PROJECTOR BACK', 'PROJ BACK'],
+ 'BB' : ['BLACK BACKWARD', 'BLACK BACK', 'BLANK BACK'],
+ 'L ' : ['LIGHT', 'COLOR', 'LAMP'],
+ 'F ' : ['FADE']
+};
+
+/** helper functions */
+
+/** startswith function from lodash, do not want the entire lib for this */
+function startsWith(string, target, position) {
+ const { length } = string;
+ position = position == null ? 0 : position;
+ if (position < 0) {
+ position = 0;
+ } else if (position > length) {
+ position = length;
+ }
+ target = `${target}`;
+ return string.slice(position, position + target.length) == target;
+}
+
+/** class Mscript */
+class Mscript {
+ constructor () {
+ this.output = {};
+ }
+ /**
+ * Clear the state of the script
+ */
+ clear () {
+ this.lines = [];
+
+ this.cam = 0;
+ this.proj = 0;
+ this.color = '';
+ this.loops = [];
+ this.rec = -1;
+
+ this.two = '';
+ this.arr = [];
+ this.light = [];
+ this.target = 0; //move to target using CAM # or PROJ #
+ this.dist = 0;
+
+ this.variables = {};
+
+ this.output = {};
+ }
+ /**
+ * Main function, accepts multi-line string, parses into lines
+ * and interprets the instructions from the text. Returns an array
+ * of steps to be fed into the mcopy.
+ */
+ interpret (text, callback) {
+ this.clear()
+
+ if (typeof text === 'undefined') {
+ return this.fail('No input');
+ }
+
+ //split string into lines, each containing a command
+ this.lines = text.split('\n');
+
+ this.lines = this.lines.map(line => {
+ line = line.replace(/\t+/g, ''); //strip tabs
+ line = line.trim(); //remove excess whitespace before and after command
+ line = line.toUpperCase();
+ return line;
+ })
+
+ for (let line of this.lines) {
+ this.two = line.substring(0, 2);
+ if (CMD.indexOf(this.two) !== -1) {
+ this.basic_cmd(line);
+ } else if (startsWith(line, '@') || line.indexOf('@') !== -1) {
+ this.variable(line);
+ } else if (startsWith(line, 'LOOP')) {
+ this.new_loop(line);
+ } else if (startsWith(line, 'L ')) {
+ this.light_state(line);
+ } else if (startsWith(line, 'F ')) {
+ this.new_loop(line, true);
+ } else if (startsWith(line, 'END')) {
+ this.end_loop(line);
+ } else if (startsWith(line, 'CAM')) { //directly go to that frame (black?)
+ this.move_cam(line);
+ } else if (startsWith(line, 'PROJ')) { //directly go to that frame
+ this.move_proj(line);
+ } else if (startsWith(line, 'SET')) { //set that state
+ this.set_state(line);
+ } else if (startsWith(line, '#') || startsWith(line, '//')) {
+ //comments
+ //ignore while parsing
+ }
+ }
+
+ this.output.success = true;
+ this.output.arr = this.arr; //all instructions
+ this.output.light = this.light; //all light instructions
+ this.output.cam = this.cam;
+ this.output.proj = this.proj;
+
+ if (typeof callback !== 'undefined') {
+ //should only be invoked by running mscript.tests()
+ callback(this.output);
+ } else {
+ return this.output;
+ }
+ }
+ variable (line) {
+ let parts = line.split('=');
+ let key = parts[0];
+ let value = parts[1];
+ let update = false;
+
+ if (value && value.indexOf('#') !== -1) {
+ value = value.split('#')[0];
+ }
+
+ if (value && value.indexOf('//') !== -1) {
+ value = value.split('//')[0];
+ }
+
+ if (value && value.indexOf('+') !== -1) {
+ if (value)
+ update = true;
+ }
+
+ if (line.indexOf('-') !== -1) {
+
+ update = true;
+ }
+
+ if (line.indexOf(',') === -1) { //if not color string
+ try {
+ value = parseInt(value);
+ } catch (err) {
+ //supress parsing error
+ }
+ }
+ //console.dir(parts)
+ if (!this.variables[key] || update) {
+ this.variables[key] = value;
+ }
+ console.dir(this.variables)
+ }
+ variable_replace(line) {
+
+ }
+ /**
+ * Apply a basic two character command
+ */
+ basic_cmd (line) {
+ if (this.rec !== -1) {
+ //hold generated arr in state loop array
+ this.loops[this.rec].arr
+ .push.apply(this.loops[this.rec].arr,
+ this.str_to_arr(line,
+ this.two));
+ this.loops[this.rec].light
+ .push.apply(this.loops[this.rec].light,
+ this.light_to_arr(line,
+ this.two));
+ } else {
+ this.arr.push.apply(this.arr, this.str_to_arr(line, this.two));
+ this.light.push.apply(this.light, this.light_to_arr(line, this.two))
+ }
+ }
+ /**
+ * Start a new loop
+ */
+ new_loop (line, fade) {
+ this.rec++;
+ this.loops[this.rec] = {
+ arr : [],
+ light : [],
+ cam : 0,
+ proj : 0,
+ cmd : line + ''
+ };
+ if (fade) {
+ this.fade(line);
+ }
+ }
+ /**
+ * Close the most recent loop
+ */
+ end_loop (line) {
+ let light_arr;
+ let start;
+ let end;
+ let len;
+
+ for (let x = 0; x < this.loop_count(this.loops[this.rec].cmd); x++) {
+ light_arr = this.loops[this.rec].light;
+ if (this.loops[this.rec].fade) {
+ start = this.loops[this.rec].start;
+ end = this.loops[this.rec].end;
+ len = this.loops[this.rec].fade_len;
+ light_arr = light_arr.map(l => {
+ return this.fade_rgb(start, end, len, x);
+ })
+ }
+ if (this.rec === 0) {
+ this.arr.push.apply(this.arr, this.loops[this.rec].arr);
+ this.light.push.apply(this.light, light_arr);
+ } else if (this.rec >= 1) {
+ this.loops[this.rec - 1].arr
+ .push.apply(this.loops[this.rec - 1].arr,
+ this.loops[this.rec].arr);
+
+ this.loops[this.rec - 1].light
+ .push.apply(this.loops[this.rec - 1].light,
+ light_arr);
+ }
+ }
+ this.update('END', this.loop_count(this.loops[this.rec].cmd));
+ delete this.loops[this.rec];
+ this.rec--;
+ }
+ /**
+ * Move camera to explicitly-defined frame
+ */
+ move_cam (line) {
+ this.target = parseInt(line.split('CAM ')[1]);
+ if (this.rec !== -1) {
+ if (this.target > this.cam) {
+ this.dist = this.target - this.cam;
+ for (let x = 0; x < this.dist; x++) {
+ this.loops[this.rec].arr.push('BF');
+ this.loops[this.rec].light.push(BLACK);
+ this.update('BF');
+ }
+ } else {
+ this.dist = this.cam - this.target;
+ for (let x = 0; x < this.dist; x++) {
+ this.loops[this.rec].arr.push('BB');
+ this.loops[this.rec].light.push(BLACK);
+ this.update('BB');
+ }
+ }
+ } else {
+ if (target > this.cam) {
+ this.dist = this.target - this.cam;
+ for (let x = 0; x < this.dist; x++) {
+ this.arr.push('BF');
+ this.light.push(BLACK);
+ this.update('BF');
+ }
+ } else {
+ this.dist = this.cam - this.target;
+ for (let x = 0; x < this.dist; x++) {
+ this.arr.push('BB');
+ this.light.push(BLACK);
+ this.update('BB');
+ }
+ }
+ }
+ }
+ /**
+ * Move projector to explicitly-defined frame
+ */
+ move_proj (line) {
+ this.target = parseInt(line.split('PROJ ')[1]);
+ if (this.rec !== -1) {
+ if (this.target > this.proj) {
+ this.dist = this.target - this.proj;
+ for (let x = 0; x < this.dist; x++) {
+ this.loops[this.rec].arr.push('PF');
+ this.loops[this.rec].light.push('');
+ this.update('PF');
+ }
+ } else {
+ this.dist = this.proj - this.target;
+ for (let x = 0; x < this.dist; x++) {
+ this.loops[this.rec].arr.push('PB');
+ this.loops[this.rec].light.push('');
+ this.update('PB');
+ }
+ }
+ } else {
+ if (this.target > this.proj) {
+ this.dist = this.target - this.proj;
+ for (let x = 0; x < this.dist; x++) {
+ this.arr.push('PF');
+ this.light.push('');
+ this.update('PF');
+ }
+ } else {
+ this.dist = this.proj - this.target;
+ for (let x = 0; x < this.dist; x++) {
+ this.arr.push('PB');
+ this.light.push('');
+ this.update('PB');
+ }
+ }
+ }
+ }
+ /**
+ * Set the state of either the cam or projector
+ */
+ set_state (line) {
+ if (startsWith(line, 'SET CAM')) {
+ this.cam = parseInt(line.split('SET CAM')[1]);
+ } else if (startsWith(line, 'SET PROJ')) {
+ this.proj = parseInt(line.split('SET PROJ')[1]);
+ }
+ }
+ /**
+ * Return the last loop
+ */
+ last_loop () {
+ return this.loops[this.loops.length - 1];
+ }
+ /**
+ * Return the second-last loop
+ */
+ parent_loop () {
+ return this.loops[this.loops.length - 2];
+ }
+ /**
+ * Extract the loop count integer from a LOOP cmd
+ */
+ loop_count (str) {
+ return parseInt(str.split(' ')[1]);
+ }
+ /**
+ * Execute a fade of frame length, from color to another color
+ */
+ fade (line) {
+ let len = this.fade_count(line);
+ let start = this.fade_start(line);
+ let end = this.fade_end(line);
+
+ this.loops[this.rec].start = start;
+ this.loops[this.rec].end = end;
+ this.loops[this.rec].fade = true;
+ this.loops[this.rec].fade_count = 0;
+ this.loops[this.rec].fade_len = len;
+ }
+ /**
+ * Extract the fade length integer from a FADE cmd
+ */
+ fade_count (str) {
+ return parseInt(str.split(' ')[1]);
+ }
+ /**
+ * Extract the start color from a string
+ */
+ fade_start (str) {
+ let color = str.split(' ')[2];
+ return this.rgb(color.trim())
+ }
+ /**
+ * Extract the end color from a string
+ */
+ fade_end (str) {
+ let color = str.split(' ')[3];
+ return this.rgb(color.trim())
+ }
+ fade_rgb (start, end, len, x) {
+ let cur = [];
+ let diff;
+ for (let i = 0; i < 3; i++) {
+ if (x === len - 1) {
+ cur[i] = end[i];
+ } else if (start[i] >= end[i]) {
+ diff = start[i] - end[i];
+ cur[i] = start[i] - Math.round((diff / (len - 1)) * x);
+ } else {
+ diff = end[i] - start[i];
+ cur[i] = start[i] + Math.round((diff / (len - 1)) * x);
+ }
+ }
+ return this.rgb_str(cur);
+
+ }
+ rgb (str) {
+ let rgb = str.split(',');
+ return rgb.map( char => {
+ return parseInt(char);
+ })
+ }
+ rgb_str (arr) {
+ return arr.join(',');
+ }
+ /**
+ * Increase the state of a specific object, such as the camera/projector,
+ * by the value defined in val
+ */
+ update (cmd, val = 1) {
+ if (cmd === 'END') {
+ //I don't understand this loop
+ for (let i = 0; i < val; i++) {
+ if (this.rec === 0) {
+ this.cam += this.loops[this.rec].cam;
+ this.proj += this.loops[this.rec].proj;
+ } else if (this.rec >= 1) {
+ this.loops[this.rec - 1].cam += this.loops[this.rec].cam;
+ this.loops[this.rec - 1].proj += this.loops[this.rec].proj;
+ }
+ }
+ } else if (cmd === 'CF') {
+ if (this.rec === -1) {
+ this.cam += val;
+ } else {
+ this.loops[this.rec].cam += val;
+ }
+ } else if (cmd === 'CB') {
+ if (this.rec === -1) {
+ this.cam -= val;
+ } else {
+ this.loops[this.rec].cam--;
+ }
+ } else if (cmd === 'PF') {
+ if (this.rec === -1) {
+ this.proj += val;
+ } else {
+ this.loops[this.rec].proj += val;
+ }
+ } else if (cmd === 'PB') {
+ if (this.rec === -1) {
+ this.proj -= val;
+ } else {
+ this.loops[this.rec].proj--;
+ }
+ } else if (cmd === 'BF') {
+ if (this.rec === -1) {
+ this.cam += val;
+ } else {
+ this.loops[this.rec].cam += val;
+ }
+ } else if (cmd === 'BB') {
+ if (this.rec === -1) {
+ this.cam -= val;
+ } else {
+ this.loops[this.rec].cam -= val;
+ }
+ } else if (cmd === 'L ') {
+
+ }
+ }
+ /**
+ * Split string on command, extract any integers from string
+ */
+ str_to_arr (str, cmd) {
+ const cnt = str.split(cmd);
+ let c = parseInt(cnt[1]);
+ let arr = [];
+ if (cnt[1] === '') {
+ c = 1;
+ } else {
+ c = parseInt(cnt[1]);
+ }
+ arr = new Array(c).fill(cmd);
+ this.update(cmd, c);
+ return arr;
+ }
+ /**
+ * Split a string on a command to extract data for light array
+ */
+ light_to_arr (str, cmd) {
+ const cnt = str.split(cmd);
+ let c = parseInt(cnt[1]);
+ let arr = [];
+ if (cnt[1] === '') {
+ c = 1;
+ } else {
+ c = parseInt(cnt[1]);
+ }
+ for (var i = 0; i < c; i++) {
+ if (cmd === 'CF'
+ || cmd === 'CB') {
+ arr.push(this.color);
+ } else if (cmd === 'BF'
+ || cmd === 'BB') {
+ arr.push(BLACK);
+ } else {
+ arr.push('');
+ }
+ }
+ return arr;
+ }
+ /**
+ * Split a string to extract an rgb color value
+ */
+ light_state (str) {
+ //add parsers for other color spaces
+ const color = str.replace('L ', '').trim();
+ this.color = color;
+ }
+
+ /**
+ * Throw an error with specific message
+ */
+ fail (msg) {
+ throw new Error(msg);
+ }
+}
+
+module.exports = Mscript;
+
+
+/*
+
+CAM # - go to camera frame #
+PROJ # - go to projector frame #
+
+SET CAM # - sets camera count to #
+SET PROJ # - sets projector count to #
+
+LOOP # - begin loop, can nest recursively, # times
+END LOOP - (or END) closes loop
+
+L #RGB - sets light to rgb value
+
+FADE 24 0,0,0 255,255,255
+
+CF - Camera forwards
+PF - Projector forwards
+BF - Black forwards
+CB - Camera backwards
+PB - Projector backwards
+BB - Black backwards
+
+*/
\ No newline at end of file
diff --git a/lib/mscript/index.old.js b/lib/mscript/index.old.js
new file mode 100644
index 0000000..65e8d17
--- /dev/null
+++ b/lib/mscript/index.old.js
@@ -0,0 +1,476 @@
+'use strict';
+
+/** @module lib/mscript */
+
+let fs;
+let input;
+
+/** object mscript */
+const mscript = {};
+
+/**
+ * Check for the presence of specific arguments in process
+ * argv
+ *
+ * @param {string} shrt Short version of argument or flag
+ * @param {string} lng Long version of argument or flag
+ *
+ * @return {boolean} Is flag present
+ */
+mscript.arg = function arg (shrt, lng) {
+ if (process.argv.indexOf(shrt) !== -1 ||
+ process.argv.indexOf(lng) !== -1) {
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Check for the position of specific arguments in process
+ * argv
+ *
+ * @param {string} shrt Short version of argument or flag
+ * @param {string} lng Long version of argument or flag
+ *
+ * @return {boolean} Position of arg or flag, for locating input
+ */
+mscript.arg_pos = function arg_pos (shrt, lng) {
+ var pos = -1;
+ pos = process.argv.indexOf(shrt);
+ if (pos === -1) {
+ pos = process.argv.indexOf(lng);
+ }
+ return pos;
+};
+
+mscript.black = '0,0,0';
+mscript.cmd = [
+ 'CF',
+ 'PF',
+ 'BF',
+ 'CB',
+ 'PB',
+ 'BB'
+];
+mscript.alts = {
+ 'CF' : ['CAMERA FORWARD', 'CAM FORWARD'],
+ 'PF' : ['PROJECTOR FORWARD', 'PROJ FORWARD'],
+ 'BF' : ['BLACK FORWARD', 'BLACK', 'BLANK FORWARD', 'BLANK'],
+ 'CB' : ['CAMERA BACKWARD', 'CAM BACKWARD', 'CAMERA BACK', 'CAM BACK'],
+ 'PB' : ['PROJECTOR FORWARD', 'PROJ FORWARD', 'PROJECTOR BACK', 'PROJ BACK'],
+ 'BB' : ['BLACK BACKWARD', 'BLACK BACK', 'BLANK BACK'],
+ 'L ' : ['LIGHT', 'COLOR', 'LAMP'],
+ 'F ' : ['FADE']
+};
+
+mscript.state = {};
+
+/**
+ * Clear the state object
+ */
+mscript.state_clear = function state_clear () {
+ mscript.state = {
+ cam : 0,
+ proj : 0,
+ color : '',
+ loops : [],
+ rec : -1
+ };
+};
+/**
+ * This is never used and doesn't do anything?
+ */
+mscript.alts_unique = function alts_unique () {
+ var ids = Object.keys(mscript.alts),
+ all = [];
+ for (var i = 0; i < ids.length; i++) {
+ if (all.indexOf(ids[i]) === -1) {
+ all.push(ids[i]);
+ } else {
+ mscript.fail("Can't compile");
+ }
+ }
+};
+/**
+ *
+ */
+mscript.interpret = function interpret (text, callback) {
+ mscript.state_clear();
+ if (typeof text === 'undefined') {
+ mscript.fail('No input');
+ }
+ var lines = text.split('\n'),
+ two = '',
+ arr = [],
+ light = [],
+ target = 0,
+ dist = 0, //?
+ output = {};
+ for (var i = 0; i < lines.length; i++) {
+ lines[i] = lines[i].replace(/\t+/g, ""); //strip tabs
+ lines[i] = lines[i].trim(); //remove excess whitespace before and after command
+ two = lines[i].substring(0, 2);
+ if (mscript.cmd.indexOf(two) !== -1) {
+
+ if (mscript.state.loops.length > 0) {
+ //hold generated arr in state loop array
+ mscript.state.loops[mscript.state.rec].arr
+ .push.apply(mscript.state.loops[mscript.state.rec].arr,
+ mscript.str_to_arr(lines[i],
+ two));
+ mscript.state.loops[mscript.state.rec].light
+ .push.apply(mscript.state.loops[mscript.state.rec].light,
+ mscript.light_to_arr(lines[i],
+ two));
+ } else {
+ arr.push.apply(arr, mscript.str_to_arr(lines[i], two));
+ light.push.apply(light, mscript.light_to_arr(lines[i], two))
+ }
+
+ } else if (lines[i].substring(0, 4) === 'LOOP') {
+ mscript.state.rec++;
+ mscript.state.loops[mscript.state.rec] = {
+ arr : [],
+ light : [],
+ cam : 0,
+ proj : 0,
+ cmd : lines[i] + ''
+ };
+ } else if (lines[i].substring(0, 2) === 'L ') {
+ mscript.light_state(lines[i]);
+ } else if (lines[i].substring(0, 3) === 'END') {
+ for (var x = 0; x < mscript.loop_count(mscript.state.loops[mscript.state.rec].cmd); x++) {
+ if (mscript.state.rec === 0) {
+ arr.push.apply(arr, mscript.state.loops[mscript.state.rec].arr);
+ light.push.apply(light, mscript.state.loops[mscript.state.rec].light);
+ } else if (mscript.state.rec >= 1) {
+ mscript.state.loops[mscript.state.rec - 1].arr
+ .push.apply(mscript.state.loops[mscript.state.rec - 1].arr,
+ mscript.state.loops[mscript.state.rec].arr);
+ mscript.state.loops[mscript.state.rec - 1].light
+ .push.apply(mscript.state.loops[mscript.state.rec - 1].light,
+ mscript.state.loops[mscript.state.rec].light);
+ }
+ }
+ mscript.state_update('END', mscript.loop_count(mscript.state.loops[mscript.state.rec].cmd));
+ delete mscript.state.loops[mscript.state.rec];
+ mscript.state.rec--;
+ } else if (lines[i].substring(0, 3) === 'CAM') { //directly go to that frame (black?)
+ target = parseInt(lines[i].split('CAM ')[1]);
+ if (mscript.state.loops.length > 0) {
+ if (target > mscript.state.cam) {
+ dist = target - mscript.state.cam;
+ for (var x = 0; x < dist; x++) {
+ mscript.state.loops[mscript.state.rec].arr.push('BF');
+ mscript.state.loops[mscript.state.rec].light.push(mscript.black);
+ mscript.state_update('BF');
+ }
+ } else {
+ dist = mscript.state.cam - target;
+ for (var x = 0; x < dist; x++) {
+ mscript.state.loops[mscript.state.rec].arr.push('BB');
+ mscript.state.loops[mscript.state.rec].light.push(mscript.black);
+ mscript.state_update('BB');
+ }
+ }
+ } else {
+ if (target > mscript.state.cam) {
+ dist = target - mscript.state.cam;
+ for (var x = 0; x < dist; x++) {
+ arr.push('BF');
+ light.push(mscript.black);
+ mscript.state_update('BF');
+ }
+ } else {
+ dist = mscript.state.cam - target;
+ for (var x = 0; x < dist; x++) {
+ arr.push('BB');
+ light.push(mscript.black);
+ mscript.state_update('BB');
+ }
+ }
+ }
+ } else if (lines[i].substring(0, 4) === 'PROJ') { //directly go to that frame
+ target = parseInt(lines[i].split('PROJ ')[1]);
+ if (mscript.state.loops.length > 0) {
+ if (target > mscript.state.proj) {
+ dist = target - mscript.state.proj;
+ for (var x = 0; x < dist; x++) {
+ mscript.state.loops[mscript.state.rec].arr.push('PF');
+ mscript.state.loops[mscript.state.rec].light.push('');
+ mscript.state_update('PF');
+ }
+ } else {
+ dist = mscript.state.proj - target;
+ for (var x = 0; x < dist; x++) {
+ mscript.state.loops[mscript.state.rec].arr.push('PB');
+ mscript.state.loops[mscript.state.rec].light.push('');
+ mscript.state_update('PB');
+ }
+ }
+ } else {
+ if (target > mscript.state.proj) {
+ dist = target - mscript.state.proj;
+ for (var x = 0; x < dist; x++) {
+ arr.push('PF');
+ light.push('');
+ mscript.state_update('PF');
+ }
+ } else {
+ dist = mscript.state.proj - target;
+ for (var x = 0; x < dist; x++) {
+ arr.push('PB');
+ light.push('');
+ mscript.state_update('PB');
+ }
+ }
+ }
+ } else if (lines[i].substring(0, 3) === 'SET') { //set that state
+ if (lines[i].substring(0, 7) === 'SET CAM') {
+ mscript.state.cam = parseInt(lines[i].split('SET CAM')[1]);
+ } else if (lines[i].substring(0, 8) === 'SET PROJ') {
+ mscript.state.proj = parseInt(lines[i].split('SET PROJ')[1]);
+ }
+ } else if (lines[i].substring(0, 1) === '#' || lines[i].substring(0, 2) === '//') {
+ //comments
+ //ignore while parsing
+ }
+ }
+ output.success = true;
+ output.arr = arr;
+ output.light = light;
+ output.cam = mscript.state.cam;
+ output.proj = mscript.state.proj;
+ if (typeof callback !== 'undefined') {
+ //should only be invoked by running mscript.tests()
+ callback(output);
+ } else {
+ return mscript.output(output);
+ }
+};
+/**
+ *
+ */
+mscript.last_loop = function last_loop () {
+ return mscript.state.loops[mscript.state.loops.length - 1];
+};
+/**
+ *
+ */
+mscript.parent_loop = function parent_loop () {
+ return mscript.state.loops[mscript.state.loops.length - 2];
+};
+/**
+ *
+ */
+mscript.state_update = function state_update (cmd, val) {
+ if (cmd === 'END') {
+ for (var i = 0; i < val; i++) {
+ if (mscript.state.rec === 0) {
+ mscript.state.cam += mscript.state.loops[mscript.state.rec].cam;
+ mscript.state.proj += mscript.state.loops[mscript.state.rec].proj;
+ } else if (mscript.state.rec >= 1) {
+ mscript.state.loops[mscript.state.rec - 1].cam += mscript.state.loops[mscript.state.rec].cam;
+ mscript.state.loops[mscript.state.rec - 1].proj += mscript.state.loops[mscript.state.rec].proj;
+ }
+ }
+ } else if (cmd === 'CF') {
+ if (mscript.state.loops.length < 1) {
+ mscript.state.cam++;
+ } else {
+ mscript.state.loops[mscript.state.rec].cam++;
+ }
+ } else if (cmd === 'CB') {
+ if (mscript.state.loops.length < 1) {
+ mscript.state.cam--;
+ } else {
+ mscript.state.loops[mscript.state.rec].cam--;
+ }
+ } else if (cmd === 'PF') {
+ if (mscript.state.loops.length < 1) {
+ mscript.state.proj++;
+ } else {
+ mscript.state.loops[mscript.state.rec].proj++;
+ }
+ } else if (cmd === 'PB') {
+ if (mscript.state.loops.length < 1) {
+ mscript.state.proj--;
+ } else {
+ mscript.state.loops[mscript.state.rec].proj--;
+ }
+ } else if (cmd === 'BF') {
+ if (mscript.state.loops.length < 1) {
+ mscript.state.cam++;
+ } else {
+ mscript.state.loops[mscript.state.rec].cam++;
+ }
+ } else if (cmd === 'BB') {
+ if (mscript.state.loops.length < 1) {
+ mscript.state.cam--;
+ } else {
+ mscript.state.loops[mscript.state.rec].cam++;
+ }
+ } else if (cmd === 'L ') {
+
+ }
+};
+/**
+ *
+ */
+mscript.str_to_arr = function str_to_arr (str, cmd) {
+ var cnt = str.split(cmd),
+ c = parseInt(cnt[1]),
+ arr = [];
+ if (cnt[1] === '') {
+ c = 1;
+ } else {
+ c = parseInt(cnt[1]);
+ }
+ for (var i = 0; i < c; i++) {
+ arr.push(cmd);
+ mscript.state_update(cmd);
+ }
+ return arr;
+};
+/**
+ *
+ */
+mscript.light_state = function light_state (str) {
+ //add parsers for other color spaces
+ var color = str.replace('L ', '').trim();
+ mscript.state.color = color;
+};
+/**
+ *
+ */
+mscript.light_to_arr = function light_to_arr (str, cmd) {
+ var cnt = str.split(cmd),
+ c = parseInt(cnt[1]),
+ arr = [];
+ if (cnt[1] === '') {
+ c = 1;
+ } else {
+ c = parseInt(cnt[1]);
+ }
+ for (var i = 0; i < c; i++) {
+ if (cmd === 'CF'
+ || cmd === 'CB') {
+ arr.push(mscript.state.color);
+ } else if (cmd === 'BF'
+ || cmd === 'BB') {
+ arr.push(mscript.black);
+ } else {
+ arr.push('');
+ }
+ }
+ return arr;
+};
+/**
+ *
+ */
+mscript.loop_count = function loop_count (str) {
+ return parseInt(str.split(' ')[1]);
+};
+mscript.fade_count = function fade_count (str) {
+ return parseInt(str.split(' ')[1]);
+}
+/**
+ *
+ */
+mscript.fail = function fail (reason) {
+ console.error(JSON.stringify({success: false, error: true, msg : reason}));
+ if (process) process.exit();
+};
+/**
+ *
+ */
+mscript.output = function output (data) {
+ var json = true; //default
+ if (mscript.arg('-j', '--json')) {
+ json = true;
+ }
+
+ if (mscript.arg('-t', '--text')) {
+ json = false;
+ }
+
+ if (json) {
+ console.log(JSON.stringify(data));
+ } else {
+ var ids = Object.keys(data);
+ for (var i = 0; i < ids.length; i++) {
+ console.log(ids[i] + ': ' + data[ids[i]]);
+ }
+ }
+};
+/**
+ *
+ */
+mscript.init = function init () {
+ if (mscript.arg('-t', '--tests')) {
+ return mscript.tests();
+ }
+
+ if (mscript.arg('-v', '--verbose')) {
+ console.time('mscript');
+ }
+
+ if (mscript.arg('-c', '--cam')) {
+ mscript.state.cam = parseInt(process.argv[mscript.arg_pos('-c', '--cam') + 1]);
+ }
+
+ if (mscript.arg('-p', '--proj')) {
+ mscript.state.proj = parseInt(process.argv[mscript.arg_pos('-p', '--proj') + 1]);
+ }
+
+ if (mscript.arg('-f', '--file')) {
+ input = process.argv[mscript.arg_pos('-f', '--file') + 1];
+ mscript.interpret(fs.readFileSync(input, 'utf8'));
+ } else {
+ mscript.interpret(input);
+ }
+
+ if (mscript.arg('-v', '--verbose')) {
+ console.timeEnd('mscript');
+ }
+};
+
+if (typeof document === 'undefined'
+ && typeof module !== 'undefined'
+ && !module.parent) {
+ //node script
+ fs = require('fs');
+ input = process.argv[2];
+ mscript.init();
+} else if (typeof module !== 'undefined' && module.parent) {
+ //module
+ fs = require('fs');
+ module.exports = mscript;
+} else {
+ //web
+}
+
+
+/*
+
+CAM # - go to camera frame #
+PROJ # - go to projector frame #
+
+SET CAM # - sets camera count to #
+SET PROJ # - sets projector count to #
+
+LOOP # - begin loop, can nest recursively, # times
+END LOOP - (or END) closes loop
+
+L #RGB - sets light to rgb value
+
+FADE
+
+CF - Camera forwards
+PF - Projector forwards
+BF - Black forwards
+CB - Camera backwards
+PB - Projector backwards
+BB - Black backwards
+
+*/
\ No newline at end of file
diff --git a/lib/mscript/package.json b/lib/mscript/package.json
new file mode 100644
index 0000000..5453ae8
--- /dev/null
+++ b/lib/mscript/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "mscript",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "",
+ "license": "ISC"
+}
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..3803e3b
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,20 @@
+{
+ "name": "mcopy",
+ "version": "0.1.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@types/node": {
+ "version": "11.10.4",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-11.10.4.tgz",
+ "integrity": "sha512-wa09itaLE8L705aXd8F80jnFpxz3Y1/KRHfKsYL2bPc0XF+wEWu8sR9n5bmeu8Ba1N9z2GRNzm/YdHcghLkLKg==",
+ "dev": true
+ },
+ "typescript": {
+ "version": "3.3.3333",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.3333.tgz",
+ "integrity": "sha512-JjSKsAfuHBE/fB2oZ8NxtRTk5iGcg6hkYXMnZ3Wc+b2RSqejEqTaem11mHASMnFilHrax3sLK0GDzcJrekZYLw==",
+ "dev": true
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..8e1236d
--- /dev/null
+++ b/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "mcopy",
+ "version": "0.1.0",
+ "description": "Small gauge film optical printer platform",
+ "main": "build.js",
+ "directories": {
+ "doc": "docs",
+ "lib": "lib"
+ },
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "build": "sh ./scripts/build.sh"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/sixteenmillimeter/mcopy.git"
+ },
+ "author": "mmcwilliams",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/sixteenmillimeter/mcopy/issues"
+ },
+ "homepage": "https://github.com/sixteenmillimeter/mcopy#readme",
+ "devDependencies": {
+ "@types/node": "^11.10.4",
+ "typescript": "^3.3.3333"
+ }
+}
diff --git a/scripts/build.sh b/scripts/build.sh
new file mode 100644
index 0000000..9e904a4
--- /dev/null
+++ b/scripts/build.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+./node_modules/.bin/tsc -p tsconfig.json
+
+#electron-build fails when local modules are in parent directory
+#copy them into lib directory
+cp -r ./lib/ ./app/lib
\ No newline at end of file
diff --git a/scripts/docs.sh b/scripts/docs.sh
new file mode 100644
index 0000000..aec64f2
--- /dev/null
+++ b/scripts/docs.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+libs="./lib/*"
+for l in $libs
+do
+ echo "Generating documentation for $l"
+ ./node_modules/.bin/jsdoc2md $l/index.js > $l/Readme.md
+done
\ No newline at end of file
diff --git a/src/arduino/index.ts b/src/arduino/index.ts
new file mode 100644
index 0000000..9703770
--- /dev/null
+++ b/src/arduino/index.ts
@@ -0,0 +1,375 @@
+'use strict'
+
+const SerialPort = require('serialport')
+const Readline = SerialPort.parsers.Readline
+const exec = require('child_process').exec
+
+const parser = new Readline('')
+const newlineRe : RegExp = new RegExp('\n', 'g')
+const returnRe : RegExp = new RegExp('\r', 'g')
+
+let eventEmitter : any
+let cfg : object
+
+/**
+ * Pause the process for X milliseconds in async/await functions
+ *
+ * @param {integer} ms milliseconds
+ *
+ * @returns {Promise} Resolves after wait
+ **/
+async function delay (ms : number) {
+ return new Promise(resolve => {
+ return setTimeout(resolve, ms)
+ })
+}
+
+/**
+ * Send a command to an Arduino using async/await
+ *
+ * @param {string} device Arduino identifier
+ * @param {string} cmd Single character command to send
+ *
+ * @returns {Promise} Resolves after sending
+ **/
+async function send (device : string, cmd : string) {
+ return new Promise ((resolve, reject) => {
+ arduino.queue[cmd] = (ms : number) => {
+ return resolve(ms)
+ }
+ return arduino.serial[device].write(cmd, (err : any, results : any) => {
+ if (err) {
+ //console.error(err)
+ return reject(err)
+ }
+ //
+ })
+ })
+}
+
+/**
+ * Send a string to an Arduino using async/await
+ *
+ * @param {string} device Arduino identifier
+ * @param {string} str String to send
+ *
+ * @returns {Promise} Resolves after sending
+ **/
+async function write (device : string, str : string) {
+ return new Promise ((resolve, reject) => {
+ arduino.serial[device].write(str, function (err, results) {
+ if (err) {
+ return reject(err)
+ }
+ //console.log('sent: ' + str)
+ return resolve(results)
+ })
+ })
+}
+
+/**
+ * Connect to an Arduino using async/await
+ *
+ * @param {string} device Arduino identifier
+ *
+ * @returns {Promise} Resolves after opening
+ **/
+async function openArduino (device : string) {
+ return new Promise((resolve, reject) => {
+ return arduino.serial[device].open(error => {
+ if (error) {
+ return reject(error)
+ }
+ return resolve(true)
+ })
+ })
+}
+
+/**
+ * Close a connection to an Arduino using async/await
+ *
+ * @param {string} device Arduino identifier
+ *
+ * @returns {Promise} Resolves after closing
+ **/
+async function closeArduino (device : string) {
+ return new Promise((resolve : any, reject : any) => {
+ return arduino.serial[device].close((err) => {
+ if (err) {
+ return reject(err)
+ }
+ return resolve(true)
+ })
+ })
+}
+
+/******
+ Arduino handlers
+*******/
+const arduino = {
+ path : {},
+ known: [
+ '/dev/tty.usbmodem1a161',
+ '/dev/tty.usbserial-A800f8dk',
+ '/dev/tty.usbserial-A900cebm',
+ '/dev/tty.usbmodem1a131',
+ '/dev/tty.usbserial-a900f6de',
+ '/dev/tty.usbmodem1a141',
+ '/dev/ttyACM0',
+ 'COM3'
+ ],
+ alias : {
+
+ },
+ serial : {
+ connect : {},
+ projector : {},
+ camera : {},
+ light : {}
+ },
+ baud : 57600,
+ queue : {},
+ timer : 0,
+ lock : false,
+ locks : {
+
+ }
+}
+
+arduino.enumerate = async function () {
+ return new Promise( (resolve, reject) => {
+ return SerialPort.list((err, ports) => {
+ let matches = []
+ if (err) {
+ return reject(err)
+ }
+ ports.forEach(port => {
+ if (arduino.known.indexOf(port.comName) !== -1) {
+ matches.push(port.comName)
+ } else if ((port.manufacturer + '').toLowerCase().indexOf('arduino') !== -1) {
+ matches.push(port.comName)
+ } else if ((port.comName + '').toLowerCase().indexOf('usbserial') !== -1) {
+ matches.push(port.comName)
+ } else if ((port.comName + '').toLowerCase().indexOf('usbmodem') !== -1) {
+ matches.push(port.comName)
+ }
+ })
+ if (matches.length === 0) {
+ return reject('No USB devices found');
+ } else if (matches.length > 0) {
+ return resolve(matches)
+ }
+ })
+ })
+}
+
+//commands which respond to a sent char
+arduino.send = async function (serial, cmd, res) {
+ const device = arduino.alias[serial]
+ let results
+ if (arduino.locks[serial]) {
+ return false
+ }
+ arduino.locks[serial] = true
+ await delay(cfg.arduino.serialDelay)
+ try {
+ results = await send(device, cmd)
+ } catch (e) {
+ return console.error(e)
+ }
+ arduino.locks[serial] = false
+ arduino.timer = new Date().getTime()
+ return await eventEmitter.emit('arduino_send', cmd)
+}
+
+//send strings, after char triggers firmware to accept
+arduino.string = async function (serial, str) {
+ const device = arduino.alias[serial]
+ let writeSuccess
+ await delay(cfg.arduino.serialDelay)
+ if (typeof arduino.serial[device].fake !== 'undefined'
+ && arduino.serial[device].fake) {
+ return arduino.serial[device].string(str)
+ } else {
+ try {
+ writeSuccess = await write(device, str)
+ } catch (e) {
+ return console.error(e)
+ }
+ return writeSuccess
+ }
+}
+
+//respond with same char over serial when done
+arduino.end = async function (serial, data) {
+ const end = new Date().getTime()
+ const ms = end - arduino.timer
+ let complete
+ if (arduino.queue[data] !== undefined) {
+ arduino.locks[serial] = false;
+ //console.log('Command ' + data + ' took ' + ms + 'ms');
+ complete = arduino.queue[data](ms) //execute callback
+ eventEmitter.emit('arduino_end', data)
+ delete arduino.queue[data]
+ } else {
+ //console.log('Received stray "' + data + '"'); //silent to user
+ }
+ return complete
+};
+arduino.alias = function (serial, device) {
+ console.log(`Making "${serial}" an alias of ${device}`)
+ arduino.alias[serial] = device
+}
+arduino.connect = async function (serial, device, confirm) {
+ return new Promise(async (resolve, reject) => {
+ let connectSuccess
+ arduino.path[serial] = device;
+ arduino.alias[serial] = device;
+ arduino.serial[device] = new SerialPort(arduino.path[serial], {
+ autoOpen : false,
+ baudRate: cfg.arduino.baud,
+ parser: parser
+ })
+ arduino.locks[device] = false
+ try {
+ connectSuccess = await openArduino(device)
+ } catch (e) {
+ console.error('failed to open: ' + e)
+ return reject(e)
+ }
+ console.log(`Opened connection with ${arduino.path[serial]} as ${serial}`);
+ if (!confirm) {
+ arduino.serial[device].on('data', async (data) => {
+ let d = data.toString('utf8')
+ d = d.replace(newlineRe, '').replace(returnRe, '')
+ return await arduino.end(serial, d)
+ })
+ } else {
+ arduino.serial[device].on('data', async (data) => {
+ let d = data.toString('utf8')
+ d = d.replace(newlineRe, '').replace(returnRe, '')
+ return await arduino.confirmEnd(d)
+ })
+ }
+ return resolve(arduino.path[serial])
+ })
+
+}
+
+arduino.confirmExec = {};
+arduino.confirmEnd = function (data) {
+ //console.dir(data)
+ if (data === cfg.arduino.cmd.connect
+ || data === cfg.arduino.cmd.proj_identifier
+ || data === cfg.arduino.cmd.cam_identifier
+ || data === cfg.arduino.cmd.light_identifier
+ || data === cfg.arduino.cmd.proj_light_identifier
+ || data === cfg.arduino.cmd.proj_cam_light_identifier
+ || data === cfg.arduino.cmd.proj_cam_identifier ) {
+ arduino.confirmExec(null, data);
+ arduino.confirmExec = {};
+ }
+}
+
+arduino.verify = async function () {
+ return new Promise(async (resolve, reject) => {
+ const device = arduino.alias['connect']
+ let writeSuccess
+ arduino.confirmExec = function (err, data) {
+ if (data === cfg.arduino.cmd.connect) {
+ return resolve(true)
+ } else {
+ return reject('Wrong data returned')
+ }
+ }
+ await delay(cfg.arduino.serialDelay)
+ try {
+ writeSuccess = await send(device, cfg.arduino.cmd.connect)
+ } catch (e) {
+ return reject(e)
+ }
+ return resolve(writeSuccess)
+ })
+}
+
+arduino.distinguish = async function () {
+ return new Promise(async (resolve, reject) => {
+ const device = arduino.alias['connect']
+ let writeSuccess
+ let type
+ arduino.confirmExec = function (err, data) {
+ if (data === cfg.arduino.cmd.proj_identifier) {
+ type = 'projector'
+ } else if (data === cfg.arduino.cmd.cam_identifier) {
+ type = 'camera'
+ } else if (data === cfg.arduino.cmd.light_identifier) {
+ type = 'light'
+ } else if (data === cfg.arduino.cmd.proj_light_identifier) {
+ type = 'projector,light'
+ } else if (data === cfg.arduino.cmd.proj_cam_light_identifier) {
+ type = 'projector,camera,light'
+ } else if (data === cfg.arduino.cmd.proj_cam_identifier) {
+ type = 'projector,camera'
+ } else if (data === cfg.ardino.cmd.proj_second_identifier) {
+ type = 'projector_second'
+ }
+ return resolve(type)
+ }
+ await delay(cfg.arduino.serialDelay)
+ try {
+ writeSuccess = await send(device, cfg.arduino.cmd.mcopy_identifier)
+ } catch (e) {
+ console.error(e)
+ return reject(e)
+ }
+ })
+}
+
+arduino.close = async function (callback) {
+ const device = arduino.alias['connect']
+ let closeSuccess
+ try {
+ closeSuccess = await closeArduino(device)
+ } catch (e) {
+ return console.error(e)
+ }
+ return closeSuccess
+};
+
+arduino.fakeConnect = async function (serial) {
+ //console.log('Connecting to fake arduino...');
+ const device = '/dev/fake'
+ arduino.alias[serial] = device
+ arduino.serial[device] = {
+ write : function (cmd, cb) {
+ const t = {
+ c : cfg.arduino.cam.time + cfg.arduino.cam.delay,
+ p : cfg.arduino.proj.time + cfg.arduino.proj.delay
+ }
+ let timeout = t[cmd]
+ let end
+ if (typeof timeout === 'undefined') timeout = 10
+ arduino.timer = +new Date()
+ setTimeout(() => {
+ arduino.end(serial, cmd)
+ return cb()
+ }, timeout)
+
+ },
+ string : async function (str) {
+ //do nothing
+ return true
+ },
+ fake : true
+ };
+ //console.log('Connected to fake arduino! Not real! Doesn\'t exist!');
+ return true
+}
+
+if (typeof module !== 'undefined' && module.parent) {
+ module.exports = function (c, ee) {
+ eventEmitter = ee
+ cfg = c
+ return arduino
+ }
+}
\ No newline at end of file
diff --git a/src/mscript/index.js b/src/mscript/index.js
new file mode 100644
index 0000000..e2fac5e
--- /dev/null
+++ b/src/mscript/index.js
@@ -0,0 +1,545 @@
+'use strict';
+
+/** @module lib/mscript */
+
+const BLACK = '0,0,0';
+const WHITE = '255,255,255';
+const CMD = [
+ 'CF',
+ 'PF',
+ 'BF',
+ 'CB',
+ 'PB',
+ 'BB'
+];
+const ALTS = {
+ 'CF' : ['CAMERA FORWARD', 'CAM FORWARD'],
+ 'PF' : ['PROJECTOR FORWARD', 'PROJ FORWARD'],
+ 'BF' : ['BLACK FORWARD', 'BLACK', 'BLANK FORWARD', 'BLANK'],
+ 'CB' : ['CAMERA BACKWARD', 'CAM BACKWARD', 'CAMERA BACK', 'CAM BACK'],
+ 'PB' : ['PROJECTOR FORWARD', 'PROJ FORWARD', 'PROJECTOR BACK', 'PROJ BACK'],
+ 'BB' : ['BLACK BACKWARD', 'BLACK BACK', 'BLANK BACK'],
+ 'L ' : ['LIGHT', 'COLOR', 'LAMP'],
+ 'F ' : ['FADE']
+};
+
+/** helper functions */
+
+/** startswith function from lodash, do not want the entire lib for this */
+function startsWith(string, target, position) {
+ const { length } = string;
+ position = position == null ? 0 : position;
+ if (position < 0) {
+ position = 0;
+ } else if (position > length) {
+ position = length;
+ }
+ target = `${target}`;
+ return string.slice(position, position + target.length) == target;
+}
+
+/** class Mscript */
+class Mscript {
+ constructor () {
+ this.output = {};
+ }
+ /**
+ * Clear the state of the script
+ */
+ clear () {
+ this.lines = [];
+
+ this.cam = 0;
+ this.proj = 0;
+ this.color = '';
+ this.loops = [];
+ this.rec = -1;
+
+ this.two = '';
+ this.arr = [];
+ this.light = [];
+ this.target = 0; //move to target using CAM # or PROJ #
+ this.dist = 0;
+
+ this.variables = {};
+
+ this.output = {};
+ }
+ /**
+ * Main function, accepts multi-line string, parses into lines
+ * and interprets the instructions from the text. Returns an array
+ * of steps to be fed into the mcopy.
+ */
+ interpret (text, callback) {
+ this.clear()
+
+ if (typeof text === 'undefined') {
+ return this.fail('No input');
+ }
+
+ //split string into lines, each containing a command
+ this.lines = text.split('\n');
+
+ this.lines = this.lines.map(line => {
+ line = line.replace(/\t+/g, ''); //strip tabs
+ line = line.trim(); //remove excess whitespace before and after command
+ line = line.toUpperCase();
+ return line;
+ })
+
+ for (let line of this.lines) {
+ this.two = line.substring(0, 2);
+ if (CMD.indexOf(this.two) !== -1) {
+ this.basic_cmd(line);
+ } else if (startsWith(line, '@') || line.indexOf('@') !== -1) {
+ this.variable(line);
+ } else if (startsWith(line, 'LOOP')) {
+ this.new_loop(line);
+ } else if (startsWith(line, 'L ')) {
+ this.light_state(line);
+ } else if (startsWith(line, 'F ')) {
+ this.new_loop(line, true);
+ } else if (startsWith(line, 'END')) {
+ this.end_loop(line);
+ } else if (startsWith(line, 'CAM')) { //directly go to that frame (black?)
+ this.move_cam(line);
+ } else if (startsWith(line, 'PROJ')) { //directly go to that frame
+ this.move_proj(line);
+ } else if (startsWith(line, 'SET')) { //set that state
+ this.set_state(line);
+ } else if (startsWith(line, '#') || startsWith(line, '//')) {
+ //comments
+ //ignore while parsing
+ }
+ }
+
+ this.output.success = true;
+ this.output.arr = this.arr; //all instructions
+ this.output.light = this.light; //all light instructions
+ this.output.cam = this.cam;
+ this.output.proj = this.proj;
+
+ if (typeof callback !== 'undefined') {
+ //should only be invoked by running mscript.tests()
+ callback(this.output);
+ } else {
+ return this.output;
+ }
+ }
+ variable (line) {
+ let parts = line.split('=');
+ let key = parts[0];
+ let value = parts[1];
+ let update = false;
+
+ if (value && value.indexOf('#') !== -1) {
+ value = value.split('#')[0];
+ }
+
+ if (value && value.indexOf('//') !== -1) {
+ value = value.split('//')[0];
+ }
+
+ if (value && value.indexOf('+') !== -1) {
+ if (value)
+ update = true;
+ }
+
+ if (line.indexOf('-') !== -1) {
+
+ update = true;
+ }
+
+ if (line.indexOf(',') === -1) { //if not color string
+ try {
+ value = parseInt(value);
+ } catch (err) {
+ //supress parsing error
+ }
+ }
+ //console.dir(parts)
+ if (!this.variables[key] || update) {
+ this.variables[key] = value;
+ }
+ console.dir(this.variables)
+ }
+ variable_replace(line) {
+
+ }
+ /**
+ * Apply a basic two character command
+ */
+ basic_cmd (line) {
+ if (this.rec !== -1) {
+ //hold generated arr in state loop array
+ this.loops[this.rec].arr
+ .push.apply(this.loops[this.rec].arr,
+ this.str_to_arr(line,
+ this.two));
+ this.loops[this.rec].light
+ .push.apply(this.loops[this.rec].light,
+ this.light_to_arr(line,
+ this.two));
+ } else {
+ this.arr.push.apply(this.arr, this.str_to_arr(line, this.two));
+ this.light.push.apply(this.light, this.light_to_arr(line, this.two))
+ }
+ }
+ /**
+ * Start a new loop
+ */
+ new_loop (line, fade) {
+ this.rec++;
+ this.loops[this.rec] = {
+ arr : [],
+ light : [],
+ cam : 0,
+ proj : 0,
+ cmd : line + ''
+ };
+ if (fade) {
+ this.fade(line);
+ }
+ }
+ /**
+ * Close the most recent loop
+ */
+ end_loop (line) {
+ let light_arr;
+ let start;
+ let end;
+ let len;
+
+ for (let x = 0; x < this.loop_count(this.loops[this.rec].cmd); x++) {
+ light_arr = this.loops[this.rec].light;
+ if (this.loops[this.rec].fade) {
+ start = this.loops[this.rec].start;
+ end = this.loops[this.rec].end;
+ len = this.loops[this.rec].fade_len;
+ light_arr = light_arr.map(l => {
+ return this.fade_rgb(start, end, len, x);
+ })
+ }
+ if (this.rec === 0) {
+ this.arr.push.apply(this.arr, this.loops[this.rec].arr);
+ this.light.push.apply(this.light, light_arr);
+ } else if (this.rec >= 1) {
+ this.loops[this.rec - 1].arr
+ .push.apply(this.loops[this.rec - 1].arr,
+ this.loops[this.rec].arr);
+
+ this.loops[this.rec - 1].light
+ .push.apply(this.loops[this.rec - 1].light,
+ light_arr);
+ }
+ }
+ this.update('END', this.loop_count(this.loops[this.rec].cmd));
+ delete this.loops[this.rec];
+ this.rec--;
+ }
+ /**
+ * Move camera to explicitly-defined frame
+ */
+ move_cam (line) {
+ this.target = parseInt(line.split('CAM ')[1]);
+ if (this.rec !== -1) {
+ if (this.target > this.cam) {
+ this.dist = this.target - this.cam;
+ for (let x = 0; x < this.dist; x++) {
+ this.loops[this.rec].arr.push('BF');
+ this.loops[this.rec].light.push(BLACK);
+ this.update('BF');
+ }
+ } else {
+ this.dist = this.cam - this.target;
+ for (let x = 0; x < this.dist; x++) {
+ this.loops[this.rec].arr.push('BB');
+ this.loops[this.rec].light.push(BLACK);
+ this.update('BB');
+ }
+ }
+ } else {
+ if (target > this.cam) {
+ this.dist = this.target - this.cam;
+ for (let x = 0; x < this.dist; x++) {
+ this.arr.push('BF');
+ this.light.push(BLACK);
+ this.update('BF');
+ }
+ } else {
+ this.dist = this.cam - this.target;
+ for (let x = 0; x < this.dist; x++) {
+ this.arr.push('BB');
+ this.light.push(BLACK);
+ this.update('BB');
+ }
+ }
+ }
+ }
+ /**
+ * Move projector to explicitly-defined frame
+ */
+ move_proj (line) {
+ this.target = parseInt(line.split('PROJ ')[1]);
+ if (this.rec !== -1) {
+ if (this.target > this.proj) {
+ this.dist = this.target - this.proj;
+ for (let x = 0; x < this.dist; x++) {
+ this.loops[this.rec].arr.push('PF');
+ this.loops[this.rec].light.push('');
+ this.update('PF');
+ }
+ } else {
+ this.dist = this.proj - this.target;
+ for (let x = 0; x < this.dist; x++) {
+ this.loops[this.rec].arr.push('PB');
+ this.loops[this.rec].light.push('');
+ this.update('PB');
+ }
+ }
+ } else {
+ if (this.target > this.proj) {
+ this.dist = this.target - this.proj;
+ for (let x = 0; x < this.dist; x++) {
+ this.arr.push('PF');
+ this.light.push('');
+ this.update('PF');
+ }
+ } else {
+ this.dist = this.proj - this.target;
+ for (let x = 0; x < this.dist; x++) {
+ this.arr.push('PB');
+ this.light.push('');
+ this.update('PB');
+ }
+ }
+ }
+ }
+ /**
+ * Set the state of either the cam or projector
+ */
+ set_state (line) {
+ if (startsWith(line, 'SET CAM')) {
+ this.cam = parseInt(line.split('SET CAM')[1]);
+ } else if (startsWith(line, 'SET PROJ')) {
+ this.proj = parseInt(line.split('SET PROJ')[1]);
+ }
+ }
+ /**
+ * Return the last loop
+ */
+ last_loop () {
+ return this.loops[this.loops.length - 1];
+ }
+ /**
+ * Return the second-last loop
+ */
+ parent_loop () {
+ return this.loops[this.loops.length - 2];
+ }
+ /**
+ * Extract the loop count integer from a LOOP cmd
+ */
+ loop_count (str) {
+ return parseInt(str.split(' ')[1]);
+ }
+ /**
+ * Execute a fade of frame length, from color to another color
+ */
+ fade (line) {
+ let len = this.fade_count(line);
+ let start = this.fade_start(line);
+ let end = this.fade_end(line);
+
+ this.loops[this.rec].start = start;
+ this.loops[this.rec].end = end;
+ this.loops[this.rec].fade = true;
+ this.loops[this.rec].fade_count = 0;
+ this.loops[this.rec].fade_len = len;
+ }
+ /**
+ * Extract the fade length integer from a FADE cmd
+ */
+ fade_count (str) {
+ return parseInt(str.split(' ')[1]);
+ }
+ /**
+ * Extract the start color from a string
+ */
+ fade_start (str) {
+ let color = str.split(' ')[2];
+ return this.rgb(color.trim())
+ }
+ /**
+ * Extract the end color from a string
+ */
+ fade_end (str) {
+ let color = str.split(' ')[3];
+ return this.rgb(color.trim())
+ }
+ fade_rgb (start, end, len, x) {
+ let cur = [];
+ let diff;
+ for (let i = 0; i < 3; i++) {
+ if (x === len - 1) {
+ cur[i] = end[i];
+ } else if (start[i] >= end[i]) {
+ diff = start[i] - end[i];
+ cur[i] = start[i] - Math.round((diff / (len - 1)) * x);
+ } else {
+ diff = end[i] - start[i];
+ cur[i] = start[i] + Math.round((diff / (len - 1)) * x);
+ }
+ }
+ return this.rgb_str(cur);
+
+ }
+ rgb (str) {
+ let rgb = str.split(',');
+ return rgb.map( char => {
+ return parseInt(char);
+ })
+ }
+ rgb_str (arr) {
+ return arr.join(',');
+ }
+ /**
+ * Increase the state of a specific object, such as the camera/projector,
+ * by the value defined in val
+ */
+ update (cmd, val = 1) {
+ if (cmd === 'END') {
+ //I don't understand this loop
+ for (let i = 0; i < val; i++) {
+ if (this.rec === 0) {
+ this.cam += this.loops[this.rec].cam;
+ this.proj += this.loops[this.rec].proj;
+ } else if (this.rec >= 1) {
+ this.loops[this.rec - 1].cam += this.loops[this.rec].cam;
+ this.loops[this.rec - 1].proj += this.loops[this.rec].proj;
+ }
+ }
+ } else if (cmd === 'CF') {
+ if (this.rec === -1) {
+ this.cam += val;
+ } else {
+ this.loops[this.rec].cam += val;
+ }
+ } else if (cmd === 'CB') {
+ if (this.rec === -1) {
+ this.cam -= val;
+ } else {
+ this.loops[this.rec].cam--;
+ }
+ } else if (cmd === 'PF') {
+ if (this.rec === -1) {
+ this.proj += val;
+ } else {
+ this.loops[this.rec].proj += val;
+ }
+ } else if (cmd === 'PB') {
+ if (this.rec === -1) {
+ this.proj -= val;
+ } else {
+ this.loops[this.rec].proj--;
+ }
+ } else if (cmd === 'BF') {
+ if (this.rec === -1) {
+ this.cam += val;
+ } else {
+ this.loops[this.rec].cam += val;
+ }
+ } else if (cmd === 'BB') {
+ if (this.rec === -1) {
+ this.cam -= val;
+ } else {
+ this.loops[this.rec].cam -= val;
+ }
+ } else if (cmd === 'L ') {
+
+ }
+ }
+ /**
+ * Split string on command, extract any integers from string
+ */
+ str_to_arr (str, cmd) {
+ const cnt = str.split(cmd);
+ let c = parseInt(cnt[1]);
+ let arr = [];
+ if (cnt[1] === '') {
+ c = 1;
+ } else {
+ c = parseInt(cnt[1]);
+ }
+ arr = new Array(c).fill(cmd);
+ this.update(cmd, c);
+ return arr;
+ }
+ /**
+ * Split a string on a command to extract data for light array
+ */
+ light_to_arr (str, cmd) {
+ const cnt = str.split(cmd);
+ let c = parseInt(cnt[1]);
+ let arr = [];
+ if (cnt[1] === '') {
+ c = 1;
+ } else {
+ c = parseInt(cnt[1]);
+ }
+ for (var i = 0; i < c; i++) {
+ if (cmd === 'CF'
+ || cmd === 'CB') {
+ arr.push(this.color);
+ } else if (cmd === 'BF'
+ || cmd === 'BB') {
+ arr.push(BLACK);
+ } else {
+ arr.push('');
+ }
+ }
+ return arr;
+ }
+ /**
+ * Split a string to extract an rgb color value
+ */
+ light_state (str) {
+ //add parsers for other color spaces
+ const color = str.replace('L ', '').trim();
+ this.color = color;
+ }
+
+ /**
+ * Throw an error with specific message
+ */
+ fail (msg) {
+ throw new Error(msg);
+ }
+}
+
+module.exports = Mscript;
+
+
+/*
+
+CAM # - go to camera frame #
+PROJ # - go to projector frame #
+
+SET CAM # - sets camera count to #
+SET PROJ # - sets projector count to #
+
+LOOP # - begin loop, can nest recursively, # times
+END LOOP - (or END) closes loop
+
+L #RGB - sets light to rgb value
+
+FADE 24 0,0,0 255,255,255
+
+CF - Camera forwards
+PF - Projector forwards
+BF - Black forwards
+CB - Camera backwards
+PB - Projector backwards
+BB - Black backwards
+
+*/
\ No newline at end of file
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..cd5b18a
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "esModuleInterop": true,
+ "target": "ES2017",
+ "noImplicitAny": true,
+ "moduleResolution": "node",
+ "sourceMap": true,
+ "removeComments" : false,
+ "outDir": "./lib/",
+ "rootDir" : "./src/"
+ },
+ "exclude" : []
+}
\ No newline at end of file