mirror of
https://github.com/eddyem/BTA_utils.git
synced 2025-12-06 02:35:13 +03:00
265 lines
8.3 KiB
C
265 lines
8.3 KiB
C
/*
|
|
* This file is part of the canserver project.
|
|
* Copyright 2025 Edward V. Emelianov <edward.emelianoff@gmail.com>.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <inttypes.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
|
|
#include "clientserver.h"
|
|
#include "parsecanmsgs.h"
|
|
|
|
static pthread_t clientthread;
|
|
static sl_sock_t *serialsock = NULL, *serversock = NULL;
|
|
|
|
#define CMDIN "in"
|
|
#define CMDOUT "out"
|
|
#define CMDMCUT "mcut"
|
|
#define CMDLED "led"
|
|
#define CMDINCH "inchnls"
|
|
#define CMDOUTCH "outchnls"
|
|
#define CMDTIME "time"
|
|
#define CMDPING "ping"
|
|
|
|
typedef struct{
|
|
const char *textcmd;
|
|
int cancmd;
|
|
int havesetter;
|
|
} commands;
|
|
|
|
// list of all supported commands
|
|
static commands allcommands[] = {
|
|
{CMDIN, CMD_GETESW, 0},
|
|
{CMDOUT, CMD_RELAY, 1},
|
|
{CMDMCUT, CMD_MCUTEMP, 0},
|
|
{CMDLED, CMD_LED, 1},
|
|
{CMDINCH, CMD_INCHNLS, 0},
|
|
{CMDOUTCH, CMD_OUTCHNLS, 0},
|
|
{CMDTIME, CMD_TIME, 1},
|
|
{CMDPING, CMD_PING, 1},
|
|
{NULL, 0, 0}
|
|
};
|
|
|
|
void killsockets(){
|
|
if(serialsock) sl_sock_delete(&serialsock);
|
|
if(serversock) sl_sock_delete(&serversock);
|
|
}
|
|
|
|
static int Relay1cmds(CAN_message *msg, char buf[BUFSIZ]){
|
|
int L = 0;
|
|
uint16_t cmd = MSGP_GET_CMD(msg);
|
|
uint8_t par = PARVAL(msg->data);
|
|
uint32_t data = MSGP_GET_U32(msg);
|
|
commands *c = allcommands;
|
|
while(c->textcmd){
|
|
if(cmd == c->cancmd) break;
|
|
++c;
|
|
}
|
|
if(!c->textcmd) return 0;
|
|
DBG("found text cmd is %s (%u)", c->textcmd, data);
|
|
//L = snprintf(buf, BUFSIZ-1, "%s=%u\n", c->textcmd, data);
|
|
if(par == NO_PARNO) L = snprintf(buf, BUFSIZ-1, "%s=%u\n", c->textcmd, data);
|
|
else L = snprintf(buf, BUFSIZ-1, "%s[%d]=%u\n", c->textcmd, par, data);
|
|
return L;
|
|
}
|
|
|
|
// check CAN_message and if recognize send ans to all clients
|
|
static void gotCANans(CAN_message *msg){
|
|
if(!msg) return;
|
|
char buf[BUFSIZ];
|
|
int L = 0;
|
|
switch(msg->ID){
|
|
case CANIDOUT:
|
|
L = Relay1cmds(msg, buf);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
if(L < 1) return;
|
|
DBG("BUF: %s", buf);
|
|
int N = sl_sock_sendall(serversock, (uint8_t*) buf, L);
|
|
green("Send to %d clients\n", N);
|
|
}
|
|
|
|
/**
|
|
* @brief clientproc - process data received from serial terminal
|
|
* @param par - socket
|
|
* @return NULL
|
|
*/
|
|
void *clientproc(void _U_ *par){
|
|
FNAME();
|
|
char rbuf[BUFSIZ];
|
|
if(!serialsock) return NULL;
|
|
do{
|
|
ssize_t got = sl_sock_readline(serialsock, rbuf, BUFSIZ);
|
|
if(got < 0){ // disconnected
|
|
WARNX("Serial server disconnected");
|
|
if(serialsock) sl_sock_delete(&serialsock);
|
|
return NULL;
|
|
}else if(got == 0){ // nothing to read from serial port
|
|
usleep(1000);
|
|
continue;
|
|
}
|
|
// process data
|
|
DBG("GOT: %s", rbuf);
|
|
CAN_message msg;
|
|
if(parseCANstr(rbuf, &msg)){
|
|
DBG("Got CAN message");
|
|
gotCANans(&msg);
|
|
}
|
|
} while(serialsock && serialsock->connected);
|
|
WARNX("disconnected");
|
|
if(serialsock) sl_sock_delete(&serialsock);
|
|
return NULL;
|
|
}
|
|
|
|
static sl_sock_hresult_e dtimeh(sl_sock_t *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
|
char buf[32];
|
|
snprintf(buf, 31, "UNIXT=%.2f\n", sl_dtime());
|
|
sl_sock_sendstrmessage(client, buf);
|
|
return RESULT_SILENCE;
|
|
}
|
|
|
|
static sl_sock_hresult_e procUserCmd(sl_sock_t _U_ *client, sl_sock_hitem_t *item, const char *req){
|
|
#define CBUFLEN (128)
|
|
char buf[CBUFLEN];
|
|
int buflen = 0;
|
|
CAN_message msg;
|
|
msg.ID = CANIDIN;
|
|
int issetter = 0;
|
|
uint8_t parno = NO_PARNO;
|
|
uint32_t setterval = 0;
|
|
if(req){
|
|
int N;
|
|
if(!sl_str2i(&N, req) || N < 0){
|
|
DBG("BAD value %d", N);
|
|
return RESULT_BADVAL;
|
|
}
|
|
setterval = (uint32_t) N;
|
|
issetter = 1;
|
|
}
|
|
commands *c = allcommands;
|
|
while(c->textcmd){
|
|
if(0 == strcmp(c->textcmd, item->key)) break;
|
|
++c;
|
|
}
|
|
if(!c->textcmd) return RESULT_BADKEY;
|
|
if(issetter){
|
|
DBG("setter");
|
|
if(!c->havesetter) return RESULT_BADVAL;
|
|
if(CANu32setter(c->cancmd, parno, setterval, &msg) && (buflen = formCANmsg(&msg, buf, CBUFLEN-1)) > 0){
|
|
}else{
|
|
WARNX("Can't form message");
|
|
return RESULT_FAIL;
|
|
}
|
|
}else{
|
|
DBG("getter");
|
|
if(CANu32getter(c->cancmd, parno, &msg) && (buflen = formCANmsg(&msg, buf, CBUFLEN-1)) > 0){
|
|
}else{
|
|
WARNX("Can't form message");
|
|
return RESULT_FAIL;
|
|
}
|
|
}
|
|
if(buflen) sl_sock_sendstrmessage(serialsock, buf);
|
|
return RESULT_SILENCE;
|
|
#undef CBUFLEN
|
|
}
|
|
/*
|
|
static sl_sock_hresult_e show(sl_sock_t *client, _U_ sl_sock_hitem_t *item, _U_ const char *req){
|
|
if(!client) return RESULT_FAIL;
|
|
if(client->type != SOCKT_UNIX){
|
|
if(*client->IP){
|
|
printf("Client \"%s\" (fd=%d) ask for flags:\n", client->IP, client->fd);
|
|
}else printf("Can't get client's IP, flags:\n");
|
|
}else printf("Socket fd=%d asks for flags:\n", client->fd);
|
|
return RESULT_OK;
|
|
}*/
|
|
|
|
// Too much clients handler
|
|
static void toomuch(int fd){
|
|
const char m[] = "Try later: too much clients connected\n";
|
|
send(fd, m, sizeof(m)-1, MSG_NOSIGNAL);
|
|
shutdown(fd, SHUT_WR);
|
|
DBG("shutdown, wait");
|
|
double t0 = sl_dtime();
|
|
uint8_t buf[8];
|
|
while(sl_dtime() - t0 < 11.){
|
|
if(sl_canread(fd)){
|
|
ssize_t got = read(fd, buf, 8);
|
|
DBG("Got=%zd", got);
|
|
if(got < 1) break;
|
|
}
|
|
}
|
|
DBG("Disc after %gs", sl_dtime() - t0);
|
|
LOGWARN("Client fd=%d tried to connect after MAX reached", fd);
|
|
}
|
|
// new connections handler
|
|
static int connected(sl_sock_t *c){
|
|
if(c->type == SOCKT_UNIX) LOGMSG("New client fd=%d connected", c->fd);
|
|
else LOGMSG("New client fd=%d, IP=%s connected", c->fd, c->IP);
|
|
// here we can change client's IP and return FALSE to close it
|
|
return TRUE;
|
|
}
|
|
// disconnected handler
|
|
static void disconnected(sl_sock_t *c){
|
|
if(c->type == SOCKT_UNIX) LOGMSG("Disconnected client fd=%d", c->fd);
|
|
else LOGMSG("Disconnected client fd=%d, IP=%s", c->fd, c->IP);
|
|
}
|
|
static sl_sock_hresult_e defhandler(struct sl_sock *s, const char *str){
|
|
if(!s || !str) return RESULT_FAIL;
|
|
sl_sock_sendstrmessage(s, "You entered wrong command:\n```\n");
|
|
sl_sock_sendstrmessage(s, str);
|
|
sl_sock_sendstrmessage(s, "\n```\nTry \"help\"\n");
|
|
return RESULT_SILENCE;
|
|
}
|
|
|
|
static sl_sock_hitem_t handlers[] = {
|
|
{dtimeh, "dtime", "get server's UNIX time for all clients connected", NULL},
|
|
{procUserCmd, CMDOUT, "outputs setter/getter", NULL},
|
|
{procUserCmd, CMDIN, "inputs getter", NULL},
|
|
{procUserCmd, CMDMCUT, "get MCU temperature (/10degC)", NULL},
|
|
{procUserCmd, CMDLED, "onboard LED set/get", NULL},
|
|
{procUserCmd, CMDINCH, "get available input channels mask", NULL},
|
|
{procUserCmd, CMDOUTCH, "get available output channels mask", NULL},
|
|
{procUserCmd, CMDTIME, "get MCU milliseconds counter value", NULL},
|
|
{procUserCmd, CMDPING, "ping controller", NULL},
|
|
{NULL, NULL, NULL, NULL}
|
|
};
|
|
|
|
sl_sock_t *RunSrv(sl_socktype_e type, const char *node){
|
|
serversock = sl_sock_run_server(type, node, 4096, handlers);
|
|
if(!serversock) return NULL;
|
|
sl_sock_maxclhandler(serversock, toomuch);
|
|
sl_sock_connhandler(serversock, connected);
|
|
sl_sock_dischandler(serversock, disconnected);
|
|
sl_sock_defmsghandler(serversock, defhandler);
|
|
return serversock;
|
|
}
|
|
|
|
sl_sock_t *RunClt(sl_socktype_e type, const char *node){
|
|
DBG("run client type %d node %s", type, node);
|
|
serialsock = sl_sock_run_client(type, node, 4096);
|
|
if(!serialsock){
|
|
DBG("Can't run client");
|
|
return NULL;
|
|
}
|
|
if(pthread_create(&clientthread, NULL, clientproc, NULL)) return NULL;
|
|
return serialsock;
|
|
}
|