mirror of
https://github.com/OpenSolo/OpenSolo.git
synced 2025-04-30 06:34:38 +02:00
167 lines
4.7 KiB
C++
167 lines
4.7 KiB
C++
#include <iostream>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <syslog.h>
|
|
#include <time.h>
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include "packetTypes.h"
|
|
#include "util.h"
|
|
#include "RC.h"
|
|
|
|
#define RC_BUFSIZE 26
|
|
|
|
/* How often to log a sendto() error */
|
|
#define SENDTO_LOG_DT_US 1000000
|
|
|
|
using namespace std;
|
|
|
|
/***********************************************************************
|
|
Method: RC constructor
|
|
|
|
Description: Sets up the RC UDP port based on an ip address string, port
|
|
and TOS value.
|
|
Call the base constructor, but store the solo IP address
|
|
separately as we may not be connected to it at the moment
|
|
***********************************************************************/
|
|
RC::RC(string ipaddr, int port, int tos) : PacketHandler("", port, tos, PKT_ID_DSM_CHANNELS)
|
|
{
|
|
// Set up the sequence number
|
|
_sequence = 0;
|
|
|
|
// We are not yet connected
|
|
_connected = false;
|
|
}
|
|
|
|
/***********************************************************************
|
|
Method: RC message constructor
|
|
|
|
Description: Encodes the RC data in the following format:
|
|
|
|
he packet as received has the channel data starting at pkt[0].
|
|
Channel data is little-endian.
|
|
|
|
Start
|
|
Byte Size Description
|
|
0 2 Channel 0
|
|
2 2 Channel 1
|
|
4 2 Channel 2
|
|
6 2 Channel 3
|
|
8 2 Channel 4
|
|
10 2 Channel 5
|
|
12 2 Channel 6
|
|
14 2 Channel 7
|
|
16 (packet length)
|
|
|
|
A packet as sent to a UDP port has the following format. All fields
|
|
are little-endian.
|
|
|
|
Start
|
|
Byte Size Description
|
|
0 8 Timestamp, usec since stm32 process started
|
|
8 2 Sequence number
|
|
10 2 Channel 0
|
|
12 2 Channel 1
|
|
14 2 Channel 2
|
|
16 2 Channel 3
|
|
18 2 Channel 4
|
|
20 2 Channel 5
|
|
22 2 Channel 6
|
|
24 2 Channel 7
|
|
26 (packet length)
|
|
|
|
***********************************************************************/
|
|
int RC::constructMessage(char inBuf[], char outBuf[], int inBufLen)
|
|
{
|
|
uint64_t timestamp_us;
|
|
|
|
// Note that this is CLOCK_REALTIME, vs. CLOCK_MONOTONIC used
|
|
// in most other places.
|
|
timestamp_us = clock_gettime_us(CLOCK_REALTIME);
|
|
|
|
memcpy(outBuf, ×tamp_us, sizeof(timestamp_us));
|
|
memcpy(&outBuf[8], &_sequence, sizeof(_sequence));
|
|
memcpy(&outBuf[10], inBuf, inBufLen);
|
|
|
|
++_sequence;
|
|
|
|
return (inBufLen + 10);
|
|
}
|
|
|
|
/***********************************************************************
|
|
Method: RC data handler
|
|
|
|
Description: Transmits RC data over the UDP. Checks RC data for
|
|
integrity, and logs if incorrect
|
|
***********************************************************************/
|
|
int RC::upHandler(char serBuf[], int len)
|
|
{
|
|
char buf[RC_BUFSIZE];
|
|
int n;
|
|
int bytesSent;
|
|
static uint64_t last_logerr_us = 0;
|
|
uint64_t now_us;
|
|
static int n_sendto_errs = 0;
|
|
|
|
// Need to wait until we get the solo IP address from the
|
|
// pair server before sending any RC data.
|
|
if (!_connected)
|
|
return 0;
|
|
|
|
if (len != 16) {
|
|
// this happens!
|
|
int npr = 16;
|
|
if (npr > len)
|
|
npr = len;
|
|
char buf[64];
|
|
char *p = buf;
|
|
int m = sizeof(buf) - 1;
|
|
memset(buf, 0, sizeof(buf));
|
|
for (int i = 0; i < npr; i++) {
|
|
int n = snprintf(p, m, "%02x ", (unsigned)(serBuf[i]));
|
|
if (n >= m)
|
|
break;
|
|
p += n;
|
|
m -= n;
|
|
}
|
|
syslog(LOG_ERR, "bad RC packet (len=%d): %s", len, buf);
|
|
return 0;
|
|
}
|
|
|
|
// Create the string to be sent
|
|
n = constructMessage(serBuf, buf, len);
|
|
|
|
// Send the data
|
|
bytesSent = sendto(_sock_fd, buf, n, 0, (struct sockaddr *)&_sock, sizeof(_sock));
|
|
|
|
if (bytesSent <= 0) {
|
|
++n_sendto_errs;
|
|
|
|
// Log the error and the number of failures we've seen
|
|
now_us = clock_gettime_us(CLOCK_MONOTONIC);
|
|
if (now_us - last_logerr_us > SENDTO_LOG_DT_US) {
|
|
syslog(LOG_ERR, "RC send failed (%i): %s\n", n_sendto_errs, strerror(errno));
|
|
last_logerr_us = now_us;
|
|
n_sendto_errs = 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
return bytesSent;
|
|
}
|
|
|
|
/***********************************************************************
|
|
Method: setSoloIP
|
|
|
|
Description: Sets the Solo IP after receiving it from the solo ip file
|
|
***********************************************************************/
|
|
void RC::setSoloIP(string *address)
|
|
{
|
|
// Changing the socket is thread-safe because we check the
|
|
//_connected flag in the upHandler.
|
|
inet_aton(address->c_str(), &_sock.sin_addr);
|
|
_connected = true;
|
|
}
|