mirror of
https://github.com/OpenSolo/OpenSolo.git
synced 2025-04-30 06:34:38 +02:00
313 lines
11 KiB
C++
313 lines
11 KiB
C++
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <iostream>
|
|
#include <sched.h>
|
|
#include <stdint.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
#include <netdb.h>
|
|
#include <time.h>
|
|
#include <sys/time.h>
|
|
#include <termios.h>
|
|
#include <sys/mman.h>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <vector>
|
|
#include <arpa/inet.h>
|
|
#include <cmath>
|
|
#include <iomanip>
|
|
#include <signal.h>
|
|
#include <sys/stat.h>
|
|
#include "Log.h"
|
|
#include "../mavlink/c_library/common/mavlink.h"
|
|
#include "../mavlink/c_library/ardupilotmega/mavlink.h"
|
|
#include "util.h"
|
|
|
|
using namespace std;
|
|
|
|
/***********************************************************************
|
|
UDP macros
|
|
***********************************************************************/
|
|
#define BUFSIZE 4096
|
|
|
|
/***********************************************************************
|
|
File descriptors
|
|
***********************************************************************/
|
|
int sock_fd;
|
|
|
|
/***********************************************************************
|
|
TLOG source port from telem_ctrl.py
|
|
***********************************************************************/
|
|
#define TLOG_SOURCE_PORT 14583
|
|
|
|
/***********************************************************************
|
|
Log file
|
|
***********************************************************************/
|
|
Log *logfile;
|
|
|
|
/***********************************************************************
|
|
Max log file size
|
|
***********************************************************************/
|
|
#define MAX_LOGFILE_SIZE 100000000 // 100MB
|
|
#define MAX_LOG_FILES 9
|
|
|
|
/***********************************************************************
|
|
Log check timeout
|
|
***********************************************************************/
|
|
#define LOG_CHECK_US 5000000 // Check every 5s
|
|
|
|
/***********************************************************************
|
|
Logfile directory and name
|
|
(directory can come from environment)
|
|
***********************************************************************/
|
|
#define LOGFILE_DIR "/log"
|
|
#define LOGFILE_NAME "solo.tlog"
|
|
|
|
/***********************************************************************
|
|
Function: int UDP_setup(void)
|
|
|
|
Description: Sets up the UDP receive port. Returns 0 in the event of
|
|
an error, 1 otherwise.
|
|
***********************************************************************/
|
|
int UDP_setup(void)
|
|
{
|
|
struct sockaddr_in myaddr; /* our address */
|
|
struct timeval timeout;
|
|
int sourcePort = TLOG_SOURCE_PORT;
|
|
|
|
/* create a UDP socket */
|
|
if ((sock_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
|
cerr << "cannot create socket" << endl;
|
|
return 0;
|
|
}
|
|
|
|
/* Socket timeout */
|
|
timeout.tv_sec = 0;
|
|
timeout.tv_usec = 1000; // 1ms
|
|
setsockopt(sock_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout));
|
|
|
|
/* Bind the socket to any IP, but we'll check the source later */
|
|
memset((char *)&myaddr, 0, sizeof(myaddr));
|
|
myaddr.sin_family = AF_INET;
|
|
myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
myaddr.sin_port = htons(sourcePort);
|
|
|
|
if (bind(sock_fd, (struct sockaddr *)&myaddr, sizeof(myaddr)) < 0) {
|
|
cerr << "bind failed" << endl;
|
|
return 0;
|
|
}
|
|
|
|
cout << "Successfully opened port " << sourcePort << endl;
|
|
return 1;
|
|
}
|
|
|
|
/***********************************************************************
|
|
Function: void dump_versions(void)
|
|
|
|
Description: Reads the sololink, artoolink, artoo, and pixhawk versions
|
|
and dumps them into the tlog as statustext
|
|
***********************************************************************/
|
|
void dump_versions(void)
|
|
{
|
|
string s_sl_ver("UNK"); // Sololink version
|
|
string s_pix_ver("UNK"); // Pixhawk version
|
|
string s_al_ver("UNK"); // Artoolink version
|
|
string s_a_ver("UNK"); // Artoo (STM32) version
|
|
string s_all_vers; // The whole statustext string
|
|
char stringbuf[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN]; // String buffer for mav pack
|
|
ifstream infile; // File for reading
|
|
mavlink_message_t mav_msg; // Mavlink message for statustext versions
|
|
uint64_t usec; // Timestamp
|
|
char msg[BUFSIZE + 8]; // Message including timestamp to dump to file
|
|
unsigned char buf[BUFSIZE]; // mavlink message in char buffer
|
|
unsigned len;
|
|
|
|
// Read the Pixhawk verison number
|
|
infile.open("/tmp/PEER_FW_VERSION", ifstream::in);
|
|
if (!infile)
|
|
cerr << "Unable to open pixhawk firmware version file" << endl;
|
|
else {
|
|
getline(infile, s_pix_ver);
|
|
if (!infile)
|
|
cerr << "Error reading Pixhawk firmware version" << endl;
|
|
infile.close();
|
|
}
|
|
|
|
// Read the SoloLink version number
|
|
infile.open("/tmp/PEER_SL_VERSION", ifstream::in);
|
|
if (!infile)
|
|
cerr << "Unable to open Sololink firmware version file" << endl;
|
|
else {
|
|
getline(infile, s_sl_ver);
|
|
if (!infile)
|
|
cerr << "Error reading Sololink firmware version" << endl;
|
|
infile.close();
|
|
}
|
|
|
|
// Read the Artoolink version number
|
|
infile.open("/VERSION", ifstream::in);
|
|
if (!infile)
|
|
cerr << "Unable to open Artoolink firmware version file" << endl;
|
|
else {
|
|
getline(infile, s_al_ver);
|
|
if (!infile)
|
|
cerr << "Error reading Artoolink firmware version" << endl;
|
|
infile.close();
|
|
}
|
|
|
|
// Read the Artoo (STM32) version number
|
|
infile.open("/STM_VERSION", ifstream::in);
|
|
if (!infile)
|
|
cerr << "Unable to open Artoo STM32 firmware version file" << endl;
|
|
else {
|
|
getline(infile, s_a_ver);
|
|
if (!infile)
|
|
cerr << "Error reading Artoo STM32 firmware version" << endl;
|
|
infile.close();
|
|
}
|
|
|
|
// Create a STATUSTEXT mavlink message to dump in the log.
|
|
s_all_vers = "P:" + s_pix_ver + " SL:" + s_sl_ver + " AL:" + s_al_ver + " A:" + s_a_ver;
|
|
|
|
// Copy 49 characters (save room for a null terminator), truncating.
|
|
memset(stringbuf, 0, sizeof(stringbuf));
|
|
len = s_all_vers.length();
|
|
if (len >= 50)
|
|
len = 49;
|
|
memcpy(stringbuf, s_all_vers.c_str(), len);
|
|
|
|
// Pack into a statustext message
|
|
mavlink_msg_statustext_pack(0, 0, &mav_msg, MAV_SEVERITY_INFO, stringbuf);
|
|
|
|
// Pull the char buffer out of the mavlink message (len reused here)
|
|
len = mavlink_msg_to_send_buffer((uint8_t *)buf, &mav_msg);
|
|
|
|
// This should be thread safe, it happens atomically in UDP_task or
|
|
// before UDP_task is started
|
|
usec = clock_gettime_us(CLOCK_REALTIME);
|
|
|
|
for (int i = 0; i < 8; ++i)
|
|
msg[i] = usec >> (8 - (i + 1)) * 8;
|
|
|
|
memcpy(msg + sizeof(uint64_t), buf, len);
|
|
logfile->log_fd.write((const char *)msg, len + sizeof(uint64_t));
|
|
logfile->log_fd.flush();
|
|
}
|
|
|
|
/***********************************************************************
|
|
Function: void UDP_task(void)
|
|
|
|
Description: The main UDP task. Waits for data on the telem UDP
|
|
and just dumps it into the tlog file.
|
|
***********************************************************************/
|
|
void UDP_task(void)
|
|
{
|
|
int recvlen; /* # bytes received */
|
|
unsigned char buf[BUFSIZE]; /* receive buffer */
|
|
unsigned char *buf_ptr;
|
|
int msglen;
|
|
struct sockaddr_in srcAddr;
|
|
socklen_t slen = sizeof(srcAddr);
|
|
uint64_t usec;
|
|
char msg[BUFSIZE + 8];
|
|
mavlink_message_t mav_msg;
|
|
mavlink_status_t mav_status;
|
|
mavlink_heartbeat_t heartbeat;
|
|
bool armedState = false;
|
|
uint64_t last, now;
|
|
|
|
last = clock_gettime_us(CLOCK_MONOTONIC);
|
|
|
|
while (true) {
|
|
// Attempt to receive data
|
|
recvlen = recvfrom(sock_fd, buf, BUFSIZE, 0, (sockaddr *)&srcAddr, &slen);
|
|
|
|
// TODO:we should check that this only comes from the Artoo
|
|
if (recvlen > 0) {
|
|
/* We can get multiple messages per udp datagram, so run through them all */
|
|
buf_ptr = buf;
|
|
while (1) {
|
|
/*Make sure this is a mavlink message. If its not, bail out of this whole thing */
|
|
if (buf_ptr[0] != 0xFE)
|
|
break;
|
|
|
|
msglen = buf_ptr[1] + 8;
|
|
|
|
// If we get a "disarm" from MAVLINK, roll the log here
|
|
if (buf_ptr[5] == MAVLINK_MSG_ID_HEARTBEAT) {
|
|
for (int i = 0; i < msglen; ++i) {
|
|
if (mavlink_parse_char(MAVLINK_COMM_0, buf_ptr[i], &mav_msg, &mav_status)) {
|
|
mavlink_msg_heartbeat_decode(&mav_msg, &heartbeat);
|
|
if (heartbeat.base_mode & MAV_MODE_FLAG_SAFETY_ARMED) {
|
|
if (!armedState)
|
|
armedState = true;
|
|
} else {
|
|
if (armedState) {
|
|
logfile->forceRoll();
|
|
dump_versions(); // Dump the versions into each new file
|
|
armedState = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
usec = clock_gettime_us(CLOCK_REALTIME);
|
|
|
|
for (int i = 0; i < 8; ++i)
|
|
msg[i] = usec >> (8 - (i + 1)) * 8;
|
|
|
|
memcpy(msg + sizeof(uint64_t), buf_ptr, msglen);
|
|
|
|
logfile->log_fd.write((const char *)msg, msglen + sizeof(uint64_t));
|
|
logfile->log_fd.flush();
|
|
|
|
buf_ptr += msglen;
|
|
if (buf_ptr >= (buf + recvlen))
|
|
break;
|
|
}
|
|
}
|
|
|
|
now = clock_gettime_us(CLOCK_MONOTONIC);
|
|
|
|
// Check if we need to roll the logfile
|
|
if ((now - last) > LOG_CHECK_US) {
|
|
logfile->checkSizeAndRoll();
|
|
last = now;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**********************************************************************
|
|
Function: int main(void)
|
|
|
|
Description: The main function. Initializes and runs the serial and
|
|
UDP threads.
|
|
***********************************************************************/
|
|
int main(void)
|
|
{
|
|
logfile = new Log("/log/solo.tlog", MAX_LOGFILE_SIZE, MAX_LOG_FILES, true);
|
|
|
|
if (!logfile->log_fd) {
|
|
cerr << "Unable to open logfile" << endl;
|
|
return -1;
|
|
}
|
|
|
|
// Dump the versions of each system into the log file
|
|
dump_versions(); // Dump the versions into each new file
|
|
|
|
/* Set up the UDP and serial ports */
|
|
if (!UDP_setup()) {
|
|
cerr << "Unable to initialize the UDP receive" << endl;
|
|
return -1;
|
|
}
|
|
|
|
// We'll enter an infinite loop here
|
|
UDP_task();
|
|
|
|
return -1;
|
|
}
|