From 05af031cbad37eff1a04ced2ea96303fac6a6d77 Mon Sep 17 00:00:00 2001 From: mmcwilliams Date: Tue, 19 May 2020 18:40:19 -0400 Subject: [PATCH] Resolves #19. Resolves #18. Resolves #17. Creates an "advanced" screen with UI for explicitly declaring the number of loops. --- app/package.json | 2 +- app/www/index.html | 29 ++++++++- app/www/static/css/index.css | 34 +++++++++-- app/www/static/js/intval.core.js | 96 ++++++++++++++++++++++++++++- app/www/static/js/intval.mobile.js | 98 ++++++++++++++++++++++++++---- app/www/static/js/intval.pwa.js | 62 +++++++++++++++++++ app/www/static/js/intval.web.js | 51 ++++++++++++++++ 7 files changed, 351 insertions(+), 21 deletions(-) diff --git a/app/package.json b/app/package.json index 5f73e1d..68a8200 100644 --- a/app/package.json +++ b/app/package.json @@ -49,4 +49,4 @@ "android" ] } -} +} \ No newline at end of file diff --git a/app/www/index.html b/app/www/index.html index 3cc8e45..08ba44a 100644 --- a/app/www/index.html +++ b/app/www/index.html @@ -88,6 +88,7 @@ +
v1.0.7 build cfb0a7be
+
diff --git a/app/www/static/css/index.css b/app/www/static/css/index.css index 476a9bb..2e86273 100644 --- a/app/www/static/css/index.css +++ b/app/www/static/css/index.css @@ -237,6 +237,11 @@ button.focus{ .cameraIcon{ background: url(data:image/svg+xml;utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTguMS4xLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDEwMCAxMDAiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDEwMCAxMDA7IiB4bWw6c3BhY2U9InByZXNlcnZlIiB3aWR0aD0iMzJweCIgaGVpZ2h0PSIzMnB4Ij4KPGc+Cgk8Zz4KCQk8cGF0aCBkPSJNNTAsNDBjLTguMjg1LDAtMTUsNi43MTgtMTUsMTVjMCw4LjI4NSw2LjcxNSwxNSwxNSwxNWM4LjI4MywwLDE1LTYuNzE1LDE1LTE1ICAgIEM2NSw0Ni43MTgsNTguMjgzLDQwLDUwLDQweiBNOTAsMjVINzhjLTEuNjUsMC0zLjQyOC0xLjI4LTMuOTQ5LTIuODQ2bC0zLjEwMi05LjMwOUM3MC40MjYsMTEuMjgsNjguNjUsMTAsNjcsMTBIMzMgICAgYy0xLjY1LDAtMy40MjgsMS4yOC0zLjk0OSwyLjg0NmwtMy4xMDIsOS4zMDlDMjUuNDI2LDIzLjcyLDIzLjY1LDI1LDIyLDI1SDEwQzQuNSwyNSwwLDI5LjUsMCwzNXY0NWMwLDUuNSw0LjUsMTAsMTAsMTBoODAgICAgYzUuNSwwLDEwLTQuNSwxMC0xMFYzNUMxMDAsMjkuNSw5NS41LDI1LDkwLDI1eiBNNTAsODBjLTEzLjgwNywwLTI1LTExLjE5My0yNS0yNWMwLTEzLjgwNiwxMS4xOTMtMjUsMjUtMjUgICAgYzEzLjgwNSwwLDI1LDExLjE5NCwyNSwyNUM3NSw2OC44MDcsNjMuODA1LDgwLDUwLDgweiBNODYuNSw0MS45OTNjLTEuOTMyLDAtMy41LTEuNTY2LTMuNS0zLjVjMC0xLjkzMiwxLjU2OC0zLjUsMy41LTMuNSAgICBjMS45MzQsMCwzLjUsMS41NjgsMy41LDMuNUM5MCw0MC40MjcsODguNDMzLDQxLjk5Myw4Ni41LDQxLjk5M3oiIGZpbGw9IiNGRkZGRkYiLz4KCTwvZz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8L3N2Zz4K) no-repeat; } +#advancedIcon > div { + filter: invert(1); + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABmJLR0QA/wD/AP+gvaeTAAAD3UlEQVRoge3Zz28VVRQH8E9LWzRRo9S6wJqYKBASXWgtAmowGv8CDabBjSGGhf8G/sBl8ddSA7oxblAkoDExxhj8sRETjRgMaJUqrkRqacNzce84j/Y93rtzZ6pEvsnkvndnzvecM/fcH+cMV/D/wQj24GfM4LnYd9nhebSWXM/+qxZVxIxg/BZsjb9nmlI21BSxMozOtvUNN6VsVVPEuBWTuB4P4E68jvca1NkIxjGHC/Gai32NoM7QGsS92IZNuE0Y8YF4fxUO4nt8ho9wVJg7/wmMCyvUT5avUr2uH4Vl+eYVt7oNY3gR55WGHY99U7gLa4QJPozR2DeFl4SRKeTOYxo3rqgH2I4z0YgF7MPmCjxbsD9ytPAbHqvJxktiGC8r3+RB3F4D73ocauPdq8Gt4ao2ZX/iyT7ljuLTPp/dGblbeBerE23siSFh/W/htBDr/aJ4y/1iArNKZ2odmVeUTqxPlE11RNRROLM3UbYrHleGU8pIFKjiCGFkzkXZRyvIX4Qx/B7J+p0TS1HVEXhKuZqNVuRAGVI556McR+BwlJ+uSnCLsFEtyFticx3ZgEXMY20VgiIx2pdhBPmOwBsqJmaDyrNTlR27HXU4UiRmpwTbkgWPZxpAPY4M4ETk2dTpgW7ebYvt4UwD6kILR+LvBzs90M2Rydh+UrNBOfg4tkkjsi6232QoHsHutv8vyMvZC1vWXfKpJSg2wZxNaLflidSeDL6xyHEmRWg+CuUU1E5Z7sgvGXyrI8d8p5tJS1kiOnEPdOhLRccVsJsjRS3q2orKxoTdeCleq8gH18X2jxShrwXPq5x2b8KxKH9SKDCcFuZHzmSfiJxfpQi9HYWmEpW1O3Es/q8LT0Tetzrd7BZaX8b2/gRFY/gAd+BbPIJfE+R7obDlixShzcojSj8TtMmREG34IfJP9nj2IgwKsV1U03vhc805Afcp51zHKOoWWheEehM83YeiRSEcH1ZvOBUobNgv2JaEcWHzWZBecKgTG4UXNadiYkVZjDtUk1FV8L7MVJdw1ipKoztrMCoVu6LuWdyQS9ZeDprIJUvApBBOLTXWg4sQm7Uy82WDsGhkh9RSDOEdpTNNjsyk0okDGihmX6105pxQPKsbu5ThdCDqbARDwkeaIr84IoRBLjYqV6cinJr84vwPtiuHf1GoO22Vlm8MCDv2m5GjCNsV+dDTjjVCpfwv5Zs8IZRZd+BuYfkeideoMLd24FXl2aklhNO0GpbYHKzFMzqntr2uk0JuX3nHLlBH6llgEPfgIeHNbxC+1l4T758VqpffCUfxD4XzWfLZ6QouB/wNDUkks50hozcAAAAASUVORK5CYII=) no-repeat; + background-size: 33px 33px; +} button i { display: block; @@ -255,8 +260,8 @@ footer{ } footer .icon { - /*width: 33.33%;*/ - width: 50%; + width: 33.33%; + /*width: 50%;*/ height: 50px; float: left; box-sizing: border-box; @@ -264,8 +269,8 @@ footer .icon { } body.mobile footer .icon{ - /*width: 25%;*/ - width: 33.33%; + width: 25%; + /*width: 33.33%;*/ } footer .icon:last-child{ @@ -440,4 +445,25 @@ body.mobile footer{ line-height: 100vh; text-shadow: 1px 1px 0px #999; font-size: 24px; +} + +#version{ + bottom: 70px; + position: absolute; + color: #666; +} + +#stats{ + margin: 10px 0; + font-size: 18px; +} + +#stats label { + color: #666; + width: 100px; + display: inline-block; +} + +#stats span{ + color: #fff; } \ No newline at end of file diff --git a/app/www/static/js/intval.core.js b/app/www/static/js/intval.core.js index 8211733..7f7b942 100644 --- a/app/www/static/js/intval.core.js +++ b/app/www/static/js/intval.core.js @@ -13,7 +13,8 @@ const STATE = { scale : 'ms', delayScale : 'ms', counter : 0, - sequence : false + sequence : false, + advanced : 0 }; //functions @@ -27,6 +28,7 @@ window.sequence = null; window.reset = null; window.restart = null; window.update = null; +window.advanced = null; //ms var shutter = function (exposure) { @@ -183,6 +185,8 @@ var setState = function (res) { document.getElementById('delayScale').value = delayScale; setDelayScale(); + calcStats(); + if (res.sequence == true) { if (mobile.ble) mobile.ble.active = true; if (pwa.wble) pwa.wble.active = true; @@ -195,18 +199,28 @@ var setState = function (res) { }; var seqState = function (state) { - const elem = document.getElementById('seq') + const elem = document.getElementById('seq'); + const advancedElem = document.getElementById('run'); + if (state) { if (!elem.classList.contains('focus')) { elem.classList.add('focus'); } + if (!advancedElem.classList.contains('focus')) { + advancedElem.classList.add('focus'); + } elem.innerHTML = 'STOP SEQUENCE'; + advancedElem.innerHTML = 'STOP'; STATE.sequence = true; } else { if (elem.classList.contains('focus')) { elem.classList.remove('focus'); } + if (advancedElem.classList.contains('focus')) { + advancedElem.classList.remove('focus'); + } elem.innerHTML = 'START SEQUENCE'; + advancedElem.innerHTML = 'RUN'; STATE.sequence = false; } }; @@ -232,11 +246,87 @@ var cameraPage = function () { document.getElementById('camera').classList.add('selected'); document.getElementById('cameraIcon').classList.add('selected'); }; +var advancedPage = function () { + unsetPages(); + document.getElementById('advanced').classList.add('selected'); + document.getElementById('advancedIcon').classList.add('selected'); +}; var isNumeric = function (n) { return !isNaN(parseFloat(n)) && isFinite(n); }; +var calcStats = function () { + const extraFwd = BOLEX.expected; + const extraBwd = BOLEX.expected + 150; + const total = parseInt(document.getElementById('len').value); + const multiple = parseInt(document.getElementById('multiple').value); + const filmTime = (total * multiple) / 24; + const delays = total - 1; + const frameEnd = STATE.counter + (STATE.dir ? total * multiple : -total * multiple); + + let exp; + let realTime; + let realTimePartial; + let realTimeDisplay; + let filmTimeDisplay; + + if (STATE.exposure > BOLEX.expected) { + exp = STATE.exposure + (STATE.dir ? extraFwd : extraBwd); + } else { + exp = BOLEX.expected; + } + realTime = (exp * total * multiple) + (delays * STATE.delay); + + STATE.advanced = realTime; + + if (realTime < 60 * 1000) { //1min + realTimeDisplay = Math.floor(realTime * 100) / 100000; + realTimeDisplay += ' sec'; + } else if (realTime >= 60 * 1000 && realTime < 60 * 60 * 1000) { //1hr + realTimePartial = Math.floor(realTime * 100) / (60 * 100000); + realTimeDisplay = Math.floor(realTimePartial); + realTimeDisplay += ' min'; + if (realTimePartial > Math.floor(realTimePartial)) { + realTimeDisplay += ' ' + Math.round((realTimePartial - Math.floor(realTimePartial) ) * 60); + realTimeDisplay += ' sec' + } + } else if (realTime >= 60 * 60 * 1000 && realTime < 24 * 60 * 60 * 1000) { //1day + realTimePartial = Math.floor(realTime * 100) / (60 * 60 * 100000); + realTimeDisplay = Math.floor(realTimePartial); + realTimeDisplay += ' hr'; + if (realTimePartial > Math.floor(realTimePartial)) { + realTimeDisplay += ' ' + Math.round((realTimePartial - Math.floor(realTimePartial) ) * 60); + realTimeDisplay += ' min' + } + } else if (realTime >= 24 * 60 * 60 * 1000 && realTime < 24 * 60 * 60 * 1000) { //1day + realTimePartial = Math.floor(realTime * 100) / (60 * 60 * 100000); + realTimeDisplay = Math.floor(realTimePartial); + realTimeDisplay += ' day'; + if (realTimePartial > Math.floor(realTimePartial)) { + realTimeDisplay += ' ' + Math.round((realTimePartial - Math.floor(realTimePartial) ) * 24); + realTimeDisplay += ' hr' + } + } + + if (filmTime < 60 * 1000) { //1min + filmTimeDisplay = Math.floor(filmTime * 100) / 100; + filmTimeDisplay += ' sec'; + } else if (filmTime >= 60 * 1000) { + filmTimePartial = Math.floor(filmTime * 100) / (60 * 100000); + filmTimeDisplay = Math.floor(filmTimePartial); + filmTimeDisplay += ' min'; + if (filmTimePartial > Math.floor(filmTimePartial)) { + filmTimeDisplay += ' ' + Math.round((filmTimePartial - Math.floor(filmTimePartial) ) * 60); + filmTimeDisplay += ' sec' + } + } + + document.getElementById('realTime').innerHTML = realTimeDisplay; + document.getElementById('filmTime').innerHTML = filmTimeDisplay; + document.getElementById('frameEnd').innerHTML = frameEnd; +}; + var UI = {}; UI.overlay = { @@ -319,4 +409,6 @@ var init = function () { document.querySelector('.fstop').oninput = function () { BOLEX.fstop = parseFloat(this.value); }; + document.getElementById('len').oninput = calcStats; + document.getElementById('multiple').oninput = calcStats; }; \ No newline at end of file diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js index df6d333..53af62a 100644 --- a/app/www/static/js/intval.mobile.js +++ b/app/www/static/js/intval.mobile.js @@ -151,6 +151,7 @@ mobile.init = function () { window.getWifi = mobile.getWifi; window.setWifi = mobile.setWifi; window.editWifi = mobile.editWifi; + window.advanced = mobile.advanced; //show ble-specific fields in settings for (let i of bleInputs) { @@ -173,6 +174,7 @@ mobile.getState = function () { mobile.stateSuccess, mobile.ble.onError); }; + mobile.stateSuccess = function (data) { let str = bytesToString(data); let res = JSON.parse(str); @@ -205,13 +207,13 @@ mobile.frameSuccess = function () { console.log('Frame finished, getting state.'); mobile.ble.active = false; document.getElementById('frame').classList.remove('focus'); - mobile.getState(); + getState(); } else { setTimeout(() => { console.log('Frame finished, getting state.'); mobile.ble.active = false; document.getElementById('frame').classList.remove('focus'); - mobile.getState(); + getState(); }, STATE.exposure + 500) } } @@ -230,7 +232,7 @@ mobile.setDir = function () { }; mobile.dirSuccess = function () { console.log('Set direction'); - mobile.getState(); + getState(); setTimeout(() => { setDirLabel(STATE.dir); }, 50); @@ -255,7 +257,7 @@ mobile.setExposure = function () { }; mobile.exposureSuccess = function () { console.log('Set exposure'); - mobile.getState(); + getState(); }; mobile.setDelay = function () { @@ -275,7 +277,7 @@ mobile.setDelay = function () { mobile.delaySuccess = function () { console.log('Set delay'); - mobile.getState(); + getState(); }; mobile.setCounter = function () { @@ -307,7 +309,7 @@ mobile.setCounter = function () { mobile.counterSuccess = function () { console.log('Set counter'); - mobile.getState(); + getState(); }; mobile.sequence = function () { @@ -334,18 +336,75 @@ mobile.sequence = function () { mobile.sequenceSuccess = function () { console.log('Sequence state changed'); - mobile.getState(); + getState(); setTimeout(() => { if (STATE.sequence) { - mobile.ble.active = true; seqState(true); } else { - mobile.ble.active = false; seqState(false); } - }, 20); + }, 42); }; +mobile.advanced = function () { + const len = parseInt(document.getElementById('len').value); + const multiple = parseInt(document.getElementById('multiple').value); + const opts = { + type : 'sequence', + len, + multiple + }; + + if (!opts.len) { + return mobile.alert('You must set a total frame count.'); + } + + if (!opts.multiple) { + return mobile.alert('You must set a frame multiple value.'); + } + const elem = document.getElementById('run'); + + if (!mobile.ble.connected) { + return mobile.alert('Not connected to an INTVAL3 device.'); + } + ble.write(mobile.ble.device.id, + mobile.ble.SERVICE_ID, + mobile.ble.CHAR_ID, + stringToBytes(JSON.stringify(opts)), //check length? + mobile.advanceSuccess, + mobile.ble.onError); + + if (!elem.classList.contains('focus')) { + elem.classList.add('focus'); + } + + mobile.ble.active = true; +}; + +mobile.advancedSuccess = function () { + console.log('Sequence state changed'); + getState(); + setTimeout(() => { + if (STATE.sequence) { + seqState(true); + } else { + seqState(false); + } + document.getElementById('seq').blur(); + document.getElementById('run').blur(); + }, 42); + setTimeout(() => { + console.log('Sequence complete'); + getState(); + setTimeout(() => { + if (STATE.sequence) { + seqState(true); + } else { + seqState(false); + } + }, 42); + }, STATE.advanced + 1000); +}; //retreive object with list of available Wifi APs, //and state of current connection, if available @@ -471,6 +530,23 @@ mobile.setWifiSuccess = function () { console.log('Set new wifi credentials'); setTimeout(mobile.getWifi, 100); }; + +mobile.getInfo = function () { + const opts = { + type : 'info' + }; + + ble.write(mobile.ble.device.id, + mobile.ble.SERVICE_ID, + mobile.ble.CHAR_ID, + stringToBytes(JSON.stringify(opts)), //check length? + mobile.dirSuccess, + mobile.ble.onError); +}; +mobile.infoSuccess = function () { + console.dir() +}; + mobile.exif = {} mobile.getCamera = function () { @@ -671,7 +747,7 @@ mobile.reset = function () { mobile.resetSuccess = function () { console.log('Reset to default settings'); setTimeout(() => { - mobile.getState(); + getState(); }, 100) }; diff --git a/app/www/static/js/intval.pwa.js b/app/www/static/js/intval.pwa.js index f6cbbb0..a0f372a 100644 --- a/app/www/static/js/intval.pwa.js +++ b/app/www/static/js/intval.pwa.js @@ -263,6 +263,7 @@ pwa.init = function () { window.getWifi = pwa.getWifi; window.setWifi = pwa.setWifi; window.editWifi = pwa.editWifi; + window.advanced = pwa.advanced; //show ble-specific fields in settings for (let i of bleInputs) { @@ -481,6 +482,67 @@ pwa.sequenceSuccess = function () { }; +pwa.advanced = async function () { + const len = parseInt(document.getElementById('len').value); + const multiple = parseInt(document.getElementById('multiple').value); + const opts = { + type : 'sequence', + len, + multiple + }; + + if (!opts.len) { + return pwa.alert('You must set a total frame count.'); + } + + if (!opts.multiple) { + return pwa.alert('You must set a frame multiple value.'); + } + const elem = document.getElementById('run'); + if (!pwa.wble.connected) { + return pwa.alert('Not connected to an INTVAL3 device.'); + } + try { + await pwa.wble.write(pwa.wble.CHAR_ID, opts); + } catch (err) { + console.error(err); + pwa.wble.onError(err.message); + return false; + } + + pwa.advancedSuccess(); + + if (!elem.classList.contains('focus')) { + elem.classList.add('focus'); + } + + pwa.wble.active = true; +}; + +pwa.advancedSuccess = function () { + console.log('Sequence state changed'); + pwa.getState(); + setTimeout(() => { + if (STATE.sequence) { + seqState(true); + } else { + seqState(false); + } + document.getElementById('seq').blur(); + }, 42); + setTimeout(() => { + console.log('Sequence complete'); + getState(); + setTimeout(() => { + if (STATE.sequence) { + seqState(true); + } else { + seqState(false); + } + }, 42); + }, STATE.advanced + 1000); +}; + //retreive object with list of available Wifi APs, //and state of current connection, if available pwa.getWifi = async function () { diff --git a/app/www/static/js/intval.web.js b/app/www/static/js/intval.web.js index 1f294ce..06f40fc 100644 --- a/app/www/static/js/intval.web.js +++ b/app/www/static/js/intval.web.js @@ -160,6 +160,56 @@ web.sequenceSuccess = function (res) { mobile.getState(); } }; +web.advanced = function () { + const len = parseInt(document.getElementById('len').value); + const multiple = parseInt(document.getElementById('multiple').value); + const params = { + len, + multiple + } + const opts = { + method : 'POST', + headers : web._header, + body : JSON.stringify(params) + } + if (!params.len) { + return alert('You must set a total frame count.'); + } + + if (!params.multiple) { + return alert('You must set a frame multiple value.'); + } + fetch('/sequence', opts) + .then(web.useJson) + .then(web.advancedSuccess) + .catch(err => { + console.error('Error getting /sequence'); + console.error(err); + }) +}; +web.advancedSuccess = function (res) { + if (res.started && res.started != false) { + STATE.sequence = true; + document.getElementById('seq').focus(); + seqState(true); + } else if (res.stopped) { + STATE.sequence = false; + document.getElementById('seq').blur(); + seqState(false); + getState(); + } + setTimeout(() => { + console.log('Sequence complete'); + getState(); + setTimeout(() => { + if (STATE.sequence) { + seqState(true); + } else { + seqState(false); + } + }, 42); + }, STATE.advanced + 1000); +}; web.reset = function () { const opts = { method : 'POST', @@ -228,5 +278,6 @@ web.init = function () { window.reset = web.reset; window.restart = web.restart; window.update = web.update; + window.advanced = web.advanced; console.log('started web') }; \ No newline at end of file