Compare commits

..

3 commits

Author SHA1 Message Date
Kizarm
e79f5facff add local storage markers 2024-10-27 15:57:23 +01:00
Kizarm
26ac333c82 add markers, text 2024-10-27 14:53:37 +01:00
Kizarm
2a7cc229fa add offscreen canvas 2024-10-26 19:22:25 +02:00
2 changed files with 140 additions and 11 deletions

View file

@ -24,7 +24,7 @@
} }
fieldset { fieldset {
background-color: #C0FFC0; background-color: #C0FFC0;
height: 100%; height: 80%;
} }
tr,td { tr,td {
height: 100%; height: 100%;
@ -88,6 +88,20 @@
<td><input type="button" class="start" id="Start" value="Start" onclick="Start();" disabled></td> <td><input type="button" class="start" id="Start" value="Start" onclick="Start();" disabled></td>
</tr></table> </tr></table>
</div> </div>
<div class="frame1"><h2>Poznámky k realizaci.</h2>
<p>V zásadě to lze udělat jako webovou aplikaci čistě v javascriptu. Musí se tomu pomoct WebSocket serverem,
který zprostředkuje komunikaci se sériovým portem. Chrome sice asi má nějakou podporu sériového portu
přímo v browseru, ale bůhví jak to funguje. Přes webové sokety lze data na obě strany honit jako JSON,
je to celkem rychlé a snese to i dlouhé pakety. Přesto nestíhá zobrazovat v řádu ms, možná by stálo za to
udělat to zobrazování ve smyčce událostí např. pomocí window.requestAnimationFrame(). Je ale možné,
že problém je už v převodu těch dat z JSON do objektu.
</p>Zobrazování probíhá tak, že pomocné objekty jako markery, text atd. se vykreslí do pomocného canvasu
(obrázku), ten se pak vykreslí na obrazovku a přes něj se plácnou jen čáry kanálů. Snad je to tak rychlejší.
Není doděláno zoomování časové základny, bylo by to složité a nepovažuji to za důležité. Stejně tak
není korektní změna velikosti okna, ale stačí dát refresh.
<p>
</p>
</div>
<script src="index.js"></script> <script src="index.js"></script>
</body> </body>
</html> </html>

View file

@ -11,10 +11,23 @@ const StartBut = document.getElementById ('Start');
const ConnectBut = document.getElementById ('Connect'); const ConnectBut = document.getElementById ('Connect');
const Connected = document.getElementById ('Connected'); const Connected = document.getElementById ('Connected');
const Canvas = document.getElementById ('canvas'); const Canvas = document.getElementById ('canvas');
var gBGCanvas = null;
var gMark = {
type : 'time',
volt : { a : 0.5, b : 1.0 },
time : { a : 100, b : 200 },
};
const REF_Y = 3.3; const REF_Y = 3.3;
const MAX_Y = 4096.0; const MAX_Y = 4096.0;
const MAX_X = 1024.0; const MAX_X = 1024.0;
const gTimeStep = [
2.0e-6, 5.0e-6, 10.0e-6,
2.0e-5, 5.0e-5, 10.0e-5,
2.0e-4, 5.0e-4, 10.0e-4,
2.0e-3, 5.0e-3, 10.0e-3,
2.0e-2, 5.0e-2, 10.0e-2,
2.0e-1, 5.0e-1, 1.0, ];
var gMZ = { m11 : 1.0, m12 : 0.0, m21 : 0.0, m22 : 1.0, ox : 0.0, oy : 0.0 }; var gMZ = { m11 : 1.0, m12 : 0.0, m21 : 0.0, m22 : 1.0, ox : 0.0, oy : 0.0 };
var gTC = { x : 100.0, y : 2048.0 }; var gTC = { x : 100.0, y : 2048.0 };
@ -44,11 +57,17 @@ function initWebSocket() {
websocket.onopen = function (evt) { websocket.onopen = function (evt) {
ConnectBut.value = 'Disconnect'; ConnectBut.value = 'Disconnect';
Connected.innerHTML = 'CONNECTED'; Connected.innerHTML = 'CONNECTED';
const stored = localStorage.getItem ('mark_store');
console.log (stored);
if (stored) {
gMark = JSON.parse (stored);
}
connected = true; connected = true;
}; };
websocket.onclose = function (evt) { websocket.onclose = function (evt) {
ConnectBut.value = 'Connect'; ConnectBut.value = 'Connect';
Connected.innerHTML = 'DISCONNECTED'; Connected.innerHTML = 'DISCONNECTED';
localStorage.setItem ('mark_store', JSON.stringify (gMark));
connected = false; connected = false;
}; };
websocket.onmessage = function (evt) { websocket.onmessage = function (evt) {
@ -57,9 +76,9 @@ function initWebSocket() {
RefreshSettings (obj); RefreshSettings (obj);
return; return;
} }
DrawTrig (); PrepareChannel (0, obj.a);
DrawPolyLine (0, obj.a, '#00FF00'); PrepareChannel (1, obj.b);
DrawPolyLine (1, obj.b, '#FF0000'); DrawAll ();
}; };
websocket.onerror = function (evt) { websocket.onerror = function (evt) {
console.log ('ERROR: ' + evt.data); console.log ('ERROR: ' + evt.data);
@ -76,7 +95,8 @@ function RefreshSettings (obj) {
document.getElementById('trigger_edge').value = obj.rising; document.getElementById('trigger_edge').value = obj.rising;
document.getElementById('time_base' ).value = obj.tim; document.getElementById('time_base' ).value = obj.tim;
gTC.x = obj.offset; gTC.y = obj.value; gTC.x = obj.offset; gTC.y = obj.value;
DrawTrig (); PrepareBG();
DrawAll ();
}; };
function ReloadMatrix (sz) { function ReloadMatrix (sz) {
const xz = sz.x / MAX_X; const xz = sz.x / MAX_X;
@ -98,10 +118,11 @@ function SendEvent (evt) {
if (!connected) return; if (!connected) return;
const reply = JSON.stringify(evt); const reply = JSON.stringify(evt);
websocket.send (reply); websocket.send (reply);
} };
function DrawTrig () { function PrepareBG () {
var ctx = Canvas.getContext('2d'); var ctx = gBGCanvas.getContext('2d');
ctx.clearRect(0, 0, Canvas.width, Canvas.height); ctx.fillStyle = "black";
ctx.fillRect(0, 0, gBGCanvas.width, gBGCanvas.height);
ctx.lineWidth = 2.0; ctx.lineWidth = 2.0;
ctx.strokeStyle = 'blue'; ctx.strokeStyle = 'blue';
ctx.beginPath(); ctx.beginPath();
@ -117,8 +138,80 @@ function DrawTrig () {
ctx.moveTo(b.x, b.y); ctx.moveTo(b.x, b.y);
ctx.lineTo(e.x, e.y); ctx.lineTo(e.x, e.y);
ctx.stroke(); ctx.stroke();
ctx.save();
// dopsat grid, markers
ctx.lineWidth = 1.0;
ctx.strokeStyle = '#808080';
ctx.setLineDash([4, 10]);
const stepx = 100, stepm = MAX_Y / REF_Y, stepy = 0.5 * stepm;
for (let x=stepx; x<MAX_X; x+=stepx) {
ctx.beginPath();
b = TrPt (x, 0);
ctx.moveTo(b.x, b.y);
e = TrPt (x, MAX_Y);
ctx.lineTo(e.x, e.y);
ctx.stroke();
}
for (let y=stepy; y<MAX_Y; y+=stepy) {
ctx.beginPath();
b = TrPt (0, y);
ctx.moveTo(b.x, b.y);
e = TrPt (MAX_X, y);
ctx.lineTo(e.x, e.y);
ctx.stroke();
}
ctx.restore ();
ctx.lineWidth = 2.0;
ctx.strokeStyle = '#FFFF00';
var ba, bb, ea, eb;
if (gMark.type === 'time') {
ba = TrPt (gMark.time.a, 0);
ea = TrPt (gMark.time.a, MAX_Y - 256);
bb = TrPt (gMark.time.b, 0);
eb = TrPt (gMark.time.b, MAX_Y - 256);
} else {
ba = TrPt (0, gMark.volt.a * stepm);
ea = TrPt (MAX_Y, gMark.volt.a * stepm);
bb = TrPt (0, gMark.volt.b * stepm);
eb = TrPt (MAX_Y, gMark.volt.b * stepm);
}
ctx.beginPath();
ctx.moveTo(ba.x, ba.y);
ctx.lineTo(ea.x, ea.y);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(bb.x, bb.y);
ctx.lineTo(eb.x, eb.y);
ctx.stroke();
var A,B,D;
const timz = gTimeStep [parseInt (TimeBase.value, 10)];
if (gMark.type === 'time') {
const d = (gMark.time.b - gMark.time.a) * timz;
const z = d === 0 ? 0 : 1.0 / d;
A = (gMark.time.a * timz).toExponential(2) + 's';
B = (gMark.time.b * timz).toExponential(2) + 's';
D = d.toExponential(2) + 's, f='+ z.toExponential(2) + 'Hz';
} else {
A = gMark.volt.a.toFixed(2) + 'V';
B = gMark.volt.b.toFixed(2) + 'V';
D =(gMark.volt.b - gMark.volt.a).toFixed(2) + 'V';
}
const txt = '0.5V/d, Mark A:' + A + ', B:' + B + ', Δ:' + D;
ctx.font = '16px Arial';
ctx.fillStyle = '#00FFFF';
ctx.fillText(txt,100,20);
}; };
function DrawPolyLine (ch, data, col) { function DrawAll () {
DrawBG ();
DrawPolyLine (0, '#00FF00');
DrawPolyLine (1, '#FF0000');
}
function DrawBG () {
var context = Canvas.getContext('2d');
context.drawImage (gBGCanvas, 0, 0);
};
function PrepareChannel (ch, data) {
var out = ch === 0 ? gCA : gCB; var out = ch === 0 ? gCA : gCB;
if (data.length === 1) { if (data.length === 1) {
out [gIndex] = data [0]; out [gIndex] = data [0];
@ -132,6 +225,9 @@ function DrawPolyLine (ch, data, col) {
out [n] = data [n]; out [n] = data [n];
} }
} }
}
function DrawPolyLine (ch, col) {
var out = ch === 0 ? gCA : gCB;
var ctx = Canvas.getContext('2d'); var ctx = Canvas.getContext('2d');
ctx.lineWidth = 2.0; ctx.lineWidth = 2.0;
ctx.strokeStyle = col; ctx.strokeStyle = col;
@ -142,15 +238,27 @@ function DrawPolyLine (ch, data, col) {
} }
ctx.stroke(); ctx.stroke();
}; };
function MoveMarker (n, p) {
const q = InPt (p.x, p.y);
const m = REF_Y / MAX_Y;
if (gMark.type === 'time') {
if (n === 0) { gMark.time.a = q.x; } else { gMark.time.b = q.x; }
} else {
if (n === 0) { gMark.volt.a = q.y * m; } else { gMark.volt.b = q.y * m; }
}
};
Canvas.addEventListener ('click', function(event) { Canvas.addEventListener ('click', function(event) {
const p = { x : event.clientX, y : event.clientY }; const p = { x : event.clientX, y : event.clientY };
const q = InPt (p.x, p.y); const q = InPt (p.x, p.y);
switch (gItemToMove) { switch (gItemToMove) {
case 0: gTC.y = q.y; SendEvent ({ type : "trg_val", value: q.y }); break; case 0: gTC.y = q.y; SendEvent ({ type : "trg_val", value: q.y }); break;
case 1: gTC.x = q.x; SendEvent ({ type : "trg_ofs", value: q.x }); break; case 1: gTC.x = q.x; SendEvent ({ type : "trg_ofs", value: q.x }); break;
case 2: MoveMarker (0, p); break;
case 3: MoveMarker (1, p); break;
default: return; default: return;
}; };
DrawTrig (); PrepareBG ();
DrawAll ();
}); });
/************************************************************************/ /************************************************************************/
TriggerSrc.onchange = (event) => { TriggerSrc.onchange = (event) => {
@ -182,9 +290,15 @@ MoveElem.onchange = (event) => {
}; };
MarkerTime.onclick = (event) => { MarkerTime.onclick = (event) => {
console.log ('time_mark'); console.log ('time_mark');
gMark.type = 'time';
PrepareBG ();
DrawAll ();
}; };
MarkerVolt.onclick = (event) => { MarkerVolt.onclick = (event) => {
console.log ('volt_mark'); console.log ('volt_mark');
gMark.type = 'volt';
PrepareBG ();
DrawAll ();
}; };
window.onload = (event) => { window.onload = (event) => {
const dim = { x : Canvas.clientWidth, y : Canvas.clientHeight }; const dim = { x : Canvas.clientWidth, y : Canvas.clientHeight };
@ -196,6 +310,7 @@ window.onload = (event) => {
gCB.push (1000); gCB.push (1000);
} }
wsUri = js_get_id (); wsUri = js_get_id ();
gBGCanvas = new OffscreenCanvas (dim.x, dim.y);
}; };
function Connect () { function Connect () {
if (connected) { if (connected) {