mirror of
https://github.com/OpenSolo/OpenSolo.git
synced 2025-04-29 22:24:32 +02:00
791 lines
28 KiB
C++
791 lines
28 KiB
C++
|
|
#include <fstream>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <vector>
|
|
|
|
#include <arpa/inet.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <poll.h>
|
|
#include <pthread.h>
|
|
#include <sched.h>
|
|
#include <signal.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
#include <termios.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include "util.h"
|
|
#include "mutex.h"
|
|
#include "RC.h"
|
|
#include "Telem.h"
|
|
#include "SysInfo.h"
|
|
#include "PairReq.h"
|
|
#include "PairRes.h"
|
|
#include "ParamStoredVals.h"
|
|
#include "ConfigStickAxes.h"
|
|
#include "SetTelemUnits.h"
|
|
#include "ConfigSweepTime.h"
|
|
#include "ButtonEventHandler.h"
|
|
#include "InputReport.h"
|
|
#include "ButtonFunctionCfg.h"
|
|
#include "SetShotInfo.h"
|
|
#include "Updater.h"
|
|
#include "LockoutState.h"
|
|
#include "AppConnected.h"
|
|
#include "SLIP.h"
|
|
#include "packetTypes.h"
|
|
#include "INIReader.h"
|
|
#include "net_wmm.h"
|
|
#ifdef INCLUDE_SERIAL_LOG
|
|
#include "SerialLog.h"
|
|
#endif // INCLUDE_SERIAL_LOG
|
|
|
|
using namespace std;
|
|
|
|
/***********************************************************************
|
|
Timeouts
|
|
***********************************************************************/
|
|
#define LOG_DT_US 10000000 // 10s
|
|
|
|
/***********************************************************************
|
|
Serial port name
|
|
***********************************************************************/
|
|
string serialPortName;
|
|
|
|
/***********************************************************************
|
|
Serial port receive buffer
|
|
***********************************************************************/
|
|
#define SERIAL_BUFSIZE 4096
|
|
#define MAX_SERIAL_MESSAGE_LEN 1024
|
|
|
|
/***********************************************************************
|
|
Thread IDs
|
|
***********************************************************************/
|
|
pthread_t upstream_ctx;
|
|
pthread_t downstream_ctx;
|
|
|
|
/***********************************************************************
|
|
Thread priorities
|
|
***********************************************************************/
|
|
#define UPSTREAM_PRIORITY 60
|
|
#define DOWNSTREAM_PRIORITY 56
|
|
|
|
/***********************************************************************
|
|
File descriptors
|
|
***********************************************************************/
|
|
int serial_fd;
|
|
|
|
#ifdef INCLUDE_SERIAL_LOG
|
|
/***********************************************************************
|
|
Serial port log
|
|
***********************************************************************/
|
|
SerialLog *serialLog;
|
|
unsigned serialLogSize;
|
|
unsigned serialLogTimeout_us;
|
|
#endif // INCLUDE_SERIAL_LOG
|
|
|
|
/***********************************************************************
|
|
The amount of data sent/received every cycle
|
|
***********************************************************************/
|
|
unsigned upBytes = 0;
|
|
unsigned upMsgs[PKT_ID_MAX + 1];
|
|
pthread_mutex_t mutex_upBytes;
|
|
|
|
/***********************************************************************
|
|
Handler objects
|
|
***********************************************************************/
|
|
RC *rc;
|
|
Telem *telem;
|
|
SysInfo *sysInfo;
|
|
PairReq *pairReq;
|
|
PairRes *pairRes;
|
|
ParamStoredVals *paramStoredVals;
|
|
ConfigStickAxes *configStickAxes;
|
|
SetTelemUnits *setTelemUnits;
|
|
ConfigSweepTime *configSweepTime;
|
|
ButtonEventHandler *btnEvt;
|
|
InputReport *inputReport;
|
|
ButtonFunctionCfg *btnFuncCfg;
|
|
SetShotInfo *setShotInfo;
|
|
Updater *updater;
|
|
LockoutState *lockoutState;
|
|
AppConnected *appConn;
|
|
|
|
/***********************************************************************
|
|
Debug control
|
|
***********************************************************************/
|
|
static uint32_t dbg_downHandler = 0;
|
|
|
|
/***********************************************************************
|
|
Function: void lock_thread_mem(uint32_t memsize)
|
|
|
|
Description: Locks memory in the thread so that stack faults don't cause
|
|
timing jitter.
|
|
***********************************************************************/
|
|
static void lock_thread_mem(uint32_t memsize)
|
|
{
|
|
uint8_t stackmem[memsize];
|
|
|
|
mlockall(MCL_CURRENT | MCL_FUTURE);
|
|
|
|
memset(stackmem, 0, sizeof(stackmem));
|
|
}
|
|
|
|
/***********************************************************************
|
|
Function: int serial_setup(int baudrate)
|
|
|
|
Description: The serial port initialization function. This function
|
|
initializes the serial port over which data is sent and
|
|
received from the STM32. A return of 0 indicates an error.
|
|
***********************************************************************/
|
|
int serial_setup(int baudrate)
|
|
{
|
|
struct termios options;
|
|
|
|
serial_fd = open(serialPortName.c_str(), O_RDWR | O_NOCTTY); // Blocking read
|
|
|
|
if (serial_fd < 0) {
|
|
syslog(LOG_ERR, "unable to open serial port %s", serialPortName.c_str());
|
|
return 0;
|
|
}
|
|
|
|
// Configure port for 8N1 transmission
|
|
tcgetattr(serial_fd, &options); // Gets the current options for the port
|
|
switch (baudrate) {
|
|
case 57600:
|
|
cfsetspeed(&options, B57600);
|
|
break;
|
|
case 115200:
|
|
cfsetspeed(&options, B115200);
|
|
break;
|
|
case 1500000:
|
|
cfsetspeed(&options, B1500000);
|
|
break;
|
|
default:
|
|
syslog(LOG_ERR, "unsupported baudrate %d", baudrate);
|
|
return 0;
|
|
}
|
|
options.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK | INPCK | ISTRIP | IXON);
|
|
options.c_oflag &= ~(OCRNL | ONLCR | ONLRET | ONOCR | OFILL | OPOST);
|
|
options.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
|
|
options.c_cflag &= ~(CSIZE | PARENB);
|
|
options.c_cflag |= CS8;
|
|
options.c_cc[VMIN] = 1;
|
|
options.c_cc[VTIME] = 0;
|
|
|
|
tcsetattr(serial_fd, TCSANOW, &options); // Set the new options for the port "NOW"
|
|
|
|
syslog(LOG_INFO, "opened serial port %s", serialPortName.c_str());
|
|
|
|
return 1;
|
|
}
|
|
|
|
static string msgToString(const char *msg, int msgLen)
|
|
{
|
|
stringstream s;
|
|
|
|
// dealing with the unknown; cap the number of bytes to log
|
|
int printLen = msgLen;
|
|
if (printLen > 8)
|
|
printLen = 8;
|
|
|
|
while (printLen-- > 0) {
|
|
s << setfill('0') << setw(2) << hex << unsigned(*msg);
|
|
if (printLen > 0)
|
|
s << " ";
|
|
msg++;
|
|
}
|
|
|
|
s << setfill(' ') << setw(0) << dec;
|
|
|
|
if (msgLen > 8)
|
|
s << "... (" << msgLen << " bytes long)";
|
|
|
|
return s.str();
|
|
}
|
|
|
|
/***********************************************************************
|
|
Function: void *upstream_task(void*)
|
|
|
|
Description: The upstream thread task. Checks for data available on the
|
|
serial and calls the relevant handler.
|
|
***********************************************************************/
|
|
const int POLLTIMEOUT_MS = 10000;
|
|
void *upstream_task(void *)
|
|
{
|
|
char buf[SERIAL_BUFSIZE];
|
|
char msg[MAX_SERIAL_MESSAGE_LEN];
|
|
struct pollfd fds[1];
|
|
int pollrc;
|
|
int i;
|
|
int msgLen = 0;
|
|
int readlen;
|
|
SLIPDecoder *slipDec = new SLIPDecoder(msg, sizeof(msg));
|
|
uint64_t now_us;
|
|
uint64_t unknown_us = 0;
|
|
unsigned unknown_skip_cnt = 0;
|
|
bool in_sync = false;
|
|
bool invalid_stick_inputs_logged = false;
|
|
|
|
fds[0].fd = serial_fd;
|
|
fds[0].events = POLLIN;
|
|
|
|
lock_thread_mem(32764);
|
|
|
|
while (true) {
|
|
// Wait for available data, block until we get some or timeout
|
|
pollrc = poll(fds, 1, POLLTIMEOUT_MS);
|
|
|
|
if (pollrc > 0 && fds[0].revents & POLLIN) {
|
|
// Check for any new incoming data, send over udp
|
|
readlen = read(serial_fd, buf, SERIAL_BUFSIZE);
|
|
if (readlen > 0) {
|
|
for (i = 0; i < readlen; ++i) {
|
|
// Decode slip packets. Returns:
|
|
// 0 : packet in progress
|
|
// -1 : error in bytes stream, e.g. invalid sync sequence
|
|
// >0 : packet received
|
|
msgLen = slipDec->addByte(&buf[i]);
|
|
if (msgLen <= 0) {
|
|
if (msgLen < 0) {
|
|
syslog(LOG_INFO, "slip error - discarding data");
|
|
in_sync = false;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!in_sync) {
|
|
// First packet after an error (or init) is no good,
|
|
// since we generally did not start after a previous
|
|
// packet's sync. But at this point, we *did* just
|
|
// receive a sync, so we drop the current packet and
|
|
// go get the next one.
|
|
syslog(LOG_INFO, "slip - initial sync");
|
|
in_sync = true;
|
|
continue;
|
|
}
|
|
|
|
// OK, got a message
|
|
|
|
#ifdef INCLUDE_SERIAL_LOG
|
|
serialLog->log_packet(msg, msgLen, SerialLog::PKTFLG_UP);
|
|
#endif // INCLUDE_SERIAL_LOG
|
|
|
|
// syslog(LOG_INFO, "packet %d: %d bytes", int(msg[0]), msgLen);
|
|
|
|
// Get the packet type from the first byte
|
|
// When passing the message, skip the first byte
|
|
// which is the packet type.
|
|
unsigned up = 0;
|
|
switch (msg[0]) {
|
|
case PKT_ID_DSM_CHANNELS:
|
|
up = rc->upHandler(&msg[1], msgLen - 1);
|
|
break;
|
|
case PKT_ID_CALIBRATE:
|
|
break;
|
|
case PKT_ID_SYS_INFO:
|
|
up = sysInfo->upHandler(&msg[1], msgLen - 1);
|
|
break;
|
|
case PKT_ID_MAVLINK:
|
|
up = telem->upHandler(&msg[1], msgLen - 1);
|
|
break;
|
|
case PKT_ID_PAIR_CONFIRM:
|
|
up = pairReq->upHandler(&msg[1], msgLen - 1);
|
|
break;
|
|
// PKT_ID_PAIR_RESULT is never upstream
|
|
case PKT_ID_PARAM_STORED_VALS:
|
|
up = paramStoredVals->upHandler(&msg[1], msgLen - 1);
|
|
break;
|
|
case PKT_ID_SHUTDOWN_REQUEST:
|
|
// Call the system shutdown command
|
|
syslog(LOG_INFO, "STM32 requested shutdown");
|
|
system("shutdown -h now");
|
|
sleep(30); // should get killed in here
|
|
exit(1); // init may restart us if we get here
|
|
break;
|
|
case PKT_ID_BUTTON_EVENT:
|
|
up = btnEvt->upHandler(&msg[1], msgLen - 1);
|
|
break;
|
|
case PKT_ID_INPUT_REPORT:
|
|
up = inputReport->upHandler(&msg[1], msgLen - 1);
|
|
break;
|
|
case PKT_ID_INVALID_STICK_INPUTS:
|
|
// polluting log - print one then ignore
|
|
if (!invalid_stick_inputs_logged) {
|
|
string s = msgToString(msg, msgLen);
|
|
syslog(LOG_INFO, "received invalid stick inputs message: %s",
|
|
s.c_str());
|
|
invalid_stick_inputs_logged = true;
|
|
}
|
|
break;
|
|
case PKT_ID_NOP:
|
|
default:
|
|
// Print unknown messages, but max one per second
|
|
// Rate limit so if the STM32 firmware gets ahead
|
|
// and emits a new message, we don't soak the log.
|
|
now_us = clock_gettime_us(CLOCK_MONOTONIC);
|
|
if (now_us >= unknown_us) {
|
|
// okay to print it
|
|
string s = msgToString(msg, msgLen);
|
|
syslog(LOG_ERR, "unknown packet: %s", s.c_str());
|
|
if (unknown_skip_cnt != 0) {
|
|
syslog(LOG_ERR, "(%u more not printed)", unknown_skip_cnt);
|
|
unknown_skip_cnt = 0;
|
|
}
|
|
// don't print another for 1,000,000 usec
|
|
unknown_us = now_us + 1000000;
|
|
} else {
|
|
// too soon after the previous one; don't print
|
|
unknown_skip_cnt++;
|
|
}
|
|
break;
|
|
}
|
|
unsigned id = msg[0];
|
|
if (id > PKT_ID_MAX)
|
|
id = PKT_ID_MAX;
|
|
pthread_mutex_lock(&mutex_upBytes);
|
|
upMsgs[id]++; // upMsgs is [PKT_ID_MAX + 1]
|
|
upBytes += up;
|
|
pthread_mutex_unlock(&mutex_upBytes);
|
|
}
|
|
}
|
|
} else if (pollrc == 0) {
|
|
// timeout
|
|
syslog(LOG_INFO, "no data received from STM32 for %d msec", POLLTIMEOUT_MS);
|
|
}
|
|
|
|
} // while (true)
|
|
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
/**********************************************************************
|
|
Function: int start_upstream_thread(void)
|
|
|
|
Description: Starts the upstream thread
|
|
***********************************************************************/
|
|
int start_upstream_thread(void)
|
|
{
|
|
int ret = 0;
|
|
struct sched_param param;
|
|
|
|
// Start the upstream thread
|
|
pthread_create(&upstream_ctx, NULL, upstream_task, NULL);
|
|
|
|
pthread_setname_np(upstream_ctx, "stm32_up");
|
|
|
|
// upstream priority and schedule
|
|
param.sched_priority = UPSTREAM_PRIORITY;
|
|
if ((ret = pthread_setschedparam(upstream_ctx, SCHED_FIFO, ¶m)))
|
|
syslog(LOG_ERR, "error %d setting upstream thread priority", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**********************************************************************
|
|
Function: void *downstream_task(void*)
|
|
|
|
Description: The downstream thread task. Selects() on the set of
|
|
fds and calls the relevant handler
|
|
***********************************************************************/
|
|
void *downstream_task(void *)
|
|
{
|
|
int nfds = 0;
|
|
int res = 0;
|
|
fd_set fds;
|
|
int bytesAvailable;
|
|
struct timeval timeout;
|
|
uint64_t log_last_us;
|
|
unsigned downBytes = 0;
|
|
|
|
lock_thread_mem(32764);
|
|
|
|
log_last_us = clock_gettime_us(CLOCK_MONOTONIC);
|
|
|
|
// Select() on the list of UDP FDs.
|
|
while (1) {
|
|
// Block until we get one
|
|
// Generate the list of fds
|
|
FD_ZERO(&fds);
|
|
FD_SET(telem->getfd(), &fds);
|
|
nfds = telem->getfd() + 1;
|
|
|
|
FD_SET(pairReq->getfd(), &fds);
|
|
if (pairReq->getfd() >= nfds)
|
|
nfds = pairReq->getfd() + 1;
|
|
|
|
FD_SET(pairRes->getfd(), &fds);
|
|
if (pairRes->getfd() >= nfds)
|
|
nfds = pairRes->getfd() + 1;
|
|
|
|
FD_SET(sysInfo->getfd(), &fds);
|
|
if (sysInfo->getfd() >= nfds)
|
|
nfds = sysInfo->getfd() + 1;
|
|
|
|
FD_SET(paramStoredVals->getfd(), &fds);
|
|
if (paramStoredVals->getfd() >= nfds)
|
|
nfds = paramStoredVals->getfd() + 1;
|
|
|
|
FD_SET(configStickAxes->getfd(), &fds);
|
|
if (configStickAxes->getfd() >= nfds)
|
|
nfds = configStickAxes->getfd() + 1;
|
|
|
|
FD_SET(setTelemUnits->getfd(), &fds);
|
|
if (setTelemUnits->getfd() >= nfds)
|
|
nfds = setTelemUnits->getfd() + 1;
|
|
|
|
FD_SET(configSweepTime->getfd(), &fds);
|
|
if (configSweepTime->getfd() >= nfds)
|
|
nfds = configSweepTime->getfd() + 1;
|
|
|
|
FD_SET(btnFuncCfg->getfd(), &fds);
|
|
if (btnFuncCfg->getfd() >= nfds)
|
|
nfds = btnFuncCfg->getfd() + 1;
|
|
|
|
FD_SET(setShotInfo->getfd(), &fds);
|
|
if (setShotInfo->getfd() >= nfds)
|
|
nfds = setShotInfo->getfd() + 1;
|
|
|
|
FD_SET(updater->getfd(), &fds);
|
|
if (updater->getfd() >= nfds)
|
|
nfds = updater->getfd() + 1;
|
|
|
|
FD_SET(lockoutState->getfd(), &fds);
|
|
if (lockoutState->getfd() >= nfds)
|
|
nfds = lockoutState->getfd() + 1;
|
|
|
|
FD_SET(appConn->getfd(), &fds);
|
|
if (appConn->getfd() >= nfds)
|
|
nfds = appConn->getfd() + 1;
|
|
|
|
// select timeout 0.1 sec
|
|
// (note that select can update timeout)
|
|
// need to time out, so if downlink stops we still see stats
|
|
timeout.tv_sec = 0;
|
|
timeout.tv_usec = 100000;
|
|
|
|
res = select(nfds, &fds, NULL, NULL, &timeout);
|
|
|
|
if (res > 0) {
|
|
|
|
// Run the appropriate downstream handler
|
|
if (FD_ISSET(telem->getfd(), &fds)) {
|
|
// record the amount of data we download
|
|
ioctl(telem->getfd(), FIONREAD, &bytesAvailable);
|
|
|
|
// Run the handler
|
|
telem->downHandler(serial_fd);
|
|
|
|
// Record the amount of data we got down
|
|
downBytes += bytesAvailable;
|
|
}
|
|
|
|
if (FD_ISSET(pairReq->getfd(), &fds)) {
|
|
// Don't increment the amount of data we send here
|
|
syslog(LOG_INFO, "pair request going down");
|
|
pairReq->downHandler(serial_fd, dbg_downHandler);
|
|
}
|
|
|
|
if (FD_ISSET(pairRes->getfd(), &fds)) {
|
|
// Don't increment the amount of data we send here
|
|
syslog(LOG_INFO, "pair result going down");
|
|
pairRes->downHandler(serial_fd, dbg_downHandler);
|
|
}
|
|
|
|
if (FD_ISSET(sysInfo->getfd(), &fds)) {
|
|
// Don't increment the amount of data we send here
|
|
sysInfo->downHandler(serial_fd, dbg_downHandler);
|
|
}
|
|
|
|
if (FD_ISSET(paramStoredVals->getfd(), &fds))
|
|
paramStoredVals->downHandler(serial_fd, dbg_downHandler);
|
|
|
|
if (FD_ISSET(configStickAxes->getfd(), &fds))
|
|
configStickAxes->downHandler(serial_fd, dbg_downHandler);
|
|
|
|
if (FD_ISSET(setTelemUnits->getfd(), &fds))
|
|
setTelemUnits->downHandler(serial_fd, dbg_downHandler);
|
|
|
|
if (FD_ISSET(configSweepTime->getfd(), &fds))
|
|
configSweepTime->downHandler(serial_fd, dbg_downHandler);
|
|
|
|
if (FD_ISSET(btnFuncCfg->getfd(), &fds))
|
|
btnFuncCfg->downHandler(serial_fd, dbg_downHandler);
|
|
|
|
if (FD_ISSET(setShotInfo->getfd(), &fds))
|
|
setShotInfo->downHandler(serial_fd, dbg_downHandler);
|
|
|
|
if (FD_ISSET(updater->getfd(), &fds)) {
|
|
// Don't increment the amount of data we send here
|
|
syslog(LOG_INFO, "updater message going down");
|
|
updater->downHandler(serial_fd, dbg_downHandler);
|
|
}
|
|
|
|
if (FD_ISSET(lockoutState->getfd(), &fds)) {
|
|
// Don't increment the amount of data we send here
|
|
syslog(LOG_INFO, "lockout state message going down");
|
|
lockoutState->downHandler(serial_fd, dbg_downHandler);
|
|
}
|
|
|
|
if (FD_ISSET(appConn->getfd(), &fds)) {
|
|
// Don't increment the amount of data we send here
|
|
syslog(LOG_INFO, "app connected message going down");
|
|
appConn->downHandler(serial_fd, dbg_downHandler);
|
|
}
|
|
}
|
|
|
|
// check for time to log status logging
|
|
uint64_t now_us = clock_gettime_us(CLOCK_MONOTONIC);
|
|
uint64_t delta = now_us - log_last_us;
|
|
if (delta >= LOG_DT_US) {
|
|
unsigned newUpBytes;
|
|
unsigned newUpMsgs[PKT_ID_MAX + 1];
|
|
pthread_mutex_lock(&mutex_upBytes);
|
|
newUpBytes = upBytes;
|
|
upBytes = 0;
|
|
memcpy(newUpMsgs, upMsgs, sizeof(newUpMsgs));
|
|
memset(upMsgs, 0, sizeof(upMsgs));
|
|
pthread_mutex_unlock(&mutex_upBytes);
|
|
|
|
log_last_us = now_us;
|
|
|
|
// normally need about 20 chars tops
|
|
char msg[200];
|
|
int x = sizeof(msg) - 1;
|
|
char *p = msg;
|
|
memset(msg, 0, sizeof(msg));
|
|
for (int i = 0; i <= PKT_ID_MAX; i++) {
|
|
if (newUpMsgs[i] != 0) {
|
|
int k = snprintf(p, x, "%d:%u ", i, newUpMsgs[i]);
|
|
if (k >= x)
|
|
break;
|
|
x -= k;
|
|
p += k;
|
|
}
|
|
}
|
|
|
|
syslog(LOG_INFO, "up %s%u B %0.2f KB/s dn %u B %0.2f KB/s", msg, newUpBytes,
|
|
newUpBytes / 1024.0 / (delta / 1e6), downBytes,
|
|
downBytes / 1024.0 / (delta / 1e6));
|
|
|
|
downBytes = 0;
|
|
}
|
|
}
|
|
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
/**********************************************************************
|
|
Function: int start_downstream_thread(void)
|
|
|
|
Description: Starts the downstream thread
|
|
***********************************************************************/
|
|
int start_downstream_thread(void)
|
|
{
|
|
int ret = 0;
|
|
struct sched_param param;
|
|
|
|
// Start the downstream thread
|
|
pthread_create(&downstream_ctx, NULL, downstream_task, NULL);
|
|
|
|
pthread_setname_np(downstream_ctx, "stm32_down");
|
|
|
|
// Set the priority and scheduler
|
|
memset(¶m, 0, sizeof(param));
|
|
param.sched_priority = DOWNSTREAM_PRIORITY;
|
|
if ((ret = pthread_setschedparam(downstream_ctx, SCHED_FIFO, ¶m)))
|
|
syslog(LOG_ERR, "error %d setting downstream thread priority", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**********************************************************************
|
|
Function: bool readSoloIP(string filename, string *ip)
|
|
|
|
Description: Reads the Solo's IP address from the solo Address File
|
|
***********************************************************************/
|
|
bool readSoloIP(string filename, string *ip)
|
|
{
|
|
ifstream fd;
|
|
|
|
// Open the address file, return false if its not been created yet
|
|
fd.open(filename.c_str(), ios::in);
|
|
if (!fd)
|
|
return false;
|
|
|
|
ip->clear();
|
|
fd >> *ip;
|
|
fd.close();
|
|
|
|
return true;
|
|
}
|
|
|
|
/**********************************************************************
|
|
Function: int main(void)
|
|
|
|
Description: The main function. Initializes and runs the serial and
|
|
UDP threads.
|
|
***********************************************************************/
|
|
int main(void)
|
|
{
|
|
int rcDestPort;
|
|
int rcUpTos;
|
|
int mavDestPort;
|
|
int sysDestPort;
|
|
int pairReqDestPort;
|
|
int pairResDestPort;
|
|
int paramStoredValsDestPort;
|
|
int configStickAxesDestPort;
|
|
int setTelemUnitsDestPort;
|
|
string setTelemUnitsSetting;
|
|
int telemLogGap;
|
|
int telemLogDelayMax;
|
|
string telemLogDelayFile;
|
|
int configSweepTimeDestPort;
|
|
int btnEvtDestPort;
|
|
int inputReportDestPort;
|
|
int btnFuncCfgDestPort;
|
|
int setShotInfoDestPort;
|
|
int updaterDestPort;
|
|
int lockoutStateDestPort;
|
|
int appConnDestPort;
|
|
int baudrate;
|
|
string soloIPAddr;
|
|
bool haveSoloIP = false;
|
|
|
|
openlog("stm32", LOG_NDELAY, LOG_LOCAL0);
|
|
|
|
syslog(LOG_INFO, "main: starting: built " __DATE__ " " __TIME__);
|
|
|
|
/* Parse the sololink.conf file for serial port, source IPs and ports */
|
|
INIReader reader("/etc/sololink.conf");
|
|
|
|
if (reader.ParseError() < 0) {
|
|
syslog(LOG_ERR, "main: can't load /etc/sololink.conf");
|
|
return -1;
|
|
}
|
|
|
|
/* Get all parameter info */
|
|
serialPortName = reader.Get("solo", "stm32Dev", "/dev/ttymxc1");
|
|
rcDestPort = reader.GetInteger("solo", "rcDestPort", 5005);
|
|
rcUpTos = reader.GetInteger("solo", "rcUpTos", 0xff);
|
|
mavDestPort = reader.GetInteger("solo", "mavDestPort", 5015);
|
|
sysDestPort = reader.GetInteger("solo", "sysDestPort", 5012);
|
|
pairReqDestPort = reader.GetInteger("solo", "pairReqDestPort", 5013);
|
|
pairResDestPort = reader.GetInteger("solo", "pairResDestPort", 5014);
|
|
paramStoredValsDestPort = reader.GetInteger("solo", "paramStoredValsDestPort", 5011);
|
|
configStickAxesDestPort = reader.GetInteger("solo", "configStickAxesDestPort", 5010);
|
|
setTelemUnitsDestPort = reader.GetInteger("solo", "setTelemUnitsDestPort", 5024);
|
|
setTelemUnitsSetting = reader.Get("solo", "uiUnits", "metric");
|
|
telemLogGap = reader.GetInteger("solo", "telemLogGap", 1000000);
|
|
syslog(LOG_INFO, "logging telemetry gaps >= %d msec", (telemLogGap + 500) / 1000);
|
|
telemLogDelayMax =
|
|
reader.GetInteger("solo", "telemLogDelayMax", 100000); // 100,000 is ~20 minutes, 6.4 MB
|
|
telemLogDelayFile = reader.Get("solo", "telemLogDelayFile", "");
|
|
if (telemLogDelayFile != "")
|
|
syslog(LOG_INFO, "main: logging packet delays to %s (%d records per file)",
|
|
telemLogDelayFile.c_str(), telemLogDelayMax);
|
|
configSweepTimeDestPort = reader.GetInteger("solo", "configSweepTimeDestPort", 5022);
|
|
btnEvtDestPort = reader.GetInteger("solo", "buttonEventDestPort", 5016);
|
|
inputReportDestPort = reader.GetInteger("solo", "inputReportDestPort", 5021);
|
|
btnFuncCfgDestPort = reader.GetInteger("solo", "buttonFunctionConfigDestPort", 5017);
|
|
setShotInfoDestPort = reader.GetInteger("solo", "setShotInfoDestPort", 5018);
|
|
updaterDestPort = reader.GetInteger("solo", "updaterDestPort", 5019);
|
|
lockoutStateDestPort = reader.GetInteger("solo", "lockoutStateDestPort", 5020);
|
|
appConnDestPort = reader.GetInteger("solo", "appConnDestPort", 5026);
|
|
baudrate = reader.GetInteger("solo", "stm32Baud", 115200);
|
|
dbg_downHandler = reader.GetInteger("solo", "dbg_downHandler", 0);
|
|
if (dbg_downHandler != 0)
|
|
syslog(LOG_INFO, "main: dbg_downHandler=0x%08x", dbg_downHandler);
|
|
|
|
#ifdef INCLUDE_SERIAL_LOG
|
|
// serial log
|
|
serialLogSize = reader.GetInteger("solo", "serialLogSize", 50000);
|
|
serialLogTimeout_us = reader.GetInteger("solo", "serialLogTimeout_us", 1000000);
|
|
|
|
serialLog = new SerialLog(serialLogSize, serialLogTimeout_us);
|
|
#endif // INCLUDE_SERIAL_LOG
|
|
|
|
// shared variable mutex
|
|
if (mutex_init(&mutex_upBytes) != 0) {
|
|
syslog(LOG_ERR, "main: can't initialize mutex");
|
|
return -1;
|
|
}
|
|
|
|
// initialize all the handler objects
|
|
|
|
// The RC destination address gets updated by the Solo IP address file. We don't
|
|
// send any data to the Solo until we've got that address
|
|
rc = new RC("", rcDestPort, rcUpTos);
|
|
telem = new Telem("127.0.0.1", mavDestPort, telemLogGap, telemLogDelayMax, telemLogDelayFile);
|
|
sysInfo = new SysInfo("127.0.0.1", sysDestPort);
|
|
pairReq = new PairReq("127.0.0.1", pairReqDestPort);
|
|
pairRes = new PairRes("127.0.0.1", pairResDestPort);
|
|
paramStoredVals = new ParamStoredVals(paramStoredValsDestPort);
|
|
configStickAxes = new ConfigStickAxes(configStickAxesDestPort);
|
|
setTelemUnits = new SetTelemUnits(setTelemUnitsDestPort);
|
|
configSweepTime = new ConfigSweepTime(configSweepTimeDestPort);
|
|
btnEvt = new ButtonEventHandler(btnEvtDestPort);
|
|
inputReport = new InputReport(inputReportDestPort, 1);
|
|
btnFuncCfg = new ButtonFunctionCfg(btnFuncCfgDestPort);
|
|
setShotInfo = new SetShotInfo(setShotInfoDestPort);
|
|
updater = new Updater("127.0.0.1", updaterDestPort);
|
|
lockoutState = new LockoutState(lockoutStateDestPort);
|
|
appConn = new AppConnected("127.0.0.1", appConnDestPort);
|
|
|
|
/* Start the serial port */
|
|
if (!serial_setup(baudrate)) {
|
|
syslog(LOG_ERR, "main: unable to initialize serial port");
|
|
return -1;
|
|
}
|
|
|
|
/* Start the upstream and downstream threads */
|
|
start_downstream_thread();
|
|
start_upstream_thread();
|
|
|
|
// Send a ping to get the STM32 information in the log
|
|
sysInfo->ping();
|
|
|
|
// Set the telemetry display units
|
|
setTelemUnits->set(setTelemUnitsSetting);
|
|
|
|
// The main while() loop just becomes a thread monitor. If any thread dies,
|
|
// exit and let inittab restart us
|
|
// We also check to see if the solo IP address has been written yet
|
|
while (true) {
|
|
if (pthread_kill(upstream_ctx, 0) != 0 || pthread_kill(downstream_ctx, 0) != 0) {
|
|
syslog(LOG_ERR, "a thread is not running, exiting");
|
|
exit(0);
|
|
}
|
|
|
|
// Check the Solo IP address file
|
|
if (!haveSoloIP) {
|
|
haveSoloIP = readSoloIP("/var/run/solo.ip", &soloIPAddr);
|
|
if (haveSoloIP) {
|
|
syslog(LOG_INFO, "got solo ip: %s", soloIPAddr.c_str());
|
|
rc->setSoloIP(&soloIPAddr);
|
|
}
|
|
}
|
|
|
|
// We also "ping" the STM32 occasionally to make sure its still talking to us.
|
|
sysInfo->ping();
|
|
|
|
// Long sleep
|
|
sleep(1);
|
|
}
|
|
|
|
return -1;
|
|
}
|