From c453e7ac4325c783fb36b74a48b9578c7133d02d Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Fri, 2 Mar 2018 22:42:15 -0500 Subject: [PATCH] Async/Await refactor for arduino and sequence-related features --- app/lib/arduino/index.js | 299 +++++++++++++++++++------------ app/main.js | 368 +++++++++++++++++++++++---------------- 2 files changed, 402 insertions(+), 265 deletions(-) diff --git a/app/lib/arduino/index.js b/app/lib/arduino/index.js index 139ccb6..2726f97 100644 --- a/app/lib/arduino/index.js +++ b/app/lib/arduino/index.js @@ -10,6 +10,58 @@ let eventEmitter const mcopy = {} +async function delay (ms) { + return new Promise(resolve => { + return setTimeout(resolve, ms) + }) +} + +async function send (device, cmd) { + return new Promise ((resolve, reject) => { + mcopy.arduino.serial[device].write(cmd, (err, results) => { + if (err) { + //console.error(err) + return reject(err) + } + return resolve(results) + }) + }) +} + +async function write (device, str) { + return new Promise ((resolve, reject) => { + mcopy.arduino.serial[device].write(str, function (err, results) { + if (err) { + return reject(err) + } + //console.log('sent: ' + str) + return resolve(results) + }) + }) +} + +async function open (device) { + return new Promise((resolve, reject) => { + return mcopy.arduino.serial[device].open(error => { + if (error) { + return reject(error) + } + return resolve(true) + }) + }) +} + +async function close (device) { + return new Promise((resolve, reject) => { + return mcopy.arduino.serial[device].close((err) => { + if (err) { + return reject(err) + } + return resolve(true) + }) + }) +} + /****** Arduino handlers *******/ @@ -40,56 +92,67 @@ mcopy.arduino = { lock : false } -mcopy.arduino.enumerate = function (callback) { - let matches = [] - SerialPort.list((err, ports) => { - //console.dir(ports) - ports.forEach(port => { - if (mcopy.arduino.known.indexOf(port.comName) !== -1) { - matches.push(port.comName) - } else if ((port.manufacturer + '').toLowerCase().indexOf('arduino') !== -1) { - matches.push(port.comName) +mcopy.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 (mcopy.arduino.known.indexOf(port.comName) !== -1) { + matches.push(port.comName) + } else if ((port.manufacturer + '').toLowerCase().indexOf('arduino') !== -1) { + matches.push(port.comName) + } + }) + if (matches.length === 0) { + return reject('No USB devices found'); + } else if (matches.length > 0) { + return resolve(matches) } }) - if (matches.length === 0) { - if (callback) { callback('No USB devices found'); } - } else if (matches.length > 0) { - if (callback) { callback(null, matches); } - } }) } //commands which respond to a sent char -mcopy.arduino.send = function (serial, cmd, res) { +mcopy.arduino.send = async function (serial, cmd, res) { const device = mcopy.arduino.alias[serial] - if (!mcopy.arduino.lock) { - mcopy.arduino.lock = true - mcopy.arduino.queue[cmd] = res - setTimeout(() => { - mcopy.arduino.serial[device].write(cmd, (err, results) => { - if (err) { console.log(err) } - mcopy.arduino.lock = false - mcopy.arduino.timer = new Date().getTime() - }) - }, mcopy.cfg.arduino.serialDelay) - eventEmitter.emit('arduino_send', cmd) + let results + if (mcopy.arduino.lock) { + return false } -}; + mcopy.arduino.lock = true + mcopy.arduino.queue[cmd] = res + await delay(mcopy.cfg.arduino.serialDelay) + try { + results = await send(device, cmd) + } catch (e) { + return console.error(e) + } + mcopy.arduino.lock = false + mcopy.arduino.timer = new Date().getTime() + return await eventEmitter.emit('arduino_send', cmd) +} + //send strings, after char triggers firmware to accept -mcopy.arduino.string = function (serial, str) { +mcopy.arduino.string = async function (serial, str) { const device = mcopy.arduino.alias[serial] - setTimeout(function () { - if (typeof mcopy.arduino.serial[device].fake !== 'undefined' - && mcopy.arduino.serial[device].fake) { - mcopy.arduino.serial[device].string(str); - } else { - mcopy.arduino.serial[device].write(str, function (err, results) { - if (err) { console.log(err); } - //console.log('sent: ' + str); - }); + let writeSuccess + await delay(mcopy.cfg.arduino.serialDelay) + if (typeof mcopy.arduino.serial[device].fake !== 'undefined' + && mcopy.arduino.serial[device].fake) { + return mcopy.arduino.serial[device].string(str) + } else { + try { + writeSuccess = await write(device, str) + } catch (e) { + return console.error(e) } - }, mcopy.cfg.arduino.serialDelay); -}; + return writeSuccess + } +} + //respond with same char over serial when done mcopy.arduino.end = function (data) { var end = new Date().getTime(), @@ -98,49 +161,50 @@ mcopy.arduino.end = function (data) { mcopy.arduino.lock = false; //console.log('Command ' + data + ' took ' + ms + 'ms'); mcopy.arduino.queue[data](ms); //execute callback - eventEmitter.emit('arduino_end', data); - delete mcopy.arduino.queue[data]; + eventEmitter.emit('arduino_end', data) + delete mcopy.arduino.queue[data] } else { - console.log('Received stray "' + data + '"'); //silent to user + //console.log('Received stray "' + data + '"'); //silent to user } }; mcopy.arduino.alias = function (serial, device) { console.log(`Making "${serial}" an alias of ${device}`) mcopy.arduino.alias[serial] = device } -mcopy.arduino.connect = function (serial, device, confirm, callback) { - mcopy.arduino.path[serial] = device; - mcopy.arduino.alias[serial] = device; - mcopy.arduino.serial[device] = new SerialPort(mcopy.arduino.path[serial], { - autoOpen : false, - baudRate: mcopy.cfg.arduino.baud, - parser: parser - }); - mcopy.arduino.serial[device].open(error => { - if (error) { - if (callback) { callback(error); } - return console.log('failed to open: '+ error); - } else { - console.log(`Opened connection with ${mcopy.arduino.path[serial]} as ${serial}`); - if (!confirm) { - mcopy.arduino.serial[device].on('data', data => { - let d = data.toString('utf8') - d = d.replace(newlineRe, '').replace(returnRe, '') - mcopy.arduino.end(d) - }) - } else { - mcopy.arduino.serial[device].on('data', data => { - let d = data.toString('utf8') - d = d.replace(newlineRe, '').replace(returnRe, '') - mcopy.arduino.confirmEnd(d) - }) - } - if (callback) { - callback(null, mcopy.arduino.path[serial]) - } +mcopy.arduino.connect = async function (serial, device, confirm) { + return new Promise(async (resolve, reject) => { + let connectSuccess + mcopy.arduino.path[serial] = device; + mcopy.arduino.alias[serial] = device; + mcopy.arduino.serial[device] = new SerialPort(mcopy.arduino.path[serial], { + autoOpen : false, + baudRate: mcopy.cfg.arduino.baud, + parser: parser + }) + try { + connectSuccess = await open(device) + } catch (e) { + console.error('failed to open: ' + e) + return reject(e) } - }); -}; + console.log(`Opened connection with ${mcopy.arduino.path[serial]} as ${serial}`); + if (!confirm) { + mcopy.arduino.serial[device].on('data', async data => { + let d = data.toString('utf8') + d = d.replace(newlineRe, '').replace(returnRe, '') + mcopy.arduino.end(d) + }) + } else { + mcopy.arduino.serial[device].on('data', data => { + let d = data.toString('utf8') + d = d.replace(newlineRe, '').replace(returnRe, '') + mcopy.arduino.confirmEnd(d) + }) + } + return resolve(mcopy.arduino.path[serial]) + }) + +} mcopy.arduino.confirmExec = {}; mcopy.arduino.confirmEnd = function (data) { @@ -155,24 +219,32 @@ mcopy.arduino.confirmEnd = function (data) { mcopy.arduino.confirmExec(null, data); mcopy.arduino.confirmExec = {}; } -}; -mcopy.arduino.verify = function (callback) { - const device = mcopy.arduino.alias['connect'] - mcopy.arduino.confirmExec = function (err, data) { - if (data === mcopy.cfg.arduino.cmd.connect) { - callback(null, true); - } - }; - setTimeout(function () { - mcopy.arduino.serial[device].write(mcopy.cfg.arduino.cmd.connect, (err, results) => { - if (err) { - return console.log(err); +} + +mcopy.arduino.verify = async function () { + return new Promise(async (resolve, reject) => { + const device = mcopy.arduino.alias['connect'] + let writeSuccess + mcopy.arduino.confirmExec = function (err, data) { + if (data === mcopy.cfg.arduino.cmd.connect) { + return resolve(true) + } else { + return reject('Wrong data returned') } - }); - }, mcopy.cfg.arduino.serialDelay); -}; -mcopy.arduino.distinguish = function (callback) { + } + await delay(mcopy.cfg.arduino.serialDelay) + try { + writeSuccess = await send(device, mcopy.cfg.arduino.cmd.connect) + } catch (e) { + return reject(e) + } + return resolve(writeSuccess) + }) +} + +mcopy.arduino.distinguish = async function (callback) { const device = mcopy.arduino.alias['connect'] + let writeSuccess mcopy.arduino.confirmExec = function (err, data) { if (data === mcopy.cfg.arduino.cmd.proj_identifier) { callback(null, 'projector'); @@ -188,49 +260,52 @@ mcopy.arduino.distinguish = function (callback) { callback(null, 'projector,camera') } } - setTimeout(function () { - mcopy.arduino.serial[device].write(mcopy.cfg.arduino.cmd.mcopy_identifier, function (err, results) { - if (err) { - return console.log(err); - } - }); - }, mcopy.cfg.arduino.serialDelay); + await delay(mcopy.cfg.arduino.serialDelay) + try { + writeSuccess = await send(device, mcopy.cfg.arduino.cmd.mcopy_identifier) + } catch (e) { + return console.error(e) + } + return writeSuccess } -mcopy.arduino.close = function (callback) { - let device = mcopy.arduino.alias['connect'] - mcopy.arduino.serial[device].close((err) => { - if (callback) { - callback(err) - } - }); +mcopy.arduino.close = async function (callback) { + const device = mcopy.arduino.alias['connect'] + let closeSuccess + try { + closeSuccess = await close(device) + } catch (e) { + return console.error(e) + } + return closeSuccess }; -mcopy.arduino.fakeConnect = function (serial, callback) { +mcopy.arduino.fakeConnect = async function (serial) { //console.log('Connecting to fake arduino...'); const device = '/dev/fake' mcopy.arduino.alias[serial] = device mcopy.arduino.serial[device] = { - write : function (cmd, res) { - var t = { + write : async function (cmd, res) { + return new Promise(async (resolve, reject) => { + var t = { c : mcopy.cfg.arduino.cam.time + mcopy.cfg.arduino.cam.delay, p : mcopy.cfg.arduino.proj.time + mcopy.cfg.arduino.proj.delay }, timeout = t[cmd]; if (typeof timeout === 'undefined') timeout = 10; mcopy.arduino.timer = +new Date(); - setTimeout(() => { - mcopy.arduino.end(cmd) - }, timeout) + await delay(timeout) + return await mcopy.arduino.end(cmd) + }) }, - string : function (str) { + string : async function (str) { //do nothing return true }, fake : true }; //console.log('Connected to fake arduino! Not real! Doesn\'t exist!'); - if (callback) callback() + return true } if (typeof module !== 'undefined' && module.parent) { diff --git a/app/main.js b/app/main.js index b452625..1a72810 100644 --- a/app/main.js +++ b/app/main.js @@ -36,6 +36,12 @@ let camera let server let menu +async function delay (ms) { + return new Promise(resolve => { + return setTimeout(resolve, ms) + }) +} + //console.log(process.version) mcopy.cfg = require('./data/cfg.json') @@ -53,17 +59,18 @@ dev.listen = function () { }) } -dev.enumerate = function (err, devices) { - if (err) { +dev.enumerate = async function () { + let devices + try{ + devices = await arduino.enumerate() + } catch (err) { log.info(err, 'SERIAL', false, true) - setTimeout(() =>{ - dev.all([]) - }, 1000) - } else { - log.info(`Found ${devices.length} USB devices`, 'SERIAL', true, true) - devices = dev.favor(devices) - dev.all(devices) + await delay(1000) + return dev.all([]) } + log.info(`Found ${devices.length} USB devices`, 'SERIAL', true, true) + devices = dev.favor(devices) + return dev.all(devices) } dev.favor = function (devices) { @@ -88,162 +95,218 @@ dev.favor = function (devices) { return devices } -dev.distinguish = function (device, callback) { - var connectCb = function (err, device) { - if (err) { - return console.error(err) - } - setTimeout(function () { - arduino.verify(verifyCb) - }, 2000); - }, - verifyCb = function (err, success) { - if (err) { - return console.error(err) - } - log.info(`Verified ${device} as mcopy device`, 'SERIAL', true, true) +dev.distinguish = async function (device, callback) { + let connectSuccess + let verifySuccess + let type - setTimeout(function () { - arduino.distinguish(distinguishCb); - }, 1000); - }, - distinguishCb = function (err, type) { - if (err) { - return console.error(err) - } - dev.remember('arduino', device, type) - - log.info(`Determined ${device} to be ${type}`, 'SERIAL', true, true) - if (callback) { callback(err, type); } + try { + connectSuccess = await arduino.connect('connect', device, true) + } catch (err) { + console.error(err) + return null } - arduino.connect('connect', device, true, connectCb) -}; + + await delay(2000) + + try { + verifySuccess = await arduino.verify() + } catch (err) { + console.error(err) + return null + } + + log.info(`Verified ${device} as mcopy device`, 'SERIAL', true, true) + + await delay(1000) + + try { + type = await arduino.distinguish() + } catch (err) { + console.error(err) + return null + } + + dev.remember('arduino', device, type) + log.info(`Determined ${device} to be ${type}`, 'SERIAL', true, true) + + return type +} + +dev.fakeProjector = async function () { + dev.connected.projector = '/dev/fake' + try { + await arduino.fakeConnect('projector') + } catch (err) { + console.error(err) + log.error(`Error connecting to fake PRONECTOR device`, 'SERIAL', true, true) + return false + } + log.info('Connected to fake PROJECTOR device', 'SERIAL', true, true) + return true +} +dev.fakeCamera = async function () { + dev.connected.camera = '/dev/fake' + try { + await arduino.fakeConnect('camera') + } catch (err) { + console.error(err) + log.error(`Error connecting to fake CAMERA device`, 'SERIAL', true, true) + return false + } + log.info('Connected to fake CAMERA device', 'SERIAL', true, true) + return true +} +dev.fakeLight = async function () { + dev.connected.light = '/dev/fake' + try { + await arduino.fakeConnect('light') + } catch (err) { + console.error(err) + log.error(`Error connecting to fake LIGHT device`, 'SERIAL', true, true) + return false + } + log.info('Connected to fake LIGHT device', 'SERIAL', true, true) + return true +} + +dev.connectDevice = async function (device, type) { + let closeSuccess + let connectSuccess + try { + closeSuccess = await arduino.close() + } catch (err) { + console.error(err) + return false + } + if (type === 'projector') { + dev.connected.projector = device + try { + connectSuccess = await arduino.connect('projector', device, false) + } catch (err) { + console.error(err) + return false + } + log.info(`Connected to ${device} as PROJECTOR`, 'SERIAL', true, true) + } else if (type === 'camera') { + dev.connected.camera = device + try { + connectSuccess = await arduino.connect('camera', device, false) + } catch (err) { + console.error(err) + return false + } + log.info(`Connected to ${device} as CAMERA`, 'SERIAL', true, true) + } else if (type === 'light') { + dev.connected.light = device + try { + connectSuccess = await arduino.connect('light', device, false) + } catch (err) { + console.error(err) + return false + } + log.info(`Connected to ${device} as LIGHT`, 'SERIAL', true, true) + } else if (type === 'projector,light') { + dev.connected.projector = device + dev.connected.light = device + arduino.alias('light', device) + try{ + connectSuccess = await arduino.connect('projector', device, false) + } catch (err) { + console.error(err) + return false + } + log.info(`Connected to ${device} as PROJECTOR + LIGHT`, 'SERIAL', true, true) + + } else if (type === 'projector,camera,light') { + dev.connected.projector = device + dev.connected.camera = device + dev.connected.light = device + arduino.alias('camera', device) + arduino.alias('light', device) + try { + connectSuccess = await arduino.connect('projector', device, false) + } catch (err) { + console.error(err) + return false + } + log.info(`Connected to ${device} as PROJECTOR + CAMERA + LIGHT`, 'SERIAL', true, true) + + } else if (type === 'projector,camera') { + dev.connected.projector = device + dev.connected.camera = device + arduino.alias('camera', device) + try { + connectSuccess = await arduino.connect('projector', device, false) + } catch (err) { + console.error(err) + return false + } + log.info(`Connected to ${device} as PROJECTOR`, 'SERIAL', true, true) + } + return connectSuccess +} //Cases for 1 or 2 arduinos connected -dev.all = function (devices) { - const connected = { +dev.all = async function (devices) { + dev.connected = { projector : false, camera : false, light : false } let checklist = [] - var fakeProjector = function () { - connected.projector = '/dev/fake' - arduino.fakeConnect('projector', () => { - log.info('Connected to fake PROJECTOR device', 'SERIAL', true, true) - - }) - } - var fakeCamera = function () { - connected.camera = '/dev/fake' - arduino.fakeConnect('camera', () => { - log.info('Connected to fake CAMERA device', 'SERIAL', true, true) - }) - } - var fakeLight = function () { - connected.light = '/dev/fake' - arduino.fakeConnect('light', () => { - log.info('Connected to fake LIGHT device', 'SERIAL', true, true) - - }) - } - var distinguishCb = function (device, type, cb) { - arduino.close(() => { - if (type === 'projector') { - connected.projector = device - arduino.connect('projector', device, false, () => { - log.info(`Connected to ${device} as PROJECTOR`, 'SERIAL', true, true) - cb() - }) - } else if (type === 'camera') { - connected.camera = device - arduino.connect('camera', device, false, () => { - log.info(`Connected to ${device} as CAMERA`, 'SERIAL', true, true) - cb() - }) - } else if (type === 'light') { - connected.light = device - arduino.connect('light', device, false, () => { - log.info(`Connected to ${device} as LIGHT`, 'SERIAL', true, true) - cb() - }) - } else if (type === 'projector,light') { - connected.projector = device - connected.light = device - arduino.connect('projector', device, false, () => { - log.info(`Connected to ${device} as PROJECTOR + LIGHT`, 'SERIAL', true, true) - cb() - }) - arduino.alias('light', device) - } else if (type === 'projector,camera,light') { - connected.projector = device - connected.camera = device - connected.light = device - arduino.connect('projector', device, false, () => { - log.info(`Connected to ${device} as PROJECTOR + CAMERA + LIGHT`, 'SERIAL', true, true) - cb() - }) - arduino.alias('camera', device) - arduino.alias('light', device) - } else if (type === 'projector,camera') { - connected.projector = device - connected.camera = device - arduino.connect('projector', device, false, () => { - log.info(`Connected to ${device} as PROJECTOR`, 'SERIAL', true, true) - cb() - }) - arduino.alias('camera', device) - } else { - cb() + + /*await Promise.all(devices.map(async (device) => { + return new Promise( async (resolve, reject) => { + let type + let d + try { + type = await dev.distinguish(device) + } catch (err) { + console.error(err) + return reject(err) } + try { + d = await dev.connectDevice(device, type) + } catch (err) { + console.error(err) + return reject(err) + } + return resolve(d) }) + })*/ + + //done checking devices + + let c = {} + let p = {} + let l = {} + + if (!dev.connected.projector) { + await dev.fakeProjector() + } + p.arduino = dev.connected.projector + if (!dev.connected.camera) { + await dev.fakeCamera() + } + c.arduino = dev.connected.camera + + if (mcopy.settings.camera.intval) { + c.intval = mcopy.settings.camera.intval + console.dir(mcopy.settings.camera) + await delay(1000) + cam.connectIntval(null, { connect : true, url : c.intval }) } - checklist = devices.map(device => { - return next => { - dev.distinguish(device, (err, type) => { - if (err) { - console.error(err) - return next() - } - distinguishCb(device, type, next) - }) - } - }) + if (!dev.connected.light) { + await dev.fakeLight() + } - async.series(checklist, () => { - //done checking devices + l.arduino = dev.connected.light - let c = {} - let p = {} - let l = {} - - if (!connected.projector) { - fakeProjector() - } - p.arduino = connected.projector - if (!connected.camera) { - fakeCamera() - } - c.arduino = connected.camera - - if (mcopy.settings.camera.intval) { - c.intval = mcopy.settings.camera.intval - console.dir(mcopy.settings.camera) - setTimeout(() => { - cam.connectIntval(null, { connect : true, url : c.intval }) - }, 1000) - } - - if (!connected.light) { - fakeLight() - } - l.arduino = connected.light - - dev.ready(p, c, l) - }) -}; + dev.ready(p, c, l) +} dev.remember = function (which, device, type) { let deviceEntry @@ -539,7 +602,7 @@ transfer.listen = function () { }) } */ -var init = function () { +var init = async function () { createWindow() createMenu() @@ -560,9 +623,8 @@ var init = function () { settings.restore() mcopy.settings = settings.all() - setTimeout( () => { - arduino.enumerate(dev.enumerate) - }, 1000) + await delay(1000) + await dev.enumerate() } app.on('ready', init)