diff options
Diffstat (limited to 'web/main.js')
| -rw-r--r-- | web/main.js | 156 |
1 files changed, 134 insertions, 22 deletions
diff --git a/web/main.js b/web/main.js index b93b2da..6d29a50 100644 --- a/web/main.js +++ b/web/main.js @@ -1,5 +1,5 @@ import { ADDR, FUNC, ARG, Message } from './protocol.js'; -import { hex, concat } from './utils.js'; +import { hex, concat, hash } from './utils.js'; import * as plot from './plot.js'; import * as PROTOCOL from './protocol.js'; import * as UTILS from './utils.js'; @@ -26,6 +26,60 @@ window.send8 = (func, ...data) => { return PORT.write(msg.buf); }; +const samplePlot = (data, interval=4) => { + if (data.length < 2) return data; + data = data.slice(); + + const result = []; + + let i = 0; + let prev; + while (data.length) { + while (data.length && data[0][0] <= i) { + prev = data.shift(); + } + + const next = data[0]; + if (!next) { + result.push([ i, prev[1] ]); + break; + } + + const dtemp = next[1] - prev[1]; + const dtime = next[0] - prev[0]; + const t = (i - prev[0]) / dtime; + result.push([ i, Math.round(prev[1] + t * dtemp) ]); + + i += interval; + } + + return result; +}; + +const unsamplePlot = (data, tolerance_interval=4) => { + if (data.length < 2) return data; + + const result = []; + result.push(data[0]); + + const tolerance = 1.1 / tolerance_interval; + + for (let i = 1; i < data.length - 1; i++) { + const prev = data[i - 1]; + const curr = data[i]; + const next = data[i + 1]; + + const left = (curr[1] - prev[1]) / (curr[0] - prev[0]); + const right = (next[1] - curr[1]) / (next[0] - curr[0]); + + if (Math.abs(left - right) < tolerance) continue; + result.push(curr); + } + + result.push(data[data.length - 1]); + return result; +}; + let PORT = null; // { write(buf), disconnect() } | null let STATUS = null; // null | 'connecting' | 'idle' | 'manual' | 'running' let START_TIME = null; @@ -48,12 +102,11 @@ plot.addPlot('temp3', { color: 'sienna' }); plot.addPlot('tempavg', { color: 'rebeccapurple' }); const elements = [ - 'temps_chamber', 'temps_controller', - 'connect', 'status', + 'connect', 'status', 'temps_chamber', 'temps_controller', 'profile', 'start', 'read', 'write', - 'profile_load', 'profile_save', 'profile_create', 'profile_show', 'profile_edit', - 'paste_load', 'paste_save', 'paste_create', 'paste_show', 'paste_edit', 'heat', 'heat_mode', 'cool', 'cool_mode', + 'profile_load', 'profile_save', 'profile_delete', 'profile_show', 'profile_edit', 'profile_clear', 'profile_stored', + 'paste_load', 'paste_save', 'paste_delete', 'paste_show', 'paste_edit', 'paste_clear', 'paste_stored', ]; const el = Object.fromEntries(elements.map(id => [id, document.getElementById(id)])); @@ -101,6 +154,28 @@ const updateUI = () => { plot.update(); }; +window.onstorage = () => { + const profile = el.profile_stored.value; + const paste = el.paste_stored.value; + + el.profile_stored.innerHTML = '<option value="" class="new">(new profile)</option>'; + el.paste_stored.innerHTML = '<option value="" class="new">(new paste)</option>'; + + for (const key of Object.keys(window.localStorage)) { + if (key.startsWith('profile-')) { + const data = JSON.parse(window.localStorage[key]); + el.profile_stored.add(new Option(data.name, key)); + } else if (key.startsWith('paste-')) { + const data = JSON.parse(window.localStorage[key]); + el.paste_stored.add(new Option(data.name, key)); + } + } + + el.profile_stored.value = profile; + el.paste_stored.value = paste; +}; + +window.onstorage(); updateUI(); const onMessage = async (msg) => { @@ -312,20 +387,57 @@ if (window.location.hash !== '#ws') { console.log('opened websocket'); }; } -el.profile_show.onclick = () => { - plot.PLOTS.profile.visible = !plot.PLOTS.profile.visible; - updateUI(); -}; -el.paste_show.onclick = () => { - plot.PLOTS.pase.visible = !plot.PLOTS.pase.visible; - updateUI(); -}; -el.profile_edit.onclick = () => { - plot.toggleActive('profile'); - updateUI(); -}; -el.paste_edit.onclick = () => { - plot.toggleActive('paste'); - updateUI(); -}; -}; + +for (const p of ['profile', 'paste']) { + el[p + '_show'].onclick = () => { + plot.PLOTS[p].visible = !plot.PLOTS[p].visible; + updateUI(); + }; + el[p + '_edit'].onclick = () => { + plot.toggleActive(p); + updateUI(); + }; + el[p + '_clear'].onclick = () => { + plot.PLOTS[p].data = [[0, 24]]; + updateUI(); + }; + + el[p + '_load'].onclick = () => { + const key = el[p + '_stored'].value; + let data = window.localStorage[key]; + if (!data) return; + + plot.PLOTS[p].data = JSON.parse(data).data; + updateUI(); + }; + + el[p + '_save'].onclick = () => { + let key = el[p + '_stored'].value; + let name; + if (key === '') { + name = window.prompt('please enter the new profile name'); + if (!name) return; + } else { + name = JSON.parse(window.localStorage[key]).name; + delete window.localStorage[key]; + } + + const data = plot.PLOTS[p].data; + if (p === 'profile') { + const sampled = new Uint8Array(samplePlot(data).map(d => Math.round(d[1]))); + key = `profile-${hash(sampled)}`; + } else { + key = `paste-${name}`; + } + + window.localStorage[key] = JSON.stringify({ name, data }); + window.onstorage(); + el[p + '_stored'].value = key; + }; + + el[p + '_delete'].onclick = () => { + const key = el[p + '_stored'].value; + delete window.localStorage[key]; + window.onstorage(); + }; +} |
