add server

This commit is contained in:
Kizarm 2024-10-25 15:20:26 +02:00
parent b2c0bc43fe
commit b7b1a581f0
9 changed files with 757 additions and 2 deletions

4
.gitignore vendored
View file

@ -15,7 +15,9 @@
V203/pwm/sin.c V203/pwm/sin.c
V203/gsm/lib/libgsm.a V203/gsm/lib/libgsm.a
V203/usb/cdc/mystrings.inc 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/.qmake.stash
V203/usb/scope/software/Makefile V203/usb/scope/software/Makefile
V203/usb/scope/software/moc/* V203/usb/scope/software/moc/*

View file

@ -0,0 +1,93 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Osciloskop</title>
<style>
html,
body {
height: 98%;
margin: 0;
padding: 0;
}
.canvas {
width: 98vw;
height: 78vh;
background-color:black;
}
.frame1 {
width: 90%;
margin: auto;
background-color: #FFFFC0;
border: 10px solid #F0C0F0;
}
fieldset {
background-color: #C0FFC0;
height: 100%;
}
tr,td {
height: 100%;
}
.start {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<canvas id="canvas" class="canvas"></canvas>
<div class="frame1">
<table><tr>
<td>
<div><input type="button" value="Connect" id="Connect" onclick="Connect();" /></div>
<div><p id="Connected">Disconnected</p></div>
</td>
<td><fieldset>
<legend>Trigger</legend>
<div><select id="trigger_src" name="trigger_src">
<option value="0">Channel A</option>
<option value="1">Channel B</option>
</select></div>
<div><select id="trigger_mode" name="trigger_mode">
<option value="0">Auto </option>
<option value="1">Normal</option>
<option value="2">Single</option>
</select></div>
<div><select id="trigger_edge" name="trigger_edge">
<option value="0">Rissing</option>
<option value="1">Falling</option>
</select></div>
</fieldset></td>
<td><fieldset>
<legend>Time Base</legend>
<div><select id="time_base" name="time_base">
<option value="0">2μs</option> <option value="1">5μs</option> <option value="2">10μs</option>
<option value="3">20μs</option> <option value="4">50μs</option> <option value="5">100μs</option>
<option value="6">200μs</option><option value="7">500μs</option><option value="8">1ms</option>
<option value="9">2ms</option> <option value="10">5ms</option> <option value="11">10ms</option>
<option value="12">20ms</option> <option value="13">50ms</option> <option value="14">100ms</option>
<option value="15">200ms</option><option value="16">500ms</option><option value="17">1s</option>
</select></div>
</fieldset></td>
<td><fieldset>
<legend>Markers</legend>
<div><input type="radio" id="time" name="markers" value="time" checked /><labelfor="time">Time</label></div>
<div><input type="radio" id="volt" name="markers" value="volt" /><labelfor="volt">Voltage</label></div>
</fieldset></td>
<td><fieldset>
<legend>Item to move</legend>
<div><select id="move" name="move">
<option value="0">Trigger value</option>
<option value="1">Trigger offset</option>
<option value="2">Marker A</option>
<option value="3">Marker B</option>
<option value="4">Time base zoom</option>
</select></div>
</fieldset></td>
<td><input type="button" class="start" id="Start" value="Start" onclick="Start();" disabled></td>
</tr></table>
</div>
<script src="index.js"></script>
</body>
</html>

203
V203/usb/scope/bin/index.js Normal file
View file

@ -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<max; n++) {
out [n] = data [n];
}
}
var ctx = Canvas.getContext('2d');
ctx.lineWidth = 2.0;
ctx.strokeStyle = col;
ctx.beginPath();
ctx.moveTo (gTB[0], out[0] * gMZ.m22 + gMZ.oy);
for (let n=1; n<1024; n++) {
ctx.lineTo(gTB[n], out[n] * gMZ.m22 + gMZ.oy);
}
ctx.stroke();
};
Canvas.addEventListener ('click', function(event) {
const p = { x : event.clientX, y : event.clientY };
const q = InPt (p.x, p.y);
switch (gItemToMove) {
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;
default: return;
};
DrawTrig ();
});
/************************************************************************/
TriggerSrc.onchange = (event) => {
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<MAX_X; n++) {
gTB.push (n * gMZ.m11 + gMZ.ox);
gCA.push (2000);
gCB.push (1000);
}
wsUri = js_get_id ();
};
function Connect () {
if (connected) {
websocket.close();
} else {
initWebSocket ();
}
/*
DrawTrig ();
const d1 = [10,20,30,40,50];
const d2 = [3000];
DrawPolyLine (0, d1, '#FF0000');
DrawPolyLine (1, d2, '#00FF00');
*/
};
function Start () {
SendEvent ({ type : 'start', value : 1 });
};

View file

@ -0,0 +1,20 @@
PR = ../bin/wserver
CC = g++
AS = as
CFLAGS = -Wall -Os
CFLAGS+= -I$(HOME)/local/include
MFLAGS = -o $(PR)
LFLAGS = -L$(HOME)/local/lib -lseasocks -lz -lpthread
all: $(PR)
OBJECTS = main.o wsclient.o
$(PR): $(OBJECTS)
$(CC) $(AFLAGS) $(MFLAGS) $(OBJECTS) $(LFLAGS)
clean:
rm -f *.o *.lst *~
%.o: %.cpp
$(CC) -std=c++17 $(AFLAGS) -c $(CFLAGS) $< -o $@
%.o: %.S
$(AS) $< -o $@

View file

@ -0,0 +1,97 @@
#include "seasocks/PrintfLogger.h"
#include "seasocks/Server.h"
#include "seasocks/StringUtil.h"
#include "seasocks/WebSocket.h"
#include "seasocks/PageHandler.h"
#include "seasocks/SimpleResponse.h"
#include "seasocks/util/Json.h"
#include "wsclient.h"
#include <cstring>
#include <iostream>
#include <fstream>
#include <memory>
#include <map>
#include <sstream>
#include <string>
#include <thread>
#include <set>
#include <fcntl.h>
#include <unistd.h>
#include <sys/epoll.h>
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<WsClient*>(ptr);
if (dz) dz->receive();
}
private:
int & _epollFd;
std::map<WebSocket*, WsClient*> _connections;
};
int main (int /*argc*/, const char * /*argv*/[]) {
auto logger = std::make_shared<PrintfLogger> (Logger::Level::Info);
int myEpoll = epoll_create (10);
Server server (logger);
auto handler = std::make_shared<WsHandler> (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;
}

View file

@ -0,0 +1 @@
../firmware/structures.h

View file

@ -0,0 +1,275 @@
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <termios.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <nlohmann/json.hpp>
#include "wsclient.h"
using json = nlohmann::json;
using namespace std;
using namespace seasocks;
static string channels_to_json (const vector<int> & a, const vector<int> & 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<r; n++) {
const char c = tmpbuf [n];
if (c == '\r') continue;
if (c == '\n') {end = true; break;}
recbuff [recindex++] = c;
}
if (end) {
recbuff [recindex++] = '@';
recbuff [recindex ] = '\0';
received_pack();
}
}
void WsClient::received_pack() {
//printf("(%d)received:%s\n", recindex, recbuff);
parse_input (recbuff, recindex);
recindex = 0;
}
void WsClient::parse_input(const char * data, const long len) {
for (long i=0; i<len; i++) {
const char c = data [i];
switch (c) {
case '$':
state = StateHeader;
packet_cnt = 0;
break;
case '#':
parse_header();
state = StateData;
packet_cnt = 0;
break;
case '@':
parse_packet();
state = StateIdle;
packet_cnt = 0;
break;
default:
packet_buf [packet_cnt] = c;
packet_cnt += 1;
if (packet_cnt >= 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<int> ChA, ChB;
if (state != StateData) return;
int k=0;
for (int n=0; n<packet_cnt; n+=3) {
char buf [4];
memcpy (buf, packet_buf + n, 3);
buf[3] = '\0';
const int sample = strtol (buf, nullptr, 16);
if (k & 1) {
ChB.push_back (sample);
} else {
ChA.push_back (sample);
}
k += 1;
}
bool sok = true;
const int Al = ChA.size(), Bl = ChB.size();
if (Al != (int) header.bits.pack_len) sok = false;
if (Bl != (int) header.bits.pack_len) sok = false;
if (sok) {
string outs = channels_to_json(ChA, ChB);
// cout << outs << endl;
ws->send(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<ADC_CHANNELS> (n);
send_trig_mode();
}
void WsClient::SendTrigerMode(int n) {
trigerSettings.mode = static_cast<TRIGER_MODE> (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<MOVE_ITEMS>(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() {
}

View file

@ -0,0 +1,62 @@
#ifndef WSCLIENT_H
#define WSCLIENT_H
#include "seasocks/Server.h"
#include "seasocks/WebSocket.h"
#include <filesystem>
#include <chrono>
#include <string>
#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

View file

@ -101,7 +101,9 @@ void DataSource::parse_packet() {
if ((al != 1ul) and (al != 1024ul)) { qDebug ("A packet len = %zd", al); } 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 ((bl != 1ul) and (bl != 1024ul)) { qDebug ("B packet len = %zd", bl); }
if (sok) { 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) { if (catching) {
catching = false; catching = false;
emit Channels_received (ChA, ChB); emit Channels_received (ChA, ChB);