mirror of
https://github.com/eddyem/small_tel.git
synced 2025-12-06 10:45:16 +03:00
274 lines
9.1 KiB
C
274 lines
9.1 KiB
C
/*
|
|
* This file is part of the domedaemon-astrosib 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 <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "astrosib_proto.h"
|
|
#include "dome.h"
|
|
|
|
// number of relay turning on/off motors power
|
|
#define MOTRELAY_NO 1
|
|
// state of relay to turn power on/off
|
|
#define MOTRELAY_ON 1
|
|
#define MOTRELAY_OFF 0
|
|
|
|
// time interval for requiring current dome status in idle state (e.g. trigger watchdog)
|
|
#define STATUSREQ_IDLE 10.
|
|
// update interval in moving state
|
|
#define STATUSREQ_MOVE 0.5
|
|
|
|
// dome status by polling (each STATUSREQ_IDLE seconds)
|
|
static dome_status_t dome_status = {0};
|
|
// finite state machine state
|
|
static dome_state_t state = DOME_S_IDLE;
|
|
// last time dome_status was updated
|
|
static double last_status_time = 0.;
|
|
// current update interval
|
|
static double status_req_interval = STATUSREQ_MOVE;
|
|
// serial device
|
|
static sl_tty_t *serialdev = NULL;
|
|
static pthread_mutex_t serialmutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
void dome_serialdev(sl_tty_t *serial){
|
|
serialdev = serial;
|
|
}
|
|
/**
|
|
* @brief serial_write - write buffer without "\r" on end to port
|
|
* @param cmd - data to send
|
|
* @param answer - buffer for answer (or NULL if not need)
|
|
* @param anslen - length of `answer`
|
|
* @return error code
|
|
*/
|
|
static int serial_write(const char *cmd, char *answer, int anslen){
|
|
if(!serialdev || !cmd || !answer || anslen < 2) return FALSE;
|
|
static char *buf = NULL;
|
|
static size_t buflen = 0;
|
|
DBG("Write %s", cmd);
|
|
size_t cmdlen = strlen(cmd);
|
|
if(buflen < cmdlen + 2){
|
|
buflen = (cmdlen + 4096) / 4096;
|
|
buflen *= 4096;
|
|
if(!(buf = realloc(buf, buflen))){
|
|
LOGERR("serial_write(): realloc() failed!");
|
|
ERRX("serial_write(): realloc() failed!");
|
|
}
|
|
}
|
|
size_t _2write = snprintf(buf, buflen-1, "%s\r", cmd);
|
|
DBG("try to send %zd bytes", _2write);
|
|
if(sl_tty_write(serialdev->comfd, buf, _2write)) return FALSE;
|
|
int got = 0, totlen = 0;
|
|
--anslen; // for /0
|
|
do{
|
|
got = sl_tty_read(serialdev);
|
|
if(got > 0){
|
|
if(got > anslen) return FALSE; // buffer overflow
|
|
memcpy(answer, serialdev->buf, got);
|
|
totlen += got;
|
|
answer += got;
|
|
anslen -= got;
|
|
answer[0] = 0;
|
|
}
|
|
DBG("got = %d", got);
|
|
}while(got > 0 && anslen);
|
|
if(got < 0){
|
|
LOGERR("serial_write(): serial device disconnected!");
|
|
ERRX("serial_write(): serial device disconnected!");
|
|
}
|
|
if(totlen < 1) return FALSE; // no answer received
|
|
if(answer[-1] == '\r') answer[-1] = 0; // remove trailing trash
|
|
return TRUE;
|
|
}
|
|
|
|
// return TRUE if can parse status
|
|
static int parsestatus(const char *buf){
|
|
if(!buf) return FALSE;
|
|
DBG("buf=%s", buf);
|
|
int n = sscanf(buf, ASIB_CMD_STATUS "%d,%d,%d,%d,%f,%f,%f,%f,%f,%f,%d,%d,%d,%d,%d",
|
|
&dome_status.coverstate[0], &dome_status.coverstate[1],
|
|
&dome_status.encoder[0], &dome_status.encoder[1],
|
|
&dome_status.Tin, &dome_status.Tout,
|
|
&dome_status.Imot[0], &dome_status.Imot[1], &dome_status.Imot[2], &dome_status.Imot[3],
|
|
&dome_status.relay[0], &dome_status.relay[1], &dome_status.relay[2],
|
|
&dome_status.rainArmed, &dome_status.israin);
|
|
DBG("n=%d", n);
|
|
if(n != 15){
|
|
WARNX("Something wrong with STATUS answer");
|
|
LOGWARN("Something wrong with STATUS answer");
|
|
LOGWARNADD("%s", buf);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// check status; return FALSE if failed
|
|
static int check_status(){
|
|
char buf[BUFSIZ];
|
|
// clear input buffers
|
|
int got = sl_tty_read(serialdev);
|
|
if(got > 0) printf("Got from serial %zd bytes of trash: `%s`\n", serialdev->buflen, serialdev->buf);
|
|
else if(got < 0){
|
|
LOGERR("Serial device disconnected?");
|
|
ERRX("Serial device disconnected?");
|
|
}
|
|
DBG("Require status");
|
|
if(!serial_write(ASIB_CMD_STATUS, buf, BUFSIZ)) return FALSE;
|
|
int ret = FALSE;
|
|
if(parsestatus(buf)){
|
|
last_status_time = sl_dtime();
|
|
ret = TRUE;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// run naked command or command with parameters
|
|
static int runcmd(const char *cmd, const char *par){
|
|
char buf[256];
|
|
if(!cmd) return FALSE;
|
|
DBG("Send command %s with par %s", cmd, par);
|
|
if(!par) snprintf(buf, 255, "%s", cmd);
|
|
else snprintf(buf, 255, "%s%s", cmd, par);
|
|
if(!serial_write(buf, buf, 255)) return FALSE;
|
|
if(strncmp(buf, "OK", 2)) return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
// check current state and turn on/off relay if need
|
|
static void chkrelay(){
|
|
static dome_state_t oldstate = DOME_S_MOVING;
|
|
static double t0 = 0.;
|
|
if(state != DOME_S_MOVING) return;
|
|
if(dome_status.coverstate[0] == COVER_INTERMEDIATE ||
|
|
dome_status.coverstate[1] == COVER_INTERMEDIATE){ // still moving
|
|
oldstate = DOME_S_MOVING;
|
|
return;
|
|
}
|
|
DBG("state=%d, oldstate=%d", state, oldstate);
|
|
// OK, we are on place - turn off motors' power after 5 seconds
|
|
if(oldstate == DOME_S_MOVING){ // just stopped - fire pause
|
|
t0 = sl_dtime();
|
|
oldstate = DOME_S_IDLE;
|
|
DBG("START 5s pause");
|
|
return;
|
|
}else{ // check pause
|
|
if(sl_dtime() - t0 < POWER_STOP_TIMEOUT) return;
|
|
}
|
|
DBG("5s out -> turn off power");
|
|
char buf[128];
|
|
snprintf(buf, 127, "%s%d,%d", ASIB_CMD_RELAY, MOTRELAY_NO, MOTRELAY_OFF);
|
|
if(serial_write(buf, buf, 128) && check_status()){
|
|
DBG("Check are motors really off");
|
|
if(dome_status.relay[MOTRELAY_NO-1] == MOTRELAY_OFF){
|
|
DBG("OK state->IDLE");
|
|
state = DOME_S_IDLE;
|
|
oldstate = DOME_S_MOVING;
|
|
}
|
|
}
|
|
}
|
|
|
|
// turn ON motors' relay
|
|
static int motors_on(){
|
|
char buf[128];
|
|
snprintf(buf, 127, "%s%d,%d", ASIB_CMD_RELAY, MOTRELAY_NO, MOTRELAY_ON);
|
|
if(serial_write(buf, buf, 128) && check_status()){
|
|
DBG("Check are motors really on");
|
|
if(dome_status.relay[MOTRELAY_NO-1] == MOTRELAY_ON){
|
|
DBG("OK state->MOVING");
|
|
state = DOME_S_MOVING;
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
// just get current status
|
|
double get_dome_status(dome_status_t *s){
|
|
if(s){
|
|
pthread_mutex_lock(&serialmutex);
|
|
*s = dome_status;
|
|
pthread_mutex_unlock(&serialmutex);
|
|
}
|
|
return last_status_time;
|
|
}
|
|
|
|
dome_state_t get_dome_state(){return state;}
|
|
|
|
dome_state_t dome_poll(dome_cmd_t cmd, int par){
|
|
char buf[128];
|
|
int st = DOME_S_ERROR;
|
|
double curtime = sl_dtime();
|
|
//DBG("curtime-lasttime=%g", curtime - last_status_time);
|
|
// simple polling and there's a lot of time until next serial device poll
|
|
if(cmd == DOME_POLL && curtime - last_status_time < status_req_interval)
|
|
return state;
|
|
pthread_mutex_lock(&serialmutex);
|
|
// check if we need to turn ON motors' relay
|
|
switch(cmd){
|
|
case DOME_OPEN:
|
|
case DOME_CLOSE:
|
|
case DOME_OPEN_ONE:
|
|
case DOME_CLOSE_ONE:
|
|
if(!motors_on()) goto ret;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
switch(cmd){
|
|
case DOME_STOP:
|
|
if(!runcmd(ASIB_CMD_STOP, NULL)) goto ret;
|
|
break;
|
|
case DOME_OPEN:
|
|
if(!runcmd(ASIB_CMD_OPEN, NULL)) goto ret;
|
|
break;
|
|
case DOME_CLOSE:
|
|
if(!runcmd(ASIB_CMD_CLOSE, NULL)) goto ret;
|
|
break;
|
|
case DOME_OPEN_ONE: // due to bug in documentation, 0 - OPEN
|
|
if(par < 1 || par > 2) goto ret;
|
|
snprintf(buf, 127, "%d,0", par-1);
|
|
if(!runcmd(ASIB_CMD_MOVEONE, buf)) goto ret;
|
|
break;
|
|
case DOME_CLOSE_ONE: // due to bug in documentation, 90 - CLOSE
|
|
if(par < 1 || par > 2) goto ret;
|
|
snprintf(buf, 127, "%d,90", par-1);
|
|
if(!runcmd(ASIB_CMD_MOVEONE, buf)) goto ret;
|
|
break;
|
|
case DOME_RELAY_ON:
|
|
if(par < NRELAY_MIN || par > NRELAY_MAX) goto ret;
|
|
snprintf(buf, 127, "%d,1", par);
|
|
if(!runcmd(ASIB_CMD_RELAY, buf)) goto ret;
|
|
break;
|
|
case DOME_RELAY_OFF:
|
|
if(par < NRELAY_MIN || par > NRELAY_MAX) goto ret;
|
|
snprintf(buf, 127, "%d,0", par);
|
|
if(!runcmd(ASIB_CMD_RELAY, buf)) goto ret;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
ret:
|
|
if(check_status()){
|
|
chkrelay();
|
|
st = state;
|
|
}
|
|
if(state == DOME_S_IDLE) status_req_interval = STATUSREQ_IDLE;
|
|
else status_req_interval = STATUSREQ_MOVE;
|
|
pthread_mutex_unlock(&serialmutex);
|
|
return st;
|
|
}
|