From b7b1a581f06b24665c4338af0ddb64427d4daf27 Mon Sep 17 00:00:00 2001 From: Kizarm Date: Fri, 25 Oct 2024 15:20:26 +0200 Subject: [PATCH] add server --- .gitignore | 4 +- V203/usb/scope/bin/index.html | 93 +++++++++ V203/usb/scope/bin/index.js | 203 ++++++++++++++++++ V203/usb/scope/server/Makefile | 20 ++ V203/usb/scope/server/main.cpp | 97 +++++++++ V203/usb/scope/server/structures.h | 1 + V203/usb/scope/server/wsclient.cpp | 275 +++++++++++++++++++++++++ V203/usb/scope/server/wsclient.h | 62 ++++++ V203/usb/scope/software/datasource.cpp | 4 +- 9 files changed, 757 insertions(+), 2 deletions(-) create mode 100644 V203/usb/scope/bin/index.html create mode 100644 V203/usb/scope/bin/index.js create mode 100644 V203/usb/scope/server/Makefile create mode 100644 V203/usb/scope/server/main.cpp create mode 120000 V203/usb/scope/server/structures.h create mode 100644 V203/usb/scope/server/wsclient.cpp create mode 100644 V203/usb/scope/server/wsclient.h diff --git a/.gitignore b/.gitignore index 29d07b9..7226500 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,9 @@ V203/pwm/sin.c V203/gsm/lib/libgsm.a V203/usb/cdc/mystrings.inc -V203/usb/scope/bin/* +V203/usb/scope/bin/osciloscope +V203/usb/scope/bin/wserver +V203/usb/scope/server/*.o V203/usb/scope/software/.qmake.stash V203/usb/scope/software/Makefile V203/usb/scope/software/moc/* diff --git a/V203/usb/scope/bin/index.html b/V203/usb/scope/bin/index.html new file mode 100644 index 0000000..2b28bb9 --- /dev/null +++ b/V203/usb/scope/bin/index.html @@ -0,0 +1,93 @@ + + + + + + Osciloskop + + + + +
+ + + + + + + +
+
+

Disconnected

+
+ Trigger +
+
+
+
+ Time Base +
+
+ Markers +
Time
+
Voltage
+
+ Item to move +
+
+
+ + + diff --git a/V203/usb/scope/bin/index.js b/V203/usb/scope/bin/index.js new file mode 100644 index 0000000..7457ada --- /dev/null +++ b/V203/usb/scope/bin/index.js @@ -0,0 +1,203 @@ +const TriggerSrc = document.getElementById ('trigger_src'); +const TriggerMode = document.getElementById ('trigger_mode'); +const TriggerEdge = document.getElementById ('trigger_edge'); + +const MarkerTime = document.getElementById ('time'); +const MarkerVolt = document.getElementById ('volt'); + +const TimeBase = document.getElementById ('time_base'); +const MoveElem = document.getElementById ('move'); +const StartBut = document.getElementById ('Start'); +const ConnectBut = document.getElementById ('Connect'); +const Connected = document.getElementById ('Connected'); +const Canvas = document.getElementById ('canvas'); + +const REF_Y = 3.3; +const MAX_Y = 4096.0; +const MAX_X = 1024.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 }; +const gTB = new Array (); +const gCA = new Array (); +const gCB = new Array (); +var gItemToMove = 0, gIndex = 512; +/************************************************************************/ +var websocket = null; +var wsUri = null; +var connected = false; + +function js_get_id () { + var host; + const locate = window.location; + if (locate.protocol == 'file:') host = 'ws://unknown'; + else if (locate.port) host = 'ws://' + locate.hostname + ':' + (parseInt(locate.port, 10) + 0) + '/ws'; + else host = 'ws://' + locate.hostname + '/ws'; + console.log ('host is: ' + host); + return host; +} +function initWebSocket() { + try { + if (typeof MozWebSocket == 'function') WebSocket = MozWebSocket; + if ( websocket && websocket.readyState == 1 ) websocket.close(); + websocket = new WebSocket( wsUri ); + websocket.onopen = function (evt) { + ConnectBut.value = 'Disconnect'; + Connected.innerHTML = 'CONNECTED'; + connected = true; + }; + websocket.onclose = function (evt) { + ConnectBut.value = 'Connect'; + Connected.innerHTML = 'DISCONNECTED'; + connected = false; + }; + websocket.onmessage = function (evt) { + const obj = JSON.parse (evt.data); + DrawTrig (); + DrawPolyLine (0, obj.a, '#FF0000'); + DrawPolyLine (1, obj.b, '#00FF00'); + }; + websocket.onerror = function (evt) { + console.log ('ERROR: ' + evt.data); + }; + } catch (exception) { + console.log ('EXCEPT: ' + exception); + } +} +/************************************************************************/ +function ReloadMatrix (sz) { + const xz = sz.x / MAX_X; + const yz = sz.y / MAX_Y; + gMZ.m11 = xz; gMZ.m22 = -yz; gMZ.oy = sz.y; +}; +function TrPt (x, y) { // matice je diagonalni + const rx = (x * gMZ.m11 + gMZ.ox); + const ry = (y * gMZ.m22 + gMZ.oy); + return { x : rx, y : ry }; +}; +function InPt (x, y) { // matice je diagonalni + const rx = Math.round ((x - gMZ.ox) / gMZ.m11); + const ry = Math.round ((y - gMZ.oy) / gMZ.m22); + return { x : rx, y : ry }; +}; +function SendEvent (evt) { + console.log (evt); + if (!connected) return; + const reply = JSON.stringify(evt); + websocket.send (reply); +} +function DrawTrig () { + var ctx = Canvas.getContext('2d'); + ctx.clearRect(0, 0, Canvas.width, Canvas.height); + ctx.lineWidth = 2.0; + ctx.strokeStyle = 'blue'; + ctx.beginPath(); + var b,e; + b = TrPt (24, gTC.y); + e = TrPt (1000, gTC.y); + ctx.moveTo(b.x, b.y); + ctx.lineTo(e.x, e.y); + ctx.stroke(); + ctx.beginPath(); + b = TrPt (gTC.x, 96); + e = TrPt (gTC.x, 4000); + ctx.moveTo(b.x, b.y); + ctx.lineTo(e.x, e.y); + ctx.stroke(); +}; +function DrawPolyLine (ch, data, col) { + var out = ch === 0 ? gCA : gCB; + if (data.length === 1) { + out [gIndex] = data [0]; + if (ch === 1) { + gIndex += 1; + gIndex = gIndex % 1024; + } + } else { + const max = data.length < 1024 ? data.length : 1024; + for (let n=0; n { + const e = { type : "trg_src", value : parseInt(event.target.value, 10) }; + SendEvent (e); +}; +TriggerMode.onchange = (event) => { + const e = { type : "trg_mod", value : parseInt(event.target.value, 10) }; + SendEvent (e); + const res = event.target.value; + if (e.value === 2) { + StartBut.disabled = false; + } else { + StartBut.disabled = true; + } +}; +TriggerEdge.onchange = (event) => { + const e = { type : "trg_edg", value : parseInt(event.target.value, 10) }; + SendEvent (e); +}; +TimeBase.onchange = (event) => { + const e = { type : "tim_bas", value : parseInt(event.target.value, 10) }; + SendEvent (e); +}; +MoveElem.onchange = (event) => { + const e = { type : "mov_ele", value : parseInt(event.target.value, 10) }; + gItemToMove = e.value; + console.log(e); +}; +MarkerTime.onclick = (event) => { + console.log ('time_mark'); +}; +MarkerVolt.onclick = (event) => { + console.log ('volt_mark'); +}; +window.onload = (event) => { + const dim = { x : Canvas.clientWidth, y : Canvas.clientHeight }; + Canvas.width = dim.x; Canvas.height = dim.y; + ReloadMatrix (dim); + for (let n=0; n +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace seasocks; + +class WsHandler : public WebSocket::Handler { + public: + explicit WsHandler (int & epollFd) + : _epollFd (epollFd) { + } + void onConnect (WebSocket * connection) override { + WsClient * dz = new WsClient (connection, _epollFd); + _connections.insert ( {connection, dz}); + if (dz->start()) printf ("WsClient started\r\n"); + std::cout << "Connected: " << connection->getRequestUri() + << " : " << formatAddress (connection->getRemoteAddress()) + << "\r\nCredentials: " << * (connection->credentials()) << "\r\n"; + } + void onData (WebSocket * connection, const char * data) override { + WsClient * dz = _connections.at (connection); + dz->send (data); + } + void onDisconnect (WebSocket * connection) override { + WsClient * dz = _connections.at (connection); + dz->stop(); + delete dz; + _connections.erase (connection); + std::cout << "Disconnected: " << connection->getRequestUri() + << " : " << formatAddress (connection->getRemoteAddress()) << "\r\n"; + } + void poll (void * ptr) { + WsClient * dz = static_cast(ptr); + if (dz) dz->receive(); + } + private: + int & _epollFd; + std::map _connections; +}; +int main (int /*argc*/, const char * /*argv*/[]) { + auto logger = std::make_shared (Logger::Level::Info); + int myEpoll = epoll_create (10); + Server server (logger); + + auto handler = std::make_shared (myEpoll); + server.addWebSocketHandler ("/ws", handler); + server.setStaticPath ("."); + if (!server.startListening (8080)) { + std::cerr << "couldn't start listening\n"; + return 1; + } + epoll_event wakeSeasocks = {EPOLLIN | EPOLLOUT | EPOLLERR, {&server}}; + epoll_ctl (myEpoll, EPOLL_CTL_ADD, server.fd(), &wakeSeasocks); + + while (true) { + constexpr auto maxEvents = 2; + epoll_event events[maxEvents]; + auto res = epoll_wait (myEpoll, events, maxEvents, -1); + if (res < 0) { + std::cerr << "epoll returned an error\n"; + return 1; + } + for (auto i = 0; i < res; ++i) { + if (events[i].data.ptr == &server) { + auto seasocksResult = server.poll (0); + if (seasocksResult == Server::PollResult::Terminated) { + return 0; + } + if (seasocksResult == Server::PollResult::Error) + return 1; + } else { + handler->poll (events[i].data.ptr); + } + } + } + + return 0; +} diff --git a/V203/usb/scope/server/structures.h b/V203/usb/scope/server/structures.h new file mode 120000 index 0000000..693584d --- /dev/null +++ b/V203/usb/scope/server/structures.h @@ -0,0 +1 @@ +../firmware/structures.h \ No newline at end of file diff --git a/V203/usb/scope/server/wsclient.cpp b/V203/usb/scope/server/wsclient.cpp new file mode 100644 index 0000000..2bdc852 --- /dev/null +++ b/V203/usb/scope/server/wsclient.cpp @@ -0,0 +1,275 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wsclient.h" + +using json = nlohmann::json; +using namespace std; +using namespace seasocks; + +static string channels_to_json (const vector & a, const vector & b) { + string res ("{\"a\":["); + for (const int e: a) { + res += to_string(e) + ","; + } + res = res.substr(0, res.length() - 1); + res+= "],\"b\":["; + for (const int e: b) { + res += to_string(e) + ","; + } + res = res.substr(0, res.length() - 1); + res+= "]}"; + return res; +} +bool WsClient::start() { + cout << "Client Start\n"; + const char * name = "/dev/serial/by-id/usb-Kizarm_Labs._USB_Osciloscope_00001-if00"; + running = false; + fd = ::open (name, O_RDWR); + if (fd < 0) return running; + + epoll_event wakeUsart = {EPOLLIN, {this}}; + epoll_ctl (pollfd, EPOLL_CTL_ADD, fd, &wakeUsart); + + struct termios LineFlags; + int attr = tcgetattr (fd, &LineFlags); + if (attr) { + printf ("Nelze zjistit parametry portu %s\r\n", name); + ::close (fd); + return running; + } + // nastaveni komunikacnich parametru do struct termios + LineFlags.c_iflag = IGNPAR /* | IXON | IXOFF*/; // ignoruj chyby parity + LineFlags.c_oflag = 0; // normalni nastaveni + LineFlags.c_cflag = CS8 | CREAD | CLOCAL; // 8-bit, povol prijem + LineFlags.c_lflag = 0; // Raw input bez echa + LineFlags.c_cc [VMIN] = 1; // minimalni pocet znaku pro cteni + LineFlags.c_cc [VTIME] = 1; // read timeout 0.1 s + + tcsetattr (fd, TCSANOW, &LineFlags); + fcntl (fd, F_SETFL, 0); + + int flag = TIOCM_DTR; + ioctl(fd, TIOCMBIS, & flag); + + printf ("Port %s opened (%d)\r\n", name, fd); + usleep (1000); + running = true; + return true; +} +void WsClient::stop() { + cout << "Client Stop\n"; + epoll_ctl (pollfd, EPOLL_CTL_DEL, fd, nullptr); + int flag = TIOCM_DTR; + ioctl(fd, TIOCMBIC, & flag); + ::close (fd); + running = false; +} +void WsClient::receive() { + if (!running) return; + const int recmax = 64; + char tmpbuf [recmax]; + ssize_t r = ::read (fd, tmpbuf, recmax); + bool end = false; + for (int n=0; n= DATASIZE) { + packet_cnt = 0; + printf ("Buffer overflow\n"); + } + break; + } + } +} +void WsClient::parse_header() { + if (packet_cnt != 4) return; + packet_buf [4] = '\0'; + const long hdr = strtoul (packet_buf, nullptr, 16); + header.common = hdr; + /* + //qDebug ("header:%04X", hdr); + if (header.bits.trig_flg) { + emit PaketTriggered (); + } + */ +} +void WsClient::parse_packet() { + vector ChA, ChB; + if (state != StateData) return; + int k=0; + for (int n=0; nsend(outs); + /* + if (trigerSettings.mode == TRIGER_MODE_SINGLE) { + if (catching) { + catching = false; + emit Channels_received (ChA, ChB); + } + } else { + emit Channels_received (ChA, ChB); + } + */ + } else { + printf ("packet error: ChA=%d, ChB=%d, size=%d\n", Al, Bl, header.bits.pack_len); + } +} + +/*******************************************************************/ +void WsClient::send(const char * data) { + // cout << "data is \"" << data << "\"\n"; // prijata data + json received = json::parse (string (data)); + int value = 0; + if (!received["value"].is_null()) { + received["value"].get_to (value); + } + string type; + if (!received["type"].is_null()) { + received["type"].get_to (type); + if (type == "trg_src") { + SendTrigerChan(value); + } else if (type == "trg_mod") { + SendTrigerMode(value); + } else if (type == "trg_edg") { + SendTrigerEdge(value); + } else if (type == "trg_val") { + trigerSettings.value = value; + SettingChanged(0); + } else if (type == "trg_ofs") { + trigerSettings.offset = value; + SettingChanged(1); + } else if (type == "tim_bas") { + SendBaseRange(value); + } else if (type == "start") { + Start (); + } + } + // cout << "type=" << type << ", value=" << value << endl; +} +void WsClient::SendTrigerChan(int n) { + trigerSettings.channel = static_cast (n); + send_trig_mode(); +} +void WsClient::SendTrigerMode(int n) { + trigerSettings.mode = static_cast (n); + send_trig_mode(); +} +void WsClient::SendTrigerEdge(int n) { + trigerSettings.rising = n ? true : false; + send_trig_mode(); +} +void WsClient::SettingChanged(int n) { + const MOVE_ITEMS items = static_cast(n); + RcvdHeader hdr; + hdr.common = 0u; + hdr.bits.destinat = DEST_TRIG; + switch (items) { + case MOVE_VALUE: hdr.bits.cmd_type = TRIGGER_CMD_VALUE; hdr.bits.cmd_value = trigerSettings.value; break; + case MOVE_OFSET: hdr.bits.cmd_type = TRIGGER_CMD_OFSET; hdr.bits.cmd_value = trigerSettings.offset; break; + default : break; + } + const unsigned len = 64; + char buffer [len]; + int r = snprintf(buffer, len, "$%04X\r\n", (int) hdr.common); + buffer [r] = '\0'; + write (buffer, r); +} +void WsClient::SendBaseRange(int n) { + RcvdHeader hdr; + hdr.common = 0u; + hdr.bits.destinat = DEST_BASE; + hdr.bits.cmd_value = n; + const unsigned len = 64; + char buffer [len]; + int r = snprintf(buffer, len, "$%04X\r\n", (int) hdr.common); + buffer [r] = '\0'; + write (buffer, r); +} +void WsClient::send_trig_mode() { + RcvdHeader hdr; + hdr.common = 0u; + hdr.bits.destinat = DEST_TRIG; + hdr.bits.cmd_type = TRIGGER_CMD_MODE; + TriggerModeUnion tmu; + tmu.common = 0u; + tmu.bits.mode = trigerSettings.mode; + tmu.bits.channel = trigerSettings.channel; + tmu.bits.rissing = trigerSettings.rising ? 1u : 0u; + hdr.bits.cmd_value = tmu.common; + + const unsigned len = 64; + char buffer [len]; + int r = snprintf(buffer, len, "$%04X\r\n", (int) hdr.common); + buffer [r] = '\0'; + write (buffer, r); +} +int WsClient::write(const char * data, const int len) { + string out (data, len); + cout << out; + int r = ::write (fd, data, len); + (void) r; + //ws->send(out); + return len; +} +void WsClient::Start() { +} + diff --git a/V203/usb/scope/server/wsclient.h b/V203/usb/scope/server/wsclient.h new file mode 100644 index 0000000..9277902 --- /dev/null +++ b/V203/usb/scope/server/wsclient.h @@ -0,0 +1,62 @@ +#ifndef WSCLIENT_H +#define WSCLIENT_H + +#include "seasocks/Server.h" +#include "seasocks/WebSocket.h" +#include +#include +#include +#include "structures.h" + +static constexpr int DATASIZE = 0x2000; +enum ParserState { + StateIdle = 0, + StateHeader, + StateData, +}; + +class WsClient { + seasocks::WebSocket * ws; + int & pollfd; + TrigerSettings trigerSettings; + bool running; + int fd; + const size_t reclen; + char * recbuff; + int recindex; + + ParserState state; + int packet_cnt; + char * packet_buf; + SendHeader header; + public: + explicit WsClient(seasocks::WebSocket * p, int & fd) : ws (p), pollfd (fd), + trigerSettings(), running(false), fd(0), reclen(0x4000ul), + recindex(0), state(StateIdle), packet_cnt(0) { + recbuff = new char [reclen]; + packet_buf = new char [DATASIZE]; + } + virtual ~WsClient() { + delete [] recbuff; + delete [] packet_buf; + }; + bool start (); + void stop (); + void send (const char * data); + void receive (); + protected: + void SendTrigerMode (int n); + void SendTrigerEdge (int n); + void SendTrigerChan (int n); + void SendBaseRange (int n); + void SettingChanged (int n); + void Start (); + void send_trig_mode (); + int write (const char * data, const int len); + void received_pack (); + void parse_input (const char * data, const long len); + void parse_header (); + void parse_packet (); +}; + +#endif // WSCLIENT_H diff --git a/V203/usb/scope/software/datasource.cpp b/V203/usb/scope/software/datasource.cpp index 4c9c7a9..152c018 100644 --- a/V203/usb/scope/software/datasource.cpp +++ b/V203/usb/scope/software/datasource.cpp @@ -101,7 +101,9 @@ void DataSource::parse_packet() { if ((al != 1ul) and (al != 1024ul)) { qDebug ("A packet len = %zd", al); } if ((bl != 1ul) and (bl != 1024ul)) { qDebug ("B packet len = %zd", bl); } if (sok) { - if (trigerSettings.mode == TRIGER_MODE_SINGLE) { + if ((al == 1ul) or (bl == 1ul)) { // v kontinuálním módu odešli vždy + emit Channels_received (ChA, ChB); + } else if (trigerSettings.mode == TRIGER_MODE_SINGLE) { if (catching) { catching = false; emit Channels_received (ChA, ChB);