mirror of
https://github.com/eddyem/BTA_utils.git
synced 2026-08-12 09:39:34 +03:00
420 lines
12 KiB
C
420 lines
12 KiB
C
/*
|
|
* This file is part of the sslsosk project.
|
|
* Copyright 2023 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 <float.h>
|
|
#include <signal.h>
|
|
#include <usefull_macros.h>
|
|
|
|
#include "bta_shdata.h"
|
|
#include "client.h"
|
|
#include "cmdlnopts.h"
|
|
#include "daemon.h" // isrunning
|
|
#include "handlers_list.h"
|
|
#include "motors.h" // motors amount, motor_state_t
|
|
#include "sslsock.h"
|
|
|
|
|
|
#define NEW_HANDLER(name, unused) \
|
|
static const char* cmd_ ## name = STR(name);
|
|
HANDLERS_LIST()
|
|
#undef NEW_HANDLER
|
|
|
|
static const char *Ecodes[RESULT_SILENCE] = {
|
|
[RESULT_OK] = "OK",
|
|
[RESULT_BADVAL] = "BADVAL",
|
|
[RESULT_BADKEY] = "BADKEY",
|
|
[RESULT_FAIL] = "FAIL",
|
|
};
|
|
|
|
typedef enum{
|
|
ARG_TYPE_INT,
|
|
ARG_TYPE_DOUBLE
|
|
} arg_type_t;
|
|
|
|
typedef struct{
|
|
union{
|
|
double d;
|
|
int64_t i;
|
|
};
|
|
arg_type_t type;
|
|
} value_t;
|
|
|
|
// setter: when setspeed command sent successfully
|
|
static double set_speed = 0.;
|
|
// common state - by last motor polled
|
|
static motor_state_t CommonState = {0};
|
|
// state of each motor
|
|
static motor_state_t MotorState[MOTORS_AMOUNT] = {0};
|
|
|
|
static int SSL_nbread(SSL *ssl, char *buf, int bufsz){
|
|
struct pollfd fds = {0};
|
|
int fd = SSL_get_fd(ssl);
|
|
fds.fd = fd;
|
|
fds.events = POLLIN;
|
|
if(poll(&fds, 1, 1) < 0){ // wait no more than 1ms
|
|
LOGWARN("SSL_nbread(): poll() failed");
|
|
WARNX("poll()");
|
|
return 0;
|
|
}
|
|
if(fds.revents == POLLIN){
|
|
// DBG("Got info in fd #%d", fd);
|
|
int l = read_string(ssl, buf, bufsz);
|
|
// DBG("read %d bytes", l);
|
|
return l;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief send_motor_command - send motor command and parse answer
|
|
* @param ssl - ssl
|
|
* @param cmd - command to send
|
|
* @param setter - command setter (or NULL for getter)
|
|
* @param getter - pointer do value changed by getter (or NULL for setter)
|
|
* @return server's answer (or OK for successfull getters)
|
|
*/
|
|
static sl_sock_hresult_e send_motor_command(SSL *ssl, const char *cmd, value_t *setter, value_t *getter){
|
|
char buf[IOBUF_LEN];
|
|
if(!ssl || !cmd) return RESULT_FAIL;
|
|
int l;
|
|
if(setter){
|
|
if(setter->type == ARG_TYPE_INT){
|
|
l = snprintf(buf, IOBUF_LEN-1, "%s=%" PRId64 "\n", cmd, setter->i);
|
|
}else{
|
|
l = snprintf(buf, IOBUF_LEN-1, "%s=%g\n", cmd, setter->d);
|
|
}
|
|
}else l = snprintf(buf, IOBUF_LEN-1, "%s\n", cmd);
|
|
buf[l] = 0;
|
|
DBG("Send to server: %s", buf);
|
|
SSL_write(ssl, buf, l);
|
|
double t0 = sl_dtime();
|
|
while(sl_dtime() - t0 < G.acc_timeout){
|
|
l = SSL_nbread(ssl, buf, IOBUF_LEN-1);
|
|
if(l == 0) continue;
|
|
else if(l < 0){
|
|
LOGWARN("Server disconnected or other error");
|
|
ERRX("Disconnected");
|
|
}
|
|
buf[l] = 0;
|
|
DBG("Received: \"%s\"\n", buf);
|
|
// parser
|
|
char key[SL_KEY_LEN] = {0}, val[SL_VAL_LEN] = {0};
|
|
int got = sl_get_keyval(buf, key, val);
|
|
DBG("got=%d, key=%s, val=%s", got, key, val);
|
|
if(got == 0){
|
|
DBG("Empty answer");
|
|
continue;
|
|
}
|
|
if((!setter && got == 1) || (setter && got == 2)){ // wrong answer
|
|
DBG("wrong answer");
|
|
continue;
|
|
}
|
|
if(setter){ // check errcode in answer
|
|
if(got == 2){
|
|
DBG("Getter answer");
|
|
continue;
|
|
}
|
|
for(int i = 0; i < RESULT_SILENCE; ++i){
|
|
DBG("compare '%s' and '%s'", key, Ecodes[i]);
|
|
if(0 == strcmp(key, Ecodes[i])){
|
|
DBG("Found errcode for '%s': %d", key, i);
|
|
return i; // found
|
|
}
|
|
}
|
|
}else{ // check "cmd = val"
|
|
if(got == 1) continue;
|
|
if(strcmp(cmd, key)) continue;
|
|
if(getter){
|
|
if(getter->type == ARG_TYPE_INT){
|
|
long long ll;
|
|
if(!sl_str2ll(&ll, val)) continue;
|
|
getter->i = (int64_t) ll;
|
|
}else{
|
|
double d;
|
|
if(!sl_str2d(&d, val)) continue;
|
|
getter->d = d;
|
|
}
|
|
}
|
|
return RESULT_OK;
|
|
}
|
|
}
|
|
return RESULT_FAIL;
|
|
}
|
|
|
|
// emergency stop motors
|
|
static void stop_all(SSL *ssl){
|
|
int ntries = 0;
|
|
//value_t speed = {.type = ARG_TYPE_DOUBLE, .d = 0.};
|
|
while(ntries < 5){
|
|
//if(RESULT_OK == send_motor_command(ssl, cmd_speed, &speed, NULL)) break;
|
|
if(RESULT_OK == send_motor_command(ssl, cmd_stop, NULL, NULL)) break;
|
|
++ntries;
|
|
usleep(100000);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief check_motor - get status etc of motor number `motno`
|
|
* @param ssl - ssl
|
|
* @param motno - motor number (iterates on success)
|
|
* @return FALSE if some step failed
|
|
*/
|
|
static int check_motor(SSL *ssl, int motno){
|
|
char msg[128];
|
|
if(!ssl || motno < 0 || motno >= MOTORS_AMOUNT) return FALSE;
|
|
value_t Ival = {.type = ARG_TYPE_INT, .i = motno};
|
|
value_t Dval = {.type = ARG_TYPE_DOUBLE};
|
|
if(RESULT_OK != send_motor_command(ssl, cmd_motnum, &Ival, NULL)) return FALSE;
|
|
if(RESULT_OK != send_motor_command(ssl, cmd_motstatus, NULL, &Ival)) return FALSE;
|
|
MotorState[motno].status = (int) Ival.i;
|
|
if(RESULT_OK != send_motor_command(ssl, cmd_motspeed, NULL, &Dval)) return FALSE;
|
|
MotorState[motno].speed = Dval.d;
|
|
if(RESULT_OK != send_motor_command(ssl, cmd_motcurrent, NULL, &Dval)) return FALSE;
|
|
MotorState[motno].current = Dval.d;
|
|
// now set common state as mean of all
|
|
if(motno != MOTORS_AMOUNT - 1) return TRUE;
|
|
int status = 0, N = 0; // mean status is largest
|
|
double speed = 0., current = 0.;
|
|
for(int i = 0; i < MOTORS_AMOUNT; ++i){
|
|
int s = MotorState[i].status;
|
|
if(s == MOT_OFF) continue;
|
|
if(status < s) status = s;
|
|
speed += MotorState[i].speed;
|
|
current += MotorState[i].current;
|
|
++N;
|
|
}
|
|
if(N){
|
|
speed /= (double)N;
|
|
current /= (double)N;
|
|
}
|
|
if(status == MOT_OFF && CommonState.status != MOT_OFF){
|
|
*msg = MesgFault;
|
|
sprintf(msg+1, "Dome: All motors are Off!\n");
|
|
SendMessage(msg);
|
|
}else if(status != MOT_OFF && CommonState.status == MOT_OFF){
|
|
*msg = MesgFault;
|
|
sprintf(msg+1, "Dome: Start motors!\n");
|
|
SendMessage(msg);
|
|
}
|
|
CommonState.status = status;
|
|
CommonState.speed = speed;
|
|
CommonState.current = current;
|
|
return TRUE;
|
|
}
|
|
|
|
// check for speed change and send given command to server
|
|
static void chk_dome_speed(SSL *ssl){
|
|
static int old_state = D_Off;
|
|
int new_state = Dome_Speed;
|
|
double tlast = 0.;
|
|
if(new_state == old_state){
|
|
if(sl_dtime() - tlast < G.speedchk_interval) return;
|
|
if(CommonState.speed == set_speed){
|
|
tlast = sl_dtime();
|
|
return;
|
|
}
|
|
}
|
|
double new_speed = 0.;
|
|
switch(new_state){
|
|
case D_Lplus:
|
|
new_speed = LSpeed;
|
|
break;
|
|
case D_Lminus:
|
|
new_speed = -LSpeed;
|
|
break;
|
|
case D_Mplus:
|
|
new_speed = MSpeed;
|
|
break;
|
|
case D_Mminus:
|
|
new_speed = -MSpeed;
|
|
break;
|
|
case D_Hplus:
|
|
new_speed = HSpeed;
|
|
break;
|
|
case D_Hminus:
|
|
new_speed = -HSpeed;
|
|
break;
|
|
default: // stop
|
|
break;
|
|
}
|
|
value_t Dval = {.type = ARG_TYPE_DOUBLE, .d = new_speed};
|
|
if(RESULT_OK == send_motor_command(ssl, cmd_speed, &Dval, NULL)){
|
|
if(RESULT_OK == send_motor_command(ssl, cmd_speed, NULL, &Dval) && fabs(Dval.d - new_speed) <= FLT_EPSILON){
|
|
old_state = new_state; // all OK, command in work
|
|
set_speed = new_speed;
|
|
tlast = sl_dtime();
|
|
}
|
|
}
|
|
}
|
|
|
|
// SHM parser; return FALSE if SHM is in erroreous state
|
|
static int process_system(SSL *ssl){
|
|
static double t0 = 0., last_mtime = 0.;
|
|
static int curMotNo = 0; // current motor number (we'll scan all 10 motors by one)
|
|
double curtime = sl_dtime();
|
|
if(t0 < 1.){ // first run
|
|
t0 = curtime;
|
|
last_mtime = M_time;
|
|
return TRUE;
|
|
}
|
|
// check time sync
|
|
static int brokenshmctr = 0;
|
|
if(M_time - last_mtime > curtime - t0 + G.T_sync_lost || !check_shm_block(&sdat)){ // broken SHM
|
|
if(brokenshmctr == 0){
|
|
LOGERR("Stalled or broken SHM block");
|
|
}
|
|
WARNX("Stalled or broken SHM block; %g -- %g", M_time - last_mtime + G.T_sync_lost, curtime - t0);
|
|
if(++brokenshmctr < 10) return TRUE;
|
|
return FALSE;
|
|
}else brokenshmctr = 0;
|
|
static int modelused = FALSE;
|
|
if(!G.emulmode){
|
|
if(UseModel == FullModel){ // model
|
|
if(!modelused){
|
|
LOGWARN("Server is in model mode");
|
|
modelused = TRUE;
|
|
stop_all(ssl);
|
|
}
|
|
return TRUE;
|
|
}else modelused = FALSE;
|
|
static int serverisdead = FALSE;
|
|
if(ServPID <= 0 || kill(ServPID, 0) < 0){ // dead server
|
|
WARNX("Main server is dead");
|
|
if(!serverisdead){
|
|
LOGERR("Main server is dead");
|
|
serverisdead = TRUE;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}else serverisdead = FALSE;
|
|
}
|
|
if(check_motor(ssl, curMotNo)){
|
|
// TODO: check state for errors
|
|
if(++curMotNo >= MOTORS_AMOUNT) curMotNo = 0;
|
|
}
|
|
if(!D_Locked && Dome_State != D_Off) chk_dome_speed(ssl);
|
|
;
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
static char *time_asc(double t){
|
|
static char buf[128];
|
|
int h, m;
|
|
double s;
|
|
h = (int)(t/3600.);
|
|
m = (int)((t - (double)h*3600.)/60.);
|
|
s = t - (double)h*3600. - (double)m*60.;
|
|
h %= 24;
|
|
if(s>59) s=59;
|
|
snprintf(buf, 127, "%d:%02d:%04.1f", h,m,s);
|
|
return buf;
|
|
}
|
|
*/
|
|
|
|
// open SSL connection for client
|
|
static SSL *openConn(SSL_CTX *ctx, int fd){
|
|
SSL *ssl = SSL_new(ctx);
|
|
SSL_set_fd(ssl, fd);
|
|
int c = SSL_connect(ssl);
|
|
if(c < 0){
|
|
LOGERR("SSL_connect()");
|
|
ERRX("SSL_connect() error: %d", SSL_get_error(ssl, c));
|
|
}
|
|
int enable = 1;
|
|
if(ioctl(fd, FIONBIO, (void *)&enable) < 0){
|
|
LOGERR("Can't make socket nonblocking");
|
|
ERRX("ioctl()");
|
|
}
|
|
return ssl;
|
|
}
|
|
|
|
// run main client process
|
|
void clientproc(SSL_CTX *ctx, int fd){
|
|
FNAME();
|
|
char buf[1024];
|
|
SSL *ssl = openConn(ctx, fd);
|
|
if(!ssl) return;
|
|
sdat.mode |= 0200; // allow W
|
|
sdat.atflag = 0; // clear SHM_RDONLY
|
|
if(!get_shm_block(&sdat, ClientSide)){
|
|
LOGERR("Can't get SHM block");
|
|
ERRX("Can't get SHM block");
|
|
}
|
|
while(isrunning){
|
|
if(!process_system(ssl)){
|
|
LOGERR("Motors error");
|
|
WARNX("ERROR!");
|
|
stop_all(ssl);
|
|
sleep(5);
|
|
break;
|
|
}
|
|
// clear receiving buffer (TODO: parse it?)
|
|
int bytes = SSL_nbread(ssl, buf, sizeof(buf)-1);
|
|
if(bytes > 0){
|
|
buf[bytes] = 0;
|
|
fprintf(stderr, "Received: \"%s\"\n", buf);
|
|
}else if(bytes < 0){
|
|
LOGWARN("Server disconnected or other error");
|
|
ERRX("Disconnected");
|
|
}
|
|
usleep(100000);
|
|
}
|
|
DBG("Exit; isrunning=%d", isrunning);
|
|
SSL_free(ssl);
|
|
}
|
|
|
|
// run in terminal mode
|
|
void terminal(SSL_CTX *ctx, int fd){
|
|
FNAME();
|
|
char buf[1024], *lptr = NULL;
|
|
size_t N = 0;
|
|
SSL *ssl = openConn(ctx, fd);
|
|
if(!ssl) return;
|
|
int printed = FALSE;
|
|
while(isrunning){
|
|
if(!printed){
|
|
printf("> ");
|
|
fflush(stdout);
|
|
printed = TRUE;
|
|
}
|
|
if(sl_canread(0)){
|
|
ssize_t L = getline(&lptr, &N, stdin);
|
|
if(L > (ssize_t)(sizeof(buf)-1)) WARNX("String too long!");
|
|
else if(L > 0){
|
|
SSL_write(ssl, lptr, L);
|
|
}
|
|
}
|
|
int bytes = 0;
|
|
while((bytes = SSL_nbread(ssl, buf, sizeof(buf)-1))){
|
|
if(bytes > 0){
|
|
buf[bytes] = 0;
|
|
printf("< %s\n", buf);
|
|
}else{
|
|
LOGWARN("Server disconnected or other error");
|
|
ERRX("Disconnected");
|
|
}
|
|
usleep(10000);
|
|
printed = FALSE;
|
|
}
|
|
}
|
|
SSL_free(ssl);
|
|
}
|