From 1db54ed07c559952f07b890dbdd982806f0f2db2 Mon Sep 17 00:00:00 2001 From: Edward Emelianov Date: Tue, 29 Sep 2026 17:06:31 +0300 Subject: [PATCH] add working with pseudo-FX3U with my firmware --- BTA_dome_modbus/Makefile | 2 +- BTA_dome_modbus/cmdlnopts.c | 2 + BTA_dome_modbus/cmdlnopts.h | 1 + BTA_dome_modbus/daemon.c | 3 +- BTA_dome_modbus/handlers_list.h | 5 + BTA_dome_modbus/io.c | 162 ++++++++++++++++++++++++++++++++ BTA_dome_modbus/io.h | 38 ++++++++ BTA_dome_modbus/motors.c | 3 + BTA_dome_modbus/run_client | 2 +- BTA_dome_modbus/run_server | 2 +- BTA_dome_modbus/server.c | 92 +++++++++++++++--- 11 files changed, 297 insertions(+), 15 deletions(-) create mode 100644 BTA_dome_modbus/io.c create mode 100644 BTA_dome_modbus/io.h diff --git a/BTA_dome_modbus/Makefile b/BTA_dome_modbus/Makefile index b2f3984..72ab840 100644 --- a/BTA_dome_modbus/Makefile +++ b/BTA_dome_modbus/Makefile @@ -8,7 +8,7 @@ SOBJDIR := mkserver COBJDIR := mkclient CFLAGS += -O2 -Wall -Wextra -Wno-trampolines -pthread COMMSRCS := sslsock.c daemon.c cmdlnopts.c main.c -SSRC := server.c motors.c $(COMMSRCS) +SSRC := server.c motors.c io.c $(COMMSRCS) CSRC := client.c bta_shdata.c $(COMMSRCS) SOBJS := $(addprefix $(SOBJDIR)/, $(SSRC:%.c=%.o)) COBJS := $(addprefix $(COBJDIR)/, $(CSRC:%.c=%.o)) diff --git a/BTA_dome_modbus/cmdlnopts.c b/BTA_dome_modbus/cmdlnopts.c index 691e52a..a6f62aa 100644 --- a/BTA_dome_modbus/cmdlnopts.c +++ b/BTA_dome_modbus/cmdlnopts.c @@ -51,6 +51,7 @@ glob_pars G = { .speedchk_interval = 10., #endif #ifdef SERVER + .io_ID = -1, // no IO board .motlogdir = "/tmp", #endif }; @@ -75,6 +76,7 @@ static sl_option_t cmdlnopts[] = { {"serialdev",NEED_ARG, NULL, 'd', arg_string, APTR(&G.serialpath),_("path to RS-485 device")}, {"serialspeed",NEED_ARG,NULL, 's', arg_int, APTR(&G.serialspeed),_("speed of serial device")}, {"mldir", NEED_ARG, NULL, 'D', arg_string, APTR(&G.motlogdir), _("directory to store motor logs (default: /tmp)")}, + {"ioid", NEED_ARG, NULL, 'i', arg_int, APTR(&G.io_ID), _("I/O board modbus ID")}, #endif #ifdef CLIENT {"mottmout",NEED_ARG, NULL, 'M', arg_double, APTR(&G.speedchk_interval), _("interval of motor's speed checking, s (default: 10)")}, diff --git a/BTA_dome_modbus/cmdlnopts.h b/BTA_dome_modbus/cmdlnopts.h index 043f0c9..f5a086d 100644 --- a/BTA_dome_modbus/cmdlnopts.h +++ b/BTA_dome_modbus/cmdlnopts.h @@ -47,6 +47,7 @@ typedef struct{ int serialspeed; // speed of serial device char *serialpath; // path to RS-485 device char *motlogdir; // directory to store motor logs + int io_ID; // ID of IO board (relays/inputs) #endif #ifdef CLIENT int terminal; // run client in terminal mode diff --git a/BTA_dome_modbus/daemon.c b/BTA_dome_modbus/daemon.c index 082e97c..8d8ad7a 100644 --- a/BTA_dome_modbus/daemon.c +++ b/BTA_dome_modbus/daemon.c @@ -84,13 +84,14 @@ int start_daemon(){ OPENLOG(G.logfile, lvl, 1); } signal(SIGTERM, signals); // kill (-15) - quit - signal(SIGHUP, SIG_IGN); // hup - ignore + //signal(SIGHUP, SIG_IGN); // hup - ignore (used in sl_daemonize()) signal(SIGINT, signals); // ctrl+C - quit signal(SIGQUIT, signals); // ctrl+\ - quit signal(SIGTSTP, SIG_IGN); // ignore ctrl+Z LOGMSG("Started"); #ifndef EBUG sl_check4running(NULL, G.pidfile); + sl_daemonize(); int savelogs = TRUE; tstart = sl_dtime(); while(1){ diff --git a/BTA_dome_modbus/handlers_list.h b/BTA_dome_modbus/handlers_list.h index b5700bc..f3ce6d8 100644 --- a/BTA_dome_modbus/handlers_list.h +++ b/BTA_dome_modbus/handlers_list.h @@ -25,13 +25,18 @@ // Handlers in this list MUST be in sortered order (by name)!!! #define HANDLERS_LIST() \ +NEW_HANDLER(adc, "read nth ADC channel") \ NEW_HANDLER(forbidden, "forbid (1) or permit (0) dome operations") \ +NEW_HANDLER(inputs, "read PLC discrete inputs") \ NEW_HANDLER(logprefix, "prefix of log file for nth motor (prefix_%d.log), max 31 chars") \ NEW_HANDLER(motcurrent, "maximal motor current") \ NEW_HANDLER(motnum, "active motor number for status requests") \ NEW_HANDLER(motspeed, "motor speed") \ NEW_HANDLER(motstatus, "motor status") \ NEW_HANDLER(nmotors, "amount of working motors") \ +NEW_HANDLER(relayoff, "reset relay with given number") \ +NEW_HANDLER(relayon, "set relay with given number") \ +NEW_HANDLER(relayset, "set/reset relays by given mask") \ NEW_HANDLER(speed, "speed setter") \ NEW_HANDLER(startlog, "start logging") \ NEW_HANDLER(stop, "stop motors") \ diff --git a/BTA_dome_modbus/io.c b/BTA_dome_modbus/io.c new file mode 100644 index 0000000..84fe300 --- /dev/null +++ b/BTA_dome_modbus/io.c @@ -0,0 +1,162 @@ +/* + * This file is part of the sslsosk project. + * Copyright 2026 Edward V. Emelianov . + * + * 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 . + */ + +#include +#include +#include +#include +#include + +#include "cmdlnopts.h" +#include "io.h" + + +static modbus_t *modbus_ctx = NULL; + +static uint8_t RelayBuf[NRELAYS]; +static uint8_t InputsBuf[NINPUTS]; +static uint16_t ADCBuf[NADCCHNLS]; +static pthread_mutex_t sync_mutex = PTHREAD_MUTEX_INITIALIZER; + +// == TRUE if something changed in buffers (reset in `io_read_inputs`) +static uint8_t GotNew = FALSE;//, GotNewADC = FALSE; + +// share modbus_t opened in motors.c +void io_set_ctx(modbus_t *ctx){ + DBG("Get context"); + pthread_mutex_lock(&sync_mutex); + modbus_ctx = ctx; + pthread_mutex_unlock(&sync_mutex); +} + +// process reading relays & inputs state +void io_process(){ + uint8_t buf[NRELAYS + NINPUTS]; + uint16_t adcbuf[NADCCHNLS]; + if(!modbus_ctx || G.io_ID < 1) return; + if(-1 == modbus_set_slave(modbus_ctx, G.io_ID)){ + WARN("Can't set IO slave ID to %d", G.io_ID); + return; + } + pthread_mutex_lock(&sync_mutex); + if(-1 == modbus_read_bits(modbus_ctx, 0, NRELAYS, buf)){ + WARN("Can't read relay state"); + goto ret; + } + for(int i = 0; i < NRELAYS; ++i){ + if(buf[i] == RelayBuf[i]) continue; +#ifdef EBUG + if(buf[i]) printf("Relay %d is ON\n", i); + else printf("Relay %d is OFF\n", i); + fflush(stdout); +#endif + RelayBuf[i] = buf[i]; + GotNew = TRUE; + } + if(-1 == modbus_read_input_bits(modbus_ctx, 0, NINPUTS, buf)){ + WARN("Can't read inputs"); + goto ret; + } + for(int i = 0; i < NINPUTS; ++i){ + if(buf[i] == InputsBuf[i]) continue; +#ifdef EBUG + if(buf[i]) printf("Input %d is Active\n", i); + else printf("Input %d is Inactive\n", i); + fflush(stdout); +#endif + InputsBuf[i] = buf[i]; + GotNew = TRUE; + } + if(-1 == modbus_read_input_registers(modbus_ctx, 0, NADCCHNLS, adcbuf)){ + WARN("Can't read ADC inputs"); + goto ret; + } + for(int i = 0; i < NADCCHNLS; ++i){ + if(abs(adcbuf[i] - ADCBuf[i]) < ADCTHRESH) continue; + // GotNewADC = TRUE; break; +#ifdef EBUG + printf("ADC input %d changed to %d\n", i, adcbuf[i]); + fflush(stdout); +#endif + } + memcpy(ADCBuf, adcbuf, sizeof(uint16_t) * NADCCHNLS); +ret: + pthread_mutex_unlock(&sync_mutex); +} + +// get inputs state +int io_read_inputs(char inputs[NINPUTS]){ + pthread_mutex_lock(&sync_mutex); + int got = GotNew; + GotNew = FALSE; + memcpy(inputs, InputsBuf, NINPUTS); + pthread_mutex_unlock(&sync_mutex); + return got; +} + +int io_read_adc(int nch, uint16_t *val){ + if(!val || nch < 0 || nch >= NADCCHNLS) return FALSE; + pthread_mutex_lock(&sync_mutex); + /*int got = GotNewADC; + GotNewADC = FALSE;*/ + *val = ADCBuf[nch]; + pthread_mutex_unlock(&sync_mutex); + return TRUE; +} + +// get relays state +void io_read_relays(char relays[NRELAYS]){ + pthread_mutex_lock(&sync_mutex); + memcpy(relays, RelayBuf, NRELAYS); + pthread_mutex_unlock(&sync_mutex); +} + +static int change_relay(int N, int state){ + if(!modbus_ctx || N < 0 || N >= NRELAYS) return FALSE; + int ret = FALSE; + pthread_mutex_lock(&sync_mutex); + if(1 == modbus_write_bit(modbus_ctx, N, state)) ret = TRUE; + else WARN("Can't set bit %d to %d", N, state); + pthread_mutex_unlock(&sync_mutex); + return ret; +} + +// set relay with given number +int io_relay_on(int N){ + return change_relay(N, 1); +} + +// reset relay with given number +int io_relay_off(int N){ + return change_relay(N, 0); +} + +int io_set_relays(int N){ + if(!modbus_ctx || N < 0) return FALSE; + int ret = FALSE; + uint8_t bits[NRELAYS]; + for(int i = 0; i < NRELAYS; ++i){ + if(N & (1 << i)) bits[i] = 1; + else bits[i] = 0; + } + pthread_mutex_lock(&sync_mutex); + if(1 == modbus_write_bits(modbus_ctx, 0, NRELAYS, bits)) ret = TRUE; + else WARN("Can't write bits %d", N); + pthread_mutex_unlock(&sync_mutex); + return ret; +} diff --git a/BTA_dome_modbus/io.h b/BTA_dome_modbus/io.h new file mode 100644 index 0000000..8930517 --- /dev/null +++ b/BTA_dome_modbus/io.h @@ -0,0 +1,38 @@ +/* + * This file is part of the sslsosk project. + * Copyright 2026 Edward V. Emelianov . + * + * 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 . + */ + +#pragma once + +#include + +#define NRELAYS 16 +#define NINPUTS 16 +#define NADCCHNLS 8 +// ADC level threshold: 50 units +#define ADCTHRESH 50 + +void io_set_ctx(modbus_t *ctx); +void io_process(); + +int io_read_inputs(char inputs[NINPUTS]); +int io_read_adc(int nch, uint16_t *val); +void io_read_relays(char relays[NRELAYS]); + +int io_relay_on(int N); +int io_relay_off(int N); +int io_set_relays(int N); diff --git a/BTA_dome_modbus/motors.c b/BTA_dome_modbus/motors.c index 4b5b954..fa0c5ef 100644 --- a/BTA_dome_modbus/motors.c +++ b/BTA_dome_modbus/motors.c @@ -24,6 +24,7 @@ #include "cmdlnopts.h" #include "motors.h" #include "esq770.h" +#include "io.h" #if 0 Протокол (s - сеттер, g - getteer): @@ -73,6 +74,7 @@ static int motindex = 0; // close modbus connection static void motors_close_m(){ + io_set_ctx(NULL); // do it before removing context if(modbus_ctx){ modbus_close(modbus_ctx); modbus_free(modbus_ctx); @@ -104,6 +106,7 @@ static int motors_open_m(const char *path, int speed){ motors_close_m(); return FALSE; } + io_set_ctx(modbus_ctx); return TRUE; } static int motors_open_e(const char _U_ *path, int _U_ speed){ // stub for emulation mode diff --git a/BTA_dome_modbus/run_client b/BTA_dome_modbus/run_client index 66ae3e5..8c3e8aa 100755 --- a/BTA_dome_modbus/run_client +++ b/BTA_dome_modbus/run_client @@ -1,3 +1,3 @@ #!/bin/bash rm -f client.log -./dome_client -a ca/ca/ca_cert.pem -c ca/client/client_cert.pem -k ca/client/private/client_key.pem -vvv -s localhost -l client.log $@ +./dome_client -a $PWD/ca/ca/ca_cert.pem -c $PWD/ca/client/client_cert.pem -k $PWD/ca/client/private/client_key.pem -vvv -s localhost -l $PWD/client.log $@ diff --git a/BTA_dome_modbus/run_server b/BTA_dome_modbus/run_server index 63a461a..aca64a3 100755 --- a/BTA_dome_modbus/run_server +++ b/BTA_dome_modbus/run_server @@ -1,3 +1,3 @@ #!/bin/bash rm -f server.log -./dome_server -a ca/ca/ca_cert.pem -c ca/server/server_cert.pem -k ca/server/private/server_key.pem -vvvl server.log $@ +./dome_server -a $PWD/ca/ca/ca_cert.pem -c $PWD/ca/server/server_cert.pem -k $PWD/ca/server/private/server_key.pem -vvvl $PWD/server.log -i 20 $@ diff --git a/BTA_dome_modbus/server.c b/BTA_dome_modbus/server.c index 2ae12a6..c6cf4d9 100644 --- a/BTA_dome_modbus/server.c +++ b/BTA_dome_modbus/server.c @@ -21,6 +21,7 @@ #include "cmdlnopts.h" #include "daemon.h" // isrunning #include "handlers_list.h" +#include "io.h" #include "motors.h" #include "server.h" @@ -146,6 +147,7 @@ void serverproc(SSL_CTX *ctx, int fd){ //int P = 0; while(isrunning){ motors_process(); + io_process(); /*double tnow = sl_dtime(); if(tnow - t0 > 5. && nfd > 1){ // broadcasting message //DBG("send ping"); @@ -217,7 +219,15 @@ void serverproc(SSL_CTX *ctx, int fd){ } /****************** Protocol handlers (return 0 in case of success or error code >0 if failed) ******************/ -sl_sock_hresult_e forbidden_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ +static sl_sock_hresult_e adc_handler(int _U_ index, char value[SL_VAL_LEN]){ + int I; uint16_t val; + if(!ISSETTER(value) || !sl_str2i(&I, value)) return RESULT_BADVAL; + if(!io_read_adc(I, &val)) return RESULT_BADVAL; + snprintf(value, SL_VAL_LEN-1, "%d", val); + return RESULT_SILENCE; +} + +static sl_sock_hresult_e forbidden_handler(int _U_ index, char value[SL_VAL_LEN]){ int I; if(ISSETTER(value)){ if(!sl_str2i(&I, value)) return RESULT_BADVAL; @@ -227,6 +237,66 @@ sl_sock_hresult_e forbidden_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ snprintf(value, SL_VAL_LEN-1, "%d", forbidden); return RESULT_SILENCE; } + +static sl_sock_hresult_e inputs_handler(int _U_ index, char value[SL_VAL_LEN]){ + if(ISSETTER(value)) return RESULT_BADVAL; + char inputs[NINPUTS], *ptr = value; + size_t restofline = SL_VAL_LEN - 1; + int triggered = io_read_inputs(inputs); + for(int i = 0; i < NINPUTS && restofline; ++i, ++ptr, --restofline){ + if(inputs[i]) *ptr = '1'; + else *ptr = '0'; + } + if(restofline && triggered) *ptr++ = 'T'; + *ptr = 0; + return RESULT_SILENCE; +} + +// common getter for all relay commands +static void read_relays(char value[SL_VAL_LEN]){ + char relays[NRELAYS], *ptr = value; + size_t restofline = SL_VAL_LEN - 1; + io_read_relays(relays); + for(int i = 0; i < NRELAYS && restofline; ++i, ++ptr, --restofline){ + if(relays[i]) *ptr = '1'; + else *ptr = '0'; + } + *ptr = 0; +} + +static sl_sock_hresult_e relayon_handler(int _U_ index, char value[SL_VAL_LEN]){ + if(!ISSETTER(value)){ + read_relays(value); + return RESULT_SILENCE; + } + int I; + if(!sl_str2i(&I, value)) return RESULT_BADVAL; + if(!io_relay_on(I)) return RESULT_FAIL; + return RESULT_OK; +} + +static sl_sock_hresult_e relayoff_handler(int _U_ index, char value[SL_VAL_LEN]){ + if(!ISSETTER(value)){ + read_relays(value); + return RESULT_SILENCE; + } + int I; + if(!sl_str2i(&I, value)) return RESULT_BADVAL; + if(!io_relay_off(I)) return RESULT_FAIL; + return RESULT_OK; +} + +static sl_sock_hresult_e relayset_handler(int _U_ index, char value[SL_VAL_LEN]){ + if(!ISSETTER(value)){ + read_relays(value); + return RESULT_SILENCE; + } + int I; + if(!sl_str2i(&I, value)) return RESULT_BADVAL; + if(!io_set_relays(I)) return RESULT_FAIL; + return RESULT_OK; +} + // key - keyword (command name), value - i/o buffer (value[0]==0 for getters) /* sl_sock_hresult_e current_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ @@ -239,7 +309,7 @@ sl_sock_hresult_e current_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ return RESULT_SILENCE; }*/ -sl_sock_hresult_e motcurrent_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ +static sl_sock_hresult_e motcurrent_handler(int _U_ index, char value[SL_VAL_LEN]){ if(ISSETTER(value)) return RESULT_BADVAL; // only getter double D; if(!motors_get_actcurrent(&D)) return RESULT_FAIL; @@ -247,7 +317,7 @@ sl_sock_hresult_e motcurrent_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ return RESULT_SILENCE; } -sl_sock_hresult_e motnum_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ +static sl_sock_hresult_e motnum_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ int I; if(ISSETTER(value)){ if(!sl_str2i(&I, value) || !motors_set_activenum(I)) return RESULT_BADVAL; @@ -257,7 +327,7 @@ sl_sock_hresult_e motnum_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ return RESULT_SILENCE; } -sl_sock_hresult_e motspeed_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ +static sl_sock_hresult_e motspeed_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ if(ISSETTER(value)) return RESULT_BADVAL; // only getter double D; if(!motors_get_actspeed(&D)) return RESULT_FAIL; @@ -265,7 +335,7 @@ sl_sock_hresult_e motspeed_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ return RESULT_SILENCE; } -sl_sock_hresult_e motstatus_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ +static sl_sock_hresult_e motstatus_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ if(ISSETTER(value)) return RESULT_BADVAL; // only getter int I; if(!motors_get_actstatus(&I)) return RESULT_FAIL; @@ -277,7 +347,7 @@ sl_sock_hresult_e relay_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ return RESULT_SILENCE; }*/ -sl_sock_hresult_e speed_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ +static sl_sock_hresult_e speed_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ double D; if(ISSETTER(value)){ if(forbidden) return RESULT_FAIL; @@ -288,17 +358,17 @@ sl_sock_hresult_e speed_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ return RESULT_SILENCE; } -sl_sock_hresult_e stop_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ +static sl_sock_hresult_e stop_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ motors_stop(); return RESULT_OK; } -sl_sock_hresult_e nmotors_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ +static sl_sock_hresult_e nmotors_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ snprintf(value, SL_VAL_LEN-1, "%d", motors_get_working_amount()); return RESULT_SILENCE; } -sl_sock_hresult_e logprefix_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ +static sl_sock_hresult_e logprefix_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ if(ISSETTER(value)){ // chkeck `value`: if(set_logfile_prefix(value)) return RESULT_OK; @@ -307,12 +377,12 @@ sl_sock_hresult_e logprefix_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ return RESULT_SILENCE; } -sl_sock_hresult_e startlog_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ +static sl_sock_hresult_e startlog_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ if(start_log()) return RESULT_OK; return RESULT_FAIL; } -sl_sock_hresult_e stoplog_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ +static sl_sock_hresult_e stoplog_handler(int _U_ index, char _U_ value[SL_VAL_LEN]){ stop_log(); return RESULT_OK; }