diff --git a/ui/bootstrap-5.3.3-dist/css/bootstrap.css b/ui/bootstrap-5.3.3-dist/css/bootstrap.css index 0cc4521..5505a00 100644 --- a/ui/bootstrap-5.3.3-dist/css/bootstrap.css +++ b/ui/bootstrap-5.3.3-dist/css/bootstrap.css @@ -12118,4 +12118,27 @@ input:checked + .slider:before { .slider.round:before { border-radius: 50%; -} \ No newline at end of file +} + +#map { + max-width: 100%; + max-height: 100%; +} + +.pico canvas { + display: inline-block; +} + +.pico :where(audio, canvas, iframe, img, svg, video) { + vertical-align: middle; +} + +*, ::after, ::before { + box-sizing: border-box; + background-repeat: no-repeat; +} + +canvas { + overflow-clip-margin: content-box; + overflow: clip; +} \ No newline at end of file diff --git a/ui/bootstrap-5.3.3-dist/js/bootstrap.js b/ui/bootstrap-5.3.3-dist/js/bootstrap.js index 9ed52e8..b87a22e 100644 --- a/ui/bootstrap-5.3.3-dist/js/bootstrap.js +++ b/ui/bootstrap-5.3.3-dist/js/bootstrap.js @@ -4472,26 +4472,39 @@ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ + + +const motors_buf = new Float32Array(2); +const motors_ml = document.getElementById("motors_ml") +const motors_mr = document.getElementById("motors_mr") +const motors_stop = document.getElementById("motors_stop") +const motors_joystick = document.getElementById('motors_joystick'); +const motors_canvas = document.getElementById('motors_canvas'); + +let motors_ws = null; + +const send_motors = (vl, vr) => { + /*if (motors_ws && motors_ws.readyState == WebSocket.OPEN) { + const lim = v => Math.max(-1, Math.min(v, 1)); + motors_buf[0] = lim(vl); + motors_buf[1] = lim(vr); + motors_ml.value = vl * 100; + motors_mr.value = vr * 100; + motors_ws.send(motors_buf); + } else if (!motors_ws || motors_ws.readyState != WebSocket.CONNECTING) { + motors_ws = new WebSocket('ws://' + document.location.host + '/motors.ws'); + motors_ws.addEventListener("open", () => send_motors(vl, vr)); + }*/ +}; + +const update_motors = () => { + const vl = motors_ml.value / 100; + const vr = motors_mr.value / 100; + send_motors(vl, vr); +} + - const index_umd = { - Alert, - Button, - Carousel, - Collapse, - Dropdown, - Modal, - Offcanvas, - Popover, - ScrollSpy, - Tab, - Toast, - Tooltip - }; - document.getElementById("colorLed").onchange=function(){ - // do whatever you want with value - console.log(this.value); - }; const btnsz = 40; let positionX = 0, positionY = 0; @@ -4503,20 +4516,19 @@ const h = motors_joystick.height; ctx.clearRect(0, 0, w, h); - ctx.fillStyle = "#00aa0040"; + ctx.fillStyle = "#00aaff40"; ctx.beginPath(); ctx.arc(w/2, h/2, w/2-btnsz, 0, 2 * Math.PI); ctx.fill(); ctx.lineWidth = 2; - ctx.strokeStyle = "#009900"; - ctx.fillStyle = "#00aa00"; + ctx.strokeStyle = "#0099ee"; + ctx.fillStyle = "#00aaff"; ctx.beginPath(); ctx.arc(w/2 + positionX, h/2 + positionY, btnsz, 0, 2 * Math.PI); ctx.fill(); ctx.stroke(); } - const updatePosition = e => { const clientX = e.clientX || e.touches[0].clientX; const clientY = e.clientY || e.touches[0].clientY; @@ -4531,11 +4543,9 @@ } positionX = vx * kw; positionY = vy * kw; - console.log("PositionX : "+ Math.round(positionX)) - console.log("PositionY : "+ Math.round(positionY)) vy *= -1; if (performance.now() - lastsend > 250) { - //send_motors(vy+vx, vy-vx); + send_motors(vy+vx, vy-vx); lastsend = performance.now(); } joystick_draw(); @@ -4552,7 +4562,7 @@ }; const onup = () => { if (clicked) { - //send_motors(0, 0); + send_motors(0, 0); motors_joystick.style.cursor = '' clicked = false; positionX = positionY = 0; @@ -4568,10 +4578,70 @@ motors_joystick.addEventListener('touchmove', onmove); motors_joystick.addEventListener('touchend', onup); motors_joystick.addEventListener('touchcancel', onup); + + //const text_color = getComputedStyle(motors_canvas).color; + document.body.addEventListener('h:infos:motors', e => { + const ctx = motors_canvas.getContext("2d"); + const w = motors_canvas.width; + const h = motors_canvas.height; + const m = 5; + const ml = e.detail[0]; + const mr = e.detail[1]; + ctx.clearRect(0, 0, w, h); + ctx.lineWidth = 3; + ctx.strokeStyle = "#808080" + ctx.fillStyle = "#008000" + + ctx.beginPath(); + ctx.rect(m, m, w-2*m, (h/2-m)); + ctx.stroke(); + + ctx.beginPath(); + ctx.rect(m, (h/2-m)+m, w-2*m, (h/2-m)); + ctx.stroke(); + + ctx.beginPath(); + if (ml > 0) + ctx.rect(w/2, m+1, ml*(w/2-m), (h/2-m)-2); + else + ctx.rect(w/2+ml*(w/2-m), m+1, -ml*(w/2-m), (h/2-m)-2); + ctx.fill(); + + ctx.beginPath(); + if (ml > 0) + ctx.rect(w/2, (h/2-m)+m+1, mr*(w/2-m), (h/2-m)-2); + else + ctx.rect(w/2+mr*(w/2-m), (h/2-m)+m+1, -mr*(w/2-m), (h/2-m)-2); + ctx.fill(); + }); + + /*document.body.addEventListener('h:section:statechanged', e => { + infos_mask(INFOS_MOTORS, e.detail['on']); + }); + infos_mask(INFOS_MOTORS, true);*/ + + document.getElementById("colorLed").value = "#ffffff" + document.getElementById("colorLed").onchange=function(){ + // do whatever you want with value + console.log(this.value); + }; + + const index_umd = { + Alert, + Button, + Carousel, + Collapse, + Dropdown, + Modal, + Offcanvas, + Popover, + ScrollSpy, + Tab, + Toast, + Tooltip + }; return index_umd; - - })); //# sourceMappingURL=bootstrap.js.map diff --git a/ui/bootstrap-5.3.3-dist/js/infos.js b/ui/bootstrap-5.3.3-dist/js/infos.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/ui/bootstrap-5.3.3-dist/js/infos.js @@ -0,0 +1 @@ + diff --git a/ui/bootstrap-5.3.3-dist/js/main.js b/ui/bootstrap-5.3.3-dist/js/main.js new file mode 100644 index 0000000..961963f --- /dev/null +++ b/ui/bootstrap-5.3.3-dist/js/main.js @@ -0,0 +1,482 @@ +//Infos + +const INFOS_POSITION = 0x01; +const INFOS_LED = 0x02; +const INFOS_MOTORS = 0x04; +const INFOS_WHEELS = 0x08; +const INFOS_SPEED = 0x10; +const INFOS_RANGEFINDER = 0x20; + +let infos_ws = null; +let period_ms = 500; +let mask = 0; + +function on_msg(e) { + const view = new DataView(e.data); + let pos = 0; + + const mask = view.getUint8(pos); + pos++; + + if (mask & INFOS_POSITION) { + const position_x = view.getFloat32(pos + 0); + const position_y = view.getFloat32(pos + 4); + const position_a = view.getFloat32(pos + 8); + pos += 3*4; + document.body.dispatchEvent(new CustomEvent('h:infos:position', { + 'detail': [position_x, position_y, position_a] + })); + } + + if (mask & INFOS_LED) { + const led_r = view.getUint8(pos + 0); + const led_g = view.getUint8(pos + 1); + const led_b = view.getUint8(pos + 2); + pos += 3; + + document.body.dispatchEvent(new CustomEvent('h:infos:led', { + 'detail': [led_r, led_g, led_b] + })); + } + + if (mask & INFOS_MOTORS) { + const ml = view.getFloat32(pos + 0); + const mr = view.getFloat32(pos + 4); + pos += 2*4; + + document.body.dispatchEvent(new CustomEvent('h:infos:motors', { + 'detail': [ml, mr] + })); + } + + if (mask & INFOS_WHEELS) { + const wl = view.getInt16(pos + 0); + const wr = view.getInt16(pos + 2); + const tl = view.getInt16(pos + 4); + const tr = view.getInt16(pos + 6); + pos += 4*2; + + document.body.dispatchEvent(new CustomEvent('h:infos:wheels', { + 'detail': [wl, wr, tl, tr] + })); + } + + if (mask & INFOS_SPEED) { + const w = view.getFloat32(pos + 0); + const v = view.getFloat32(pos + 4); + pos += 4*2; + + document.body.dispatchEvent(new CustomEvent('h:infos:speed', { + 'detail': [w, v] + })); + } + + if (mask & INFOS_RANGEFINDER) { + const d = view.getUint16(pos + 0); + pos += 2; + + console.log(d); + + document.body.dispatchEvent(new CustomEvent('h:infos:rangefinder', { + 'detail': [d] + })); + } +} + +function send_mask() { + /*if (mask == 0) { + if (infos_ws) { + infos_ws.close(); + infos_ws = null; + } + } else { + if (infos_ws) { + if (infos_ws.readyState == WebSocket.OPEN) { + const buf = new Uint8Array(2); + buf[0] = period_ms / 10; + buf[1] = mask; + infos_ws.send(buf); + } + } else { + infos_ws = new WebSocket('ws://' + document.location.host + '/infos.ws'); + infos_ws.binaryType = "arraybuffer"; + infos_ws.addEventListener("open", send_mask); + infos_ws.addEventListener("message", on_msg); + } + }*/ +} + +function infos_mask(v, on) { + if (on) + mask |= v; + else + mask &= ~v; + send_mask(); +} + + + + +//-------------------------------- + +//main +document.body.innerHTML = ` +
+ +

MinHAUMtaure

+
+`; + +const sections_opened = new Set(); +if (sessionStorage.getItem('sections_opened')) { + for (const s of sessionStorage.getItem('sections_opened').split('\n')) + sections_opened.add(s); +} + +const sections = [ + { 'id': 'led', 'title': 'Led' }, + { 'id': 'motors', 'title': 'Moteurs' }, + { 'id': 'wheels', 'title': 'Retour roues' }, + { 'id': 'position', 'title': 'Position' }, + { 'id': 'turtle', 'title': 'Turtle' }, + { 'id': 'rangefinder', 'title': 'Télémètre' }, + { 'id': 'config', 'title': 'Config' }, + { 'id': 'wlan', 'title': 'Wifi' }, +]; +const ul_header_menu = document.getElementById('ul_header_menu'); +for (const d of sections) { + const s = document.createElement('section'); + const id = d['id']; + const title = d['title']; + s.id = 'section_' + id; + document.body.append(s); + + const li = document.createElement('li'); + ul_header_menu.append(li); + const label = document.createElement('label'); + li.append(label);type="module" + const checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; + checkbox.checked = false; + label.append(checkbox, title); + + const h2 = document.createElement('h2'); + const b_spans = document.createElement('b'); + const span_fs = document.createElement('span'); + span_fs.innerText = "⛶"; + const span_close = document.createElement('span'); + span_close.innerText = "×"; + b_spans.append(span_fs, span_close); + h2.append(title, b_spans); + s.insertBefore(h2, s.firstChild); + s.classList.add('pico'); + span_close.addEventListener('click', () => { + if (document.fullscreenElement != null) + document.exitFullscreen(); + toggle(false); + }); + span_fs.addEventListener('click', () => { + if (document.fullscreenElement == null) + s.requestFullscreen(); + else + document.exitFullscreen(); + }); + + const toggle = on => { + s.classList.toggle('opened', on); + checkbox.checked = on; + if (on) + sections_opened.add(id); + else + sections_opened.delete(id); + sessionStorage.setItem('sections_opened', Array.from(sections_opened).join('\n')); + if (on) + import('./' + id + '.js'); + document.body.dispatchEvent(new CustomEvent('h:section:statechanged', { + 'detail': { + 'id': id, + 'on': on, + 'section': s + } + })); + } + checkbox.addEventListener('click', e => toggle(e.target.checked)); + if (sections_opened.has(id)) toggle(true); +} + +async function form_post(form) { + const data = new URLSearchParams(); + for (const pair of new FormData(form)) { + data.append(pair[0], pair[1]); + } + return await fetch(form.action, { + method: 'post', + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + body: data + }); +} + +//-------------------------------- + +//motors + +const section = document.getElementById('section_motors'); +section.insertAdjacentHTML('beforeend', ` +
+

+

+
+
+ + +

+
+`); + +const motors_buf = new Float32Array(2); +const motors_ml = document.getElementById("motors_ml") +const motors_mr = document.getElementById("motors_mr") +const motors_stop = document.getElementById("motors_stop") +const motors_joystick = document.getElementById('motors_joystick'); +const motors_canvas = document.getElementById('motors_canvas'); + +let motors_ws = null; + +const send_motors = (vl, vr) => { + /*if (motors_ws && motors_ws.readyState == WebSocket.OPEN) { + const lim = v => Math.max(-1, Math.min(v, 1)); + motors_buf[0] = lim(vl); + motors_buf[1] = lim(vr); + motors_ml.value = vl * 100; + motors_mr.value = vr * 100; + motors_ws.send(motors_buf); + } else if (!motors_ws || motors_ws.readyState != WebSocket.CONNECTING) { + motors_ws = new WebSocket('ws://' + document.location.host + '/motors.ws'); + motors_ws.addEventListener("open", () => send_motors(vl, vr)); + }*/ +}; + +const update_motors = () => { + const vl = motors_ml.value / 100; + const vr = motors_mr.value / 100; + send_motors(vl, vr); +} + +motors_ml.addEventListener('change', update_motors); +motors_mr.addEventListener('change', update_motors); +motors_stop.addEventListener('click', e => { + e.preventDefault(); + motors_ml.value = 0; + motors_mr.value = 0; + update_motors(); +}); + +const btnsz = 40; +let positionX = 0, positionY = 0; +let clicked = false; +let lastsend = performance.now(); +const joystick_draw = () => { + const ctx = motors_joystick.getContext("2d"); + const w = motors_joystick.width; + const h = motors_joystick.height; + ctx.clearRect(0, 0, w, h); + + ctx.fillStyle = "#00aaff40"; + ctx.beginPath(); + ctx.arc(w/2, h/2, w/2-btnsz, 0, 2 * Math.PI); + ctx.fill(); + + ctx.lineWidth = 2; + ctx.strokeStyle = "#0099ee"; + ctx.fillStyle = "#00aaff"; + ctx.beginPath(); + ctx.arc(w/2 + positionX, h/2 + positionY, btnsz, 0, 2 * Math.PI); + ctx.fill(); + ctx.stroke(); +} +const updatePosition = e => { + const clientX = e.clientX || e.touches[0].clientX; + const clientY = e.clientY || e.touches[0].clientY; + const r = motors_joystick.getBoundingClientRect(); + const kw = r.width/2 - btnsz; + let vx = Math.max(-1, Math.min((clientX - r.left - r.width/2) / kw, 1)); + let vy = Math.max(-1, Math.min((clientY - r.top - r.height/2) / kw, 1)); + if (vx*vx + vy*vy > 1) { + const a = Math.atan2(vy, vx); + vx = Math.cos(a); + vy = Math.sin(a); + } + positionX = vx * kw; + positionY = vy * kw; + vy *= -1; + if (performance.now() - lastsend > 250) { + send_motors(vy+vx, vy-vx); + lastsend = performance.now(); + } + joystick_draw(); +} +const ondown = e => { + clicked = true; + motors_joystick.style.cursor = 'none' + updatePosition(e); +}; +const onmove = e => { + if (clicked) { + updatePosition(e); + } +}; +const onup = () => { + if (clicked) { + send_motors(0, 0); + motors_joystick.style.cursor = '' + clicked = false; + positionX = positionY = 0; + joystick_draw(); + } +}; +joystick_draw(); +motors_joystick.addEventListener('mousedown', ondown); +motors_joystick.addEventListener('mousemove', onmove); +motors_joystick.addEventListener('mouseup', onup); +motors_joystick.addEventListener('mouseleave', onup); +motors_joystick.addEventListener('touchstart', ondown); +motors_joystick.addEventListener('touchmove', onmove); +motors_joystick.addEventListener('touchend', onup); +motors_joystick.addEventListener('touchcancel', onup); + +const text_color = getComputedStyle(motors_canvas).color; +document.body.addEventListener('h:infos:motors', e => { + const ctx = motors_canvas.getContext("2d"); + const w = motors_canvas.width; + const h = motors_canvas.height; + const m = 5; + const ml = e.detail[0]; + const mr = e.detail[1]; + ctx.clearRect(0, 0, w, h); + ctx.lineWidth = 3; + ctx.strokeStyle = "#808080" + ctx.fillStyle = "#008000" + + ctx.beginPath(); + ctx.rect(m, m, w-2*m, (h/2-m)); + ctx.stroke(); + + ctx.beginPath(); + ctx.rect(m, (h/2-m)+m, w-2*m, (h/2-m)); + ctx.stroke(); + + ctx.beginPath(); + if (ml > 0) + ctx.rect(w/2, m+1, ml*(w/2-m), (h/2-m)-2); + else + ctx.rect(w/2+ml*(w/2-m), m+1, -ml*(w/2-m), (h/2-m)-2); + ctx.fill(); + + ctx.beginPath(); + if (ml > 0) + ctx.rect(w/2, (h/2-m)+m+1, mr*(w/2-m), (h/2-m)-2); + else + ctx.rect(w/2+mr*(w/2-m), (h/2-m)+m+1, -mr*(w/2-m), (h/2-m)-2); + ctx.fill(); +}); + +document.body.addEventListener('h:section:statechanged', e => { + infos_mask(INFOS_MOTORS, e.detail['on']); +}); +infos_mask(INFOS_MOTORS, true); + + //-------------------------------------------------- + + //position + + const section2 = document.getElementById('section_position'); +section2.insertAdjacentHTML('beforeend', ` +

+
+
+ +

+`); + +const position_reset = document.getElementById('position_reset'); +position_reset.addEventListener('click', async () => { + position_reset.disabled = true; + await fetch('/position/reset') + position_reset.disabled = false; +}); + +const map_canvas = document.getElementById('map'); +const map_scale_range = document.getElementById('map_scale'); +const pos_hist = []; +const text_color2 = getComputedStyle(map_canvas).color; +const map_draw = (x, y, a) => { + const ctx = map_canvas.getContext("2d"); + const w = map_canvas.width; + const h = map_canvas.height; + const map_scale = Number(map_scale_range.value); + ctx.clearRect(0, 0, w, h); + + pos_hist.unshift([x, y]); + if (pos_hist.length > 10) pos_hist.pop(); + + ctx.font = '30px sans-serif'; + ctx.fillStyle = text_color2; + ctx.textAlign = 'right'; + ctx.fillText(Math.round(x*1000) + ' mm ', w-5, 30); + ctx.fillText(Math.round(y*1000) + ' mm ', w-5, 60); + ctx.fillText(Math.round(a*180/Math.PI) + ' deg ', w-5, 90); + + ctx.save(); + ctx.translate(w/2, h/2); + ctx.scale(map_scale/1000, map_scale/1000); + ctx.lineWidth = Math.round(2*1000/map_scale); + + ctx.beginPath(); + for (let i = -10; i <= 11; i++) { + ctx.moveTo(-95+190*i, -95-2090); + ctx.lineTo(-95+190*i, -95+2090+190); + ctx.moveTo(-95-2090, -95+190*i); + ctx.lineTo(-95+2090+190, -95+190*i); + } + ctx.strokeStyle = 'rgba(180, 180, 180, 50%)'; + ctx.stroke(); + + const hl = pos_hist.length; + for (let i = 1; i < hl; i++) { + ctx.beginPath(); + ctx.moveTo(1000*pos_hist[i-1][0], -1000*pos_hist[i-1][1]); + ctx.lineTo(1000*pos_hist[i][0], -1000*pos_hist[i][1]); + const v = Math.round((hl-i)/(hl-1)*100); + ctx.strokeStyle = 'rgba(180, 200, 0, ' + v + '%)'; + ctx.stroke(); + } + + ctx.translate(x*1000, -y*1000); + ctx.rotate(-a); + ctx.beginPath(); + ctx.moveTo(40, 0); + ctx.lineTo(35, 77/4); + ctx.lineTo(24, 77/2); + ctx.lineTo(-68, 77/2); + ctx.lineTo(-68, -77/2); + ctx.lineTo(24, -77/2); + ctx.lineTo(35, -77/4); + ctx.lineTo(40, 0); + ctx.strokeStyle = '#008000' + ctx.stroke(); + + ctx.restore(); +}; + +document.body.addEventListener('h:infos:position', e => { + map_draw(...e.detail); +}); + +document.body.addEventListener('h:section:statechanged', e => { + infos_mask(INFOS_POSITION, e.detail['on']); +}); +infos_mask(INFOS_POSITION, true); + diff --git a/ui/bootstrap-5.3.3-dist/js/motors.js b/ui/bootstrap-5.3.3-dist/js/motors.js new file mode 100644 index 0000000..ab941e7 --- /dev/null +++ b/ui/bootstrap-5.3.3-dist/js/motors.js @@ -0,0 +1,166 @@ +import { infos_mask, INFOS_MOTORS } from "./infos.js"; + +const section = document.getElementById('section_motors'); +section.insertAdjacentHTML('beforeend', ` +
+

+

+
+
+ + +

+
+`); + +const motors_buf = new Float32Array(2); +const motors_ml = document.getElementById("motors_ml") +const motors_mr = document.getElementById("motors_mr") +const motors_stop = document.getElementById("motors_stop") +const motors_joystick = document.getElementById('motors_joystick'); +const motors_canvas = document.getElementById('motors_canvas'); + +let motors_ws = null; + +const send_motors = (vl, vr) => { + if (motors_ws && motors_ws.readyState == WebSocket.OPEN) { + const lim = v => Math.max(-1, Math.min(v, 1)); + motors_buf[0] = lim(vl); + motors_buf[1] = lim(vr); + motors_ml.value = vl * 100; + motors_mr.value = vr * 100; + motors_ws.send(motors_buf); + } else if (!motors_ws || motors_ws.readyState != WebSocket.CONNECTING) { + motors_ws = new WebSocket('ws://' + document.location.host + '/motors.ws'); + motors_ws.addEventListener("open", () => send_motors(vl, vr)); + } +}; + +const update_motors = () => { + const vl = motors_ml.value / 100; + const vr = motors_mr.value / 100; + send_motors(vl, vr); +} + +motors_ml.addEventListener('change', update_motors); +motors_mr.addEventListener('change', update_motors); +motors_stop.addEventListener('click', e => { + e.preventDefault(); + motors_ml.value = 0; + motors_mr.value = 0; + update_motors(); +}); + +const btnsz = 40; +let positionX = 0, positionY = 0; +let clicked = false; +let lastsend = performance.now(); +const joystick_draw = () => { + const ctx = motors_joystick.getContext("2d"); + const w = motors_joystick.width; + const h = motors_joystick.height; + ctx.clearRect(0, 0, w, h); + + ctx.fillStyle = "#00aaff40"; + ctx.beginPath(); + ctx.arc(w/2, h/2, w/2-btnsz, 0, 2 * Math.PI); + ctx.fill(); + + ctx.lineWidth = 2; + ctx.strokeStyle = "#0099ee"; + ctx.fillStyle = "#00aaff"; + ctx.beginPath(); + ctx.arc(w/2 + positionX, h/2 + positionY, btnsz, 0, 2 * Math.PI); + ctx.fill(); + ctx.stroke(); +} +const updatePosition = e => { + const clientX = e.clientX || e.touches[0].clientX; + const clientY = e.clientY || e.touches[0].clientY; + const r = motors_joystick.getBoundingClientRect(); + const kw = r.width/2 - btnsz; + let vx = Math.max(-1, Math.min((clientX - r.left - r.width/2) / kw, 1)); + let vy = Math.max(-1, Math.min((clientY - r.top - r.height/2) / kw, 1)); + if (vx*vx + vy*vy > 1) { + const a = Math.atan2(vy, vx); + vx = Math.cos(a); + vy = Math.sin(a); + } + positionX = vx * kw; + positionY = vy * kw; + vy *= -1; + if (performance.now() - lastsend > 250) { + send_motors(vy+vx, vy-vx); + lastsend = performance.now(); + } + joystick_draw(); +} +const ondown = e => { + clicked = true; + motors_joystick.style.cursor = 'none' + updatePosition(e); +}; +const onmove = e => { + if (clicked) { + updatePosition(e); + } +}; +const onup = () => { + if (clicked) { + send_motors(0, 0); + motors_joystick.style.cursor = '' + clicked = false; + positionX = positionY = 0; + joystick_draw(); + } +}; +joystick_draw(); +motors_joystick.addEventListener('mousedown', ondown); +motors_joystick.addEventListener('mousemove', onmove); +motors_joystick.addEventListener('mouseup', onup); +motors_joystick.addEventListener('mouseleave', onup); +motors_joystick.addEventListener('touchstart', ondown); +motors_joystick.addEventListener('touchmove', onmove); +motors_joystick.addEventListener('touchend', onup); +motors_joystick.addEventListener('touchcancel', onup); + +const text_color = getComputedStyle(motors_canvas).color; +document.body.addEventListener('h:infos:motors', e => { + const ctx = motors_canvas.getContext("2d"); + const w = motors_canvas.width; + const h = motors_canvas.height; + const m = 5; + const ml = e.detail[0]; + const mr = e.detail[1]; + ctx.clearRect(0, 0, w, h); + ctx.lineWidth = 3; + ctx.strokeStyle = "#808080" + ctx.fillStyle = "#008000" + + ctx.beginPath(); + ctx.rect(m, m, w-2*m, (h/2-m)); + ctx.stroke(); + + ctx.beginPath(); + ctx.rect(m, (h/2-m)+m, w-2*m, (h/2-m)); + ctx.stroke(); + + ctx.beginPath(); + if (ml > 0) + ctx.rect(w/2, m+1, ml*(w/2-m), (h/2-m)-2); + else + ctx.rect(w/2+ml*(w/2-m), m+1, -ml*(w/2-m), (h/2-m)-2); + ctx.fill(); + + ctx.beginPath(); + if (ml > 0) + ctx.rect(w/2, (h/2-m)+m+1, mr*(w/2-m), (h/2-m)-2); + else + ctx.rect(w/2+mr*(w/2-m), (h/2-m)+m+1, -mr*(w/2-m), (h/2-m)-2); + ctx.fill(); +}); + +document.body.addEventListener('h:section:statechanged', e => { + infos_mask(INFOS_MOTORS, e.detail['on']); +}); +infos_mask(INFOS_MOTORS, true); diff --git a/ui/bootstrap-5.3.3-dist/js/position.js b/ui/bootstrap-5.3.3-dist/js/position.js new file mode 100644 index 0000000..30748fb --- /dev/null +++ b/ui/bootstrap-5.3.3-dist/js/position.js @@ -0,0 +1,89 @@ +import { infos_mask, INFOS_POSITION } from "./infos.js"; + +const section = document.getElementById('section_position'); +section.insertAdjacentHTML('beforeend', ` +

+
+
+ +

+`); + +const position_reset = document.getElementById('position_reset'); +position_reset.addEventListener('click', async () => { + position_reset.disabled = true; + await fetch('/position/reset') + position_reset.disabled = false; +}); + +const map_canvas = document.getElementById('map'); +const map_scale_range = document.getElementById('map_scale'); +const pos_hist = []; +const text_color = getComputedStyle(map_canvas).color; +const map_draw = (x, y, a) => { + const ctx = map_canvas.getContext("2d"); + const w = map_canvas.width; + const h = map_canvas.height; + const map_scale = Number(map_scale_range.value); + ctx.clearRect(0, 0, w, h); + + pos_hist.unshift([x, y]); + if (pos_hist.length > 10) pos_hist.pop(); + + ctx.font = '30px sans-serif'; + ctx.fillStyle = text_color; + ctx.textAlign = 'right'; + ctx.fillText(Math.round(x*1000) + ' mm ', w-5, 30); + ctx.fillText(Math.round(y*1000) + ' mm ', w-5, 60); + ctx.fillText(Math.round(a*180/Math.PI) + ' deg
', w-5, 90); + + ctx.save(); + ctx.translate(w/2, h/2); + ctx.scale(map_scale/1000, map_scale/1000); + ctx.lineWidth = Math.round(2*1000/map_scale); + + ctx.beginPath(); + for (let i = -10; i <= 11; i++) { + ctx.moveTo(-95+190*i, -95-2090); + ctx.lineTo(-95+190*i, -95+2090+190); + ctx.moveTo(-95-2090, -95+190*i); + ctx.lineTo(-95+2090+190, -95+190*i); + } + ctx.strokeStyle = 'rgba(180, 180, 180, 50%)'; + ctx.stroke(); + + const hl = pos_hist.length; + for (let i = 1; i < hl; i++) { + ctx.beginPath(); + ctx.moveTo(1000*pos_hist[i-1][0], -1000*pos_hist[i-1][1]); + ctx.lineTo(1000*pos_hist[i][0], -1000*pos_hist[i][1]); + const v = Math.round((hl-i)/(hl-1)*100); + ctx.strokeStyle = 'rgba(180, 200, 0, ' + v + '%)'; + ctx.stroke(); + } + + ctx.translate(x*1000, -y*1000); + ctx.rotate(-a); + ctx.beginPath(); + ctx.moveTo(40, 0); + ctx.lineTo(35, 77/4); + ctx.lineTo(24, 77/2); + ctx.lineTo(-68, 77/2); + ctx.lineTo(-68, -77/2); + ctx.lineTo(24, -77/2); + ctx.lineTo(35, -77/4); + ctx.lineTo(40, 0); + ctx.strokeStyle = '#008000' + ctx.stroke(); + + ctx.restore(); +}; + +document.body.addEventListener('h:infos:position', e => { + map_draw(...e.detail); +}); + +document.body.addEventListener('h:section:statechanged', e => { + infos_mask(INFOS_POSITION, e.detail['on']); +}); +infos_mask(INFOS_POSITION, true); diff --git a/ui/images/labyrinth-1080p-wallpaper-1920x1080.jpg b/ui/images/labyrinth-1080p-wallpaper-1920x1080.jpg new file mode 100644 index 0000000..faa84f2 Binary files /dev/null and b/ui/images/labyrinth-1080p-wallpaper-1920x1080.jpg differ diff --git a/ui/index.html b/ui/index.html index 039418b..1025ec7 100644 --- a/ui/index.html +++ b/ui/index.html @@ -6,30 +6,51 @@ MinHAUMTaure - -

Bienvenue sur MinHAUMTaure

+ -

Fonctionnalités

+
+
+
+

Bienvenue sur MinHAUMTaure

+
+
+
+
+
+

Fonctionnalités

+
+
+
+

Directions

+ +
+
+

Changement de couleur LED

+

Sélection de la couleur

+ + +

Automatique

+
+ + +
-
-
+
+
-

Sélection de la couleur

- - -
-
- -

Directions

- - -
-
- -

Automatique

-
- - + +
+
+
+
+
+

Repère Spatial

+
+
+ +
+
+