<!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>Web Oscilloscope
Ver 0.9</title>
<style>
html,
body {
width: 100%;
height: 100%;
margin: 0px;
border: 0;
overflow: hidden;
/*
Disable scrollbars */
display: block;
/* No floating
content on sides */
}
</style>
<script>
//
------------------------------------------------------
// Tiny + Cheap
Oscilloscope
// [[ AP MODE
Info]] SSID : OSCIL_V06, PWD :
12345678
// +-------- GPL
License --------------------------------+
// do not remove
contact & url
// contact :
terminal0070@gmail.com
// blog :
https://andy-power.blogspot.com/
//
------------------------------------------------------
//-------------------------------------------------------------------
// for rounded
rectangular
// x, y, width,
height, radius of each conner, fill?, stroke?
//-------------------------------------------------------------------
CanvasRenderingContext2D.prototype.roundRect = function (x, y, width, height, radius, fill, stroke) {
var cornerRadius = { upperLeft: 0, upperRight: 0, lowerLeft: 0, lowerRight: 0 };
if (typeof stroke == "undefined") { stroke = true; }
if (typeof radius == "object") {
for (var side in radius) {
cornerRadius[side] = radius[side];
}
}
this.beginPath();
this.moveTo(x + cornerRadius.upperLeft, y);
this.lineTo(x + width - cornerRadius.upperRight, y);
this.quadraticCurveTo(x + width, y, x + width, y + cornerRadius.upperRight);
this.lineTo(x + width, y + height - cornerRadius.lowerRight);
this.quadraticCurveTo(x + width, y + height, x + width - cornerRadius.lowerRight, y + height);
this.lineTo(x + cornerRadius.lowerLeft, y + height);
this.quadraticCurveTo(x, y + height, x, y + height - cornerRadius.lowerLeft);
this.lineTo(x, y + cornerRadius.upperLeft);
this.quadraticCurveTo(x, y, x + cornerRadius.upperLeft, y);
this.closePath();
if (stroke) { this.stroke(); }
if (fill) { this.fill(); }
}
//-------------------------------------------------------------------
// for websocket
//-------------------------------------------------------------------
var g_WebSocket = null; // web socket
var g_bForceCloseWebsocket = false;
function startSocket() {
g_strConntectionStatus = "Try
con..."
redraw();
if ("WebSocket" in window) {
g_WebSocket = new WebSocket("ws://192.168.4.1:8080"); // home
g_WebSocket.binaryType = 'arraybuffer';
g_WebSocket.onopen = function (event) {
g_strConntectionStatus = "Connected";
redraw();
}
g_WebSocket.onmessage = function (event) {
var wsRecvMsg = event.data;
var dataStream = new Uint8Array(wsRecvMsg);
// first 1 bytes
are control code
switch (dataStream[0]) {
case 0: // main info
//
g_aryLastChartDatas[0] == processing code
//
g_aryLastChartDatas[1] == channel num
g_aryLastChartDatas = dataStream;
redraw();
break;
case 1: // additional
info (Hz, Volt, ~~)
//----------------------------------------------------------
// 부가 데이터 순서 ( sub info bytes order)
// 0
1 2 3 4 5
// code,
collapsed flag, sub scale, scale, freq /
256, freq % 256,
// 6, 8, 10 7,9, 11
// volt (Integer part), volt (Fraction part * 10)
// 12 13
// #total_data /
256, #total_data % 256,
// 14 15
// m_dScrollStart / 256, m_dScrollStart % 256,
// 16
// m_dTracking
//----------------------------------------------------------
if (dataStream[1] == 0) {
g_bCollaped = false;
} else {
g_bCollaped = true;
}
g_dSubScale = dataStream[2];
g_dScale = dataStream[3];
g_dFrequency = dataStream[4] * 256 + dataStream[5];
g_aryVolts[0] = dataStream[6] + dataStream[7] / 10;
g_aryVolts[1] = dataStream[8] + dataStream[9] / 10;
g_aryVolts[2] = dataStream[10] + dataStream[11] / 10;
g_dTotalDataNum = dataStream[12] * 256 + dataStream[13];
g_dScrollPos = dataStream[14] * 256 + dataStream[15];
g_dTrackingFlag = dataStream[16];
redraw();
break;
default:
console.log(dataStream[0]);
break;
}
}
g_WebSocket.onclose = function (event) {
if (g_bForceCloseWebsocket == false) {
startSocket();
return;
} else {
g_strConntectionStatus = "Connect";
g_WebSocket = null;
redraw();
}
}
}
}
//-------------------------------------------------------------------
// for oscilloscope
function
//-------------------------------------------------------------------
var g_OscilloCanvas = null; // target Canvas
var g_Context = null; // context
var g_dChNum = 3; // number of
channel
var g_bCollaped = false; // collapse
channels or not.
var g_strConntectionStatus = "Connect";
var g_dChartDisplayDataLength = 300;
var g_aryLastChartDatas = null;
var g_aryChColors = ["#3498DB", "#4AA02C", "#C68E17"]; // each channel
color
var g_dTrackingFlag = 0;
// oscilloscope top
side info
var g_dFrequency = 0; // ch1 frequency
var g_aryVolts = [0.0, 0.0, 0.0]; // volts (3
channels)
var g_dScale = 1; // main scale
var g_dSubScale = 1; // sub scale
var g_dTotalDataNum = 380 * 2; // number of data (
== depth)
var g_dScrollPos = 0; // now scroll pos
// for UI (mouse
event)
var g_dLastMouseX = 0, g_dLastMouseY = 0; // Last
mouse click pos.
var g_dScrollGrapBarSx = 0, g_dScrollGrapBarEx = 0; // scroll grap
bar position
var g_dScrollGrapStartX = 0;
var g_dPrevBtnIndex = -1;
var g_dMouseDownStatus = 0; // 0 : up status, 1
: down status, 2 : scrollbar down status
var g_dNormalButtonStartWidth = 60;
//-------------------------------------------------------------------
// Master functions
//-------------------------------------------------------------------
function initialize() {
g_OscilloCanvas = document.getElementById('oscilloscopeCanvas');
g_Context = g_OscilloCanvas.getContext('2d');
g_OscilloCanvas.addEventListener("mousedown", onMousedown, false);
g_OscilloCanvas.addEventListener("mousemove", onMousemove, false);
g_OscilloCanvas.addEventListener("mouseup", onMouseup, false);
g_OscilloCanvas.addEventListener("touchstart", onMousedown, false);
g_OscilloCanvas.addEventListener("touchmove", onMousemove, false);
//
g_OscilloCanvas.addEventListener("touchend", onMouseup,
false);
window.addEventListener('resize', resizeCanvas, false);
resizeCanvas();
}
function resizeCanvas() {
g_OscilloCanvas.width = window.innerWidth;
g_OscilloCanvas.height = window.innerHeight;
redraw();
}
function redraw() {
g_Context.save(); //Freeze redraw
g_Context.fillStyle = "#303030";
g_Context.font = "12px
Arial";
g_Context.lineWidth = 1;
g_Context.fillRect(0, 0, g_OscilloCanvas.width, g_OscilloCanvas.height);
drawTopside();
drawMiddleSide();
drawBottomSide();
g_Context.restore(); //And now do the
redraw
}
//-------------------------------------------------------------------
// for Mouse
interface
//-------------------------------------------------------------------
function onMousedown(e) {
g_dLastMouseX = e.pageX;
g_dLastMouseY = e.pageY;
if (g_dMouseDownStatus == 1) { return; }
g_dMouseDownStatus = 1;
g_dPrevBtnIndex = getButtonIndex();
if (g_dPrevBtnIndex >= 0) {
redraw();
} else if (g_dLastMouseX > g_dScrollGrapBarSx && g_dLastMouseX < g_dScrollGrapBarEx
&& g_dLastMouseY < 30) {
g_dScrollGrapStartX = g_dLastMouseX;
g_dMouseDownStatus = 2; // start
scrollbar grapping
}
}
function onMousemove(e) {
g_dLastMouseX = e.pageX;
g_dLastMouseY = e.pageY;
if (g_dMouseDownStatus == 1) {
var dNowBtnIndex = getButtonIndex();
if (g_dPrevBtnIndex != dNowBtnIndex) {
g_dPrevBtnIndex = dNowBtnIndex;
redraw();
}
} else if (g_dMouseDownStatus == 2) { // process
scrolling
var gap = (g_dLastMouseX - g_dScrollGrapStartX) * ((g_dTotalDataNum - g_dChartDisplayDataLength) / (100 + g_dScrollGrapBarSx - g_dScrollGrapBarEx));
var dNewScrollPos = parseInt(g_dScrollPos + gap);
if (dNewScrollPos < 0) dNewScrollPos = 0;
if (Math.abs(dNewScrollPos - g_dScrollPos) > 2) {
g_dScrollGrapStartX = g_dLastMouseX;
var tmp = [1, 10, parseInt(dNewScrollPos / 256), parseInt(dNewScrollPos % 256)];
var sendBuffer = new Uint8Array(tmp);
g_WebSocket.send(sendBuffer.buffer);
}
}
}
function onMouseup(e) {
g_dMouseDownStatus = 0;
g_dLastMouseX = e.pageX;
g_dLastMouseY = e.pageY;
g_dPrevBtnIndex = -1;
if (g_dLastMouseY < 30 && g_dLastMouseX > (window.innerWidth - 80)) {
if (g_WebSocket == null) { // 'connect',
'disconnect' button
startSocket();
} else {
g_bForceCloseWebsocket = true;
g_WebSocket.close();
g_WebSocket = null;
}
} else {
var btnIndex = getButtonIndex(); // function button
if (btnIndex >= 0 && g_WebSocket != null && g_WebSocket
&& g_WebSocket.readyState == g_WebSocket.OPEN) {
var tmp = [1, btnIndex];
var sendBuffer = new Uint8Array(tmp);
g_WebSocket.send(sendBuffer.buffer);
if (btnIndex == 0) {
g_dTrackingFlag = 1;
redraw();
}
}
}
redraw();
}
// 클릭 포인트를 바탕으로 어떤 버튼이 눌려졌는지 조사하는 함수
function getButtonIndex() {
var btnIndex = -1;
if (g_dLastMouseY >= (window.innerHeight - 45)) {
for (let i = 0; i < 7; i++) {
if (g_dLastMouseX >= 5 + i * g_dNormalButtonStartWidth && g_dLastMouseX <= 5 + i * g_dNormalButtonStartWidth + 34) {
btnIndex = i;
break;
}
}
}
return btnIndex;
}
//-------------------------------------------------------------------
// Sub Functions
//-------------------------------------------------------------------
function drawTopside() {
// header info
g_Context.font = "18px
Arial";
g_Context.textAlign = "left";
g_Context.fillStyle = "#dfdfdf";
g_Context.fillText("Scale :
" + g_dSubScale + " / " + g_dScale, 10, 20);
g_Context.fillStyle = g_aryChColors[0];
g_Context.fillText(g_dFrequency + " Hz", 140, 20);
// draw scroll
bar...
var scrollsx = window.innerWidth / 2 - 50;
var scrollWidth = 100;
var barWidth = (scrollWidth - 2) * g_dChartDisplayDataLength / g_dTotalDataNum;
g_dScrollGrapBarSx = (scrollWidth - 2) * g_dScrollPos / g_dTotalDataNum + scrollsx + 1;
g_dScrollGrapBarEx = g_dScrollGrapBarSx + barWidth;
g_Context.strokeStyle = "#f3f3f3";
g_Context.beginPath();
drawLine(scrollsx, 6, scrollsx, 23);
drawLine(scrollsx + scrollWidth, 6, scrollsx + scrollWidth, 23);
drawLine(scrollsx, 15, scrollsx + scrollWidth, 15);
g_Context.stroke();
if (g_dMouseDownStatus == 2) {
g_Context.fillStyle = "#5DADE2";
} else { g_Context.fillStyle = "#d6d6d6" };
g_Context.roundRect(g_dScrollGrapBarSx, 11, barWidth, 9, { upperLeft: 2, upperRight: 2, lowerLeft: 2, lowerRight: 2 }, true, false);
// draw connecting
status..
g_Context.font = "12px
Arial";
if (g_strConntectionStatus == "Connected") {
g_Context.fillStyle = "#1D8348";
} else {
g_Context.fillStyle = "#EC7063";
}
g_Context.roundRect(window.innerWidth - 80, 4, 72, 22, { upperLeft: 3, upperRight: 3, lowerLeft: 3, lowerRight: 3 }, true, false);
g_Context.fillStyle = "#fafafa";
g_Context.textAlign = "center";
g_Context.fillText(g_strConntectionStatus, window.innerWidth - 44, 19);
// bottom line
g_Context.strokeStyle = "#dfdfdf";
g_Context.beginPath();
drawLine(0, 30, window.innerWidth, 30);
g_Context.stroke();
}
function drawMiddleSide() {
// separating line
g_Context.strokeStyle = "#dfdfdf";
g_Context.beginPath();
drawLine(49, 30, 49, window.innerHeight - 50)
g_Context.stroke();
drawChart();
if (g_dTrackingFlag == 1) {
g_Context.fillStyle = "#303030";
g_Context.roundRect(window.innerWidth / 2 - 160, window.innerHeight / 2 - 25, 320, 50, { upperLeft: 3, upperRight: 3, lowerLeft: 3, lowerRight: 3 }, true, false);
g_Context.font = "18px
Arial";
g_Context.fillStyle = "#fafafa";
g_Context.textAlign = "center";
g_Context.fillText("Waiting
for phase changing...", window.innerWidth / 2, window.innerHeight / 2 + 9);
}
}
function drawBottomSide() {
// bottom line
g_Context.strokeStyle = "#dfdfdf";
g_Context.beginPath();
drawLine(0, window.innerHeight - 50, window.innerWidth, window.innerHeight - 50)
g_Context.stroke();
//buttons
drawButtons();
}
function drawChart() {
var OneChannelCnt = g_dChartDisplayDataLength; // one channel
data count
if (g_aryLastChartDatas != null) {
g_dChNum = g_aryLastChartDatas[1];
}
var allHeight = window.innerHeight - 30 - 50 - 2; // height -
topside - bottomside
var allWidth = window.innerWidth - 50; // width -
leftside
var hopWidth = allWidth / OneChannelCnt;
var yPos = [30 + allHeight, 30 + allHeight, 30 + allHeight];
var eachMax = allHeight - 4;
g_Context.strokeStyle = "#dfdfdf";
g_Context.fillStyle = g_aryChColors[0];
g_Context.fillText(g_aryVolts[0] + " V", 22, 66);
g_Context.fillRect(0, 31, 49, 20);
g_Context.fillStyle = "#dfdfdf";
g_Context.fillText("CH 1", 22, 46);
if (g_bCollaped == false) {
switch (g_dChNum) {
case 1:
yPos[0] = 30 + allHeight - 2;
break;
case 2: // display 2nd
channel
yPos[0] = 30 + allHeight / 2 - 2;
yPos[1] = 30 + allHeight - 2;
eachMax = allHeight / 2 - 4;
g_Context.beginPath();
drawLine(0, 30 + allHeight / 2 + 2, window.innerWidth, 30 + allHeight / 2 + 2);
g_Context.stroke();
g_Context.fillStyle = g_aryChColors[1];
g_Context.fillText(g_aryVolts[1] + " V", 22, yPos[0] + 40);
g_Context.fillRect(0, yPos[0] + 5, 49, 20);
g_Context.fillStyle = "#dfdfdf";
g_Context.fillText("CH 2", 22, yPos[0] + 20);
break;
case 3: // display 3th
channel
yPos[0] = 30 + allHeight / 3 - 2;
yPos[1] = 30 + allHeight * 2 / 3 - 2;
yPos[2] = 30 + allHeight - 2;
eachMax = allHeight / 3 - 4;
g_Context.beginPath();
drawLine(0, 30 + allHeight / 3 + 2, window.innerWidth, 30 + allHeight / 3 + 2);
drawLine(0, 30 + allHeight * 2 / 3 + 2, window.innerWidth, 30 + allHeight * 2 / 3 + 2);
g_Context.stroke();
g_Context.fillStyle = g_aryChColors[1];
g_Context.fillText(g_aryVolts[1] + " V", 22, yPos[0] + 40);
g_Context.fillRect(0, yPos[0] + 5, 49, 20);
g_Context.fillStyle = "#dfdfdf";
g_Context.fillText("CH 2", 22, yPos[0] + 20);
g_Context.fillStyle = g_aryChColors[2];
g_Context.fillText(g_aryVolts[2] + " V", 22, yPos[1] + 40);
g_Context.fillRect(0, yPos[1] + 5, 49, 20);
g_Context.fillStyle = "#dfdfdf";
g_Context.fillText("CH 3", 22, yPos[1] + 19);
default:
break;
}
} else {
for (let i = 1; i < g_dChNum; i++) {
g_Context.fillStyle = g_aryChColors[i];
g_Context.fillText(g_aryVolts[i] + " V", 22, 66 + i * 61);
g_Context.fillRect(0, 31 + i * 61, 49, 20);
g_Context.fillStyle = "#dfdfdf";
g_Context.fillText("CH " + (i + 1), 22, 46 + i * 61);
}
}
if (g_aryLastChartDatas == null) {
return;
}
ratio = eachMax / 256;
for (let j = 2; j >= 0; j--) {
if (j >= g_dChNum) {
continue;
}
g_Context.strokeStyle = g_aryChColors[j];
g_Context.beginPath();
g_Context.moveTo(50, yPos[j] - g_aryLastChartDatas[2 + j * OneChannelCnt] * ratio);
for (let index = 0; index < OneChannelCnt; index++) {
g_Context.lineTo(50 + index * hopWidth, yPos[j] - g_aryLastChartDatas[2 + index + j * OneChannelCnt] * ratio);
}
g_Context.stroke();
}
}
function drawButtons() {
var shapeColor = "#dfdfdf";
var backColor = "#505050";
var sy = window.innerHeight - 40;
g_Context.lineWidth = 3;
// 모든 버튼은 원, 선, 점등으로 한땀 한땀 그렷다.
for (let i = 0; i < 7; i++) {
//var sx = 5 + i *
47;
var sx = 5 + i * g_dNormalButtonStartWidth;
if (i == g_dPrevBtnIndex) {
g_Context.fillStyle = "#5DADE2";
g_Context.strokeStyle = "#5DADE2";
} else {
g_Context.fillStyle = "#d3d3d3";
g_Context.strokeStyle = "#d3d3d3";
}
g_Context.roundRect(sx, sy, 34, 30, { upperLeft: 3, upperRight: 3, lowerLeft: 3, lowerRight: 3 }, false, true);
switch (i) {
case 0: // 추적 모드인경우에는 T 자 모양을 다른 색으로 표시한다.
// T
g_Context.fillStyle = "#dfdfdf";
if (g_dTrackingFlag == 1) {
g_Context.fillStyle = "#F5B041";
} else if (2 == g_dTrackingFlag) {
g_Context.fillStyle = "#3498DB";
}
g_Context.fillRect(sx + 6, sy + 6, 21, 4);
g_Context.fillRect(sx + 15, sy + 10, 4, 14);
break;
case 1: // zoom in
g_Context.beginPath();
g_Context.arc(sx + 16, sy + 14, 10, 0, 2 * Math.PI, false);
g_Context.moveTo(sx + 24, sy + 21); g_Context.lineTo(sx + 28, sy + 25);
g_Context.moveTo(sx + 16, sy + 9); g_Context.lineTo(sx + 16, sy + 19);
g_Context.moveTo(sx + 11, sy + 14); g_Context.lineTo(sx + 21, sy + 14);
g_Context.stroke();
break;
case 2: // zoom out
g_Context.beginPath();
g_Context.arc(sx + 16, sy + 14, 10, 0, 2 * Math.PI, false);
g_Context.moveTo(sx + 24, sy + 21); g_Context.lineTo(sx + 28, sy + 25);
g_Context.moveTo(sx + 11, sy + 14); g_Context.lineTo(sx + 21, sy + 14);
g_Context.stroke();
break;
case 3: //
<<<>
g_Context.beginPath();
g_Context.moveTo(sx + 22, sy + 7); g_Context.lineTo(sx + 8, sy + 14); g_Context.lineTo(sx + 22, sy + 21);
g_Context.stroke();
break;
case 4: // >
g_Context.beginPath();
g_Context.moveTo(sx + 11, sy + 7); g_Context.lineTo(sx + 25, sy + 14); g_Context.lineTo(sx + 11, sy + 21);
g_Context.stroke();
break;
case 5: // collapse
channel on or off
g_Context.lineWidth = 2;
g_Context.beginPath();
if (g_bCollaped) {
drawLine(sx + 6, sy + 14, sx + 27, sy + 14);
drawLine(sx + 8, sy + 12, sx + 12, sy + 8);
drawLine(sx + 12, sy + 8, sx + 16, sy + 12);
drawLine(sx + 16, sy + 12, sx + 21, sy + 7);
drawLine(sx + 21, sy + 7, sx + 25, sy + 11);
drawLine(sx + 8, sy + 19, sx + 11, sy + 16);
drawLine(sx + 11, sy + 16, sx + 16, sy + 21);
drawLine(sx + 16, sy + 21, sx + 21, sy + 16);
drawLine(sx + 21, sy + 16, sx + 26, sy + 20);
} else {
drawLine(sx + 8, sy + 15, sx + 12, sy + 11);
drawLine(sx + 12, sy + 11, sx + 19, sy + 18);
drawLine(sx + 19, sy + 18, sx + 25, sy + 9);
}
g_Context.stroke();
g_Context.lineWidth = 3;
break;
case 6: // change
channel number
g_Context.textAlign = "center";
g_Context.fillText("CH+", sx + 17, sy + 19);
break;
}
}
}
function drawLine(sx, sy, ex, ey) {
g_Context.moveTo(sx, sy); g_Context.lineTo(ex, ey);
}
</script>
</head>
<body onload="initialize()">
<canvas id='oscilloscopeCanvas' style='position:absolute;
left:0px; top:0px;'></canvas>
</body>
</html>
댓글 없음:
댓글 쓰기