implemented precision timestamps in report messages
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/01/27.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "messages.h"
|
||||
#include "event_reports.h"
|
||||
|
||||
static uint8_t evt_buf[10];
|
||||
|
||||
bool EventReport_Start(EventReport *report)
|
||||
{
|
||||
assert_param(report->timestamp != 0);
|
||||
|
||||
TF_Msg msg;
|
||||
TF_ClearMsg(&msg);
|
||||
msg.len = (TF_LEN) (report->length + 1 /*callsign*/ + 1 /*type*/ + 8 /*checksum*/);
|
||||
msg.type = MSG_UNIT_REPORT;
|
||||
if (!TF_Send_Multipart(comm, &msg)) {
|
||||
dbg("!! Err sending event");
|
||||
return false;
|
||||
}
|
||||
|
||||
PayloadBuilder pb = pb_start(evt_buf, 10, NULL);
|
||||
pb_u8(&pb, report->unit->callsign);
|
||||
pb_u8(&pb, report->type);
|
||||
pb_u64(&pb, report->timestamp);
|
||||
assert_param(pb.ok);
|
||||
TF_Multipart_Payload(comm, evt_buf, 10);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EventReport_Data(const uint8_t *buff, uint16_t len)
|
||||
{
|
||||
TF_Multipart_Payload(comm, buff, len);
|
||||
}
|
||||
|
||||
void EventReport_End(void)
|
||||
{
|
||||
TF_Multipart_Close(comm);
|
||||
}
|
||||
|
||||
bool EventReport_Send(EventReport *report)
|
||||
{
|
||||
assert_param(report->data != NULL);
|
||||
|
||||
if (!EventReport_Start(report)) return false;
|
||||
EventReport_Data(report->data, report->length);
|
||||
EventReport_End();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// Created by MightyPork on 2018/01/27.
|
||||
//
|
||||
|
||||
#ifndef GEX_F072_EVENT_REPORTS_H
|
||||
#define GEX_F072_EVENT_REPORTS_H
|
||||
|
||||
#ifndef GEX_MESSAGES_H
|
||||
#error "Include messages.h instead!"
|
||||
#endif
|
||||
|
||||
#include <TinyFrame.h>
|
||||
#include "framework/unit.h"
|
||||
|
||||
/**
|
||||
* Event report object
|
||||
*/
|
||||
typedef struct event_report_ {
|
||||
Unit *unit; //!< Reporting unit
|
||||
uint8_t type; //!< Report type (if unit has multiple reports, otherwise 0)
|
||||
uint64_t timestamp; //!< Microsecond timestamp of the event, captured as close as possible to the IRQ
|
||||
uint16_t length; //!< Payload length
|
||||
uint8_t *data; //!< Data if using the EventReport_Send() function, otherwise NULL and data is sent using EventReport_Data()
|
||||
} EventReport;
|
||||
|
||||
bool EventReport_Send(EventReport *report);
|
||||
|
||||
bool EventReport_Start(EventReport *report);
|
||||
void EventReport_Data(const uint8_t *buff, uint16_t len);
|
||||
void EventReport_End(void);
|
||||
|
||||
#endif //GEX_F072_EVENT_REPORTS_H
|
||||
@@ -49,6 +49,7 @@ extern TinyFrame *comm;
|
||||
#include "msg_responses.h"
|
||||
#include "msg_bulkread.h"
|
||||
#include "msg_bulkwrite.h"
|
||||
#include "event_reports.h"
|
||||
|
||||
/**
|
||||
* Initialize TinyFrame and set up listeners
|
||||
|
||||
@@ -13,3 +13,8 @@
|
||||
#include "utils/malloc_safe.h"
|
||||
#include "payload_builder.h"
|
||||
#include "payload_parser.h"
|
||||
#include "utils/avrlibc.h"
|
||||
#include "tasks/task_msg.h"
|
||||
#include "platform/timebase.h"
|
||||
#include "platform/irq_dispatcher.h"
|
||||
#include "comm/messages.h"
|
||||
|
||||
@@ -16,7 +16,7 @@ static uint32_t effect_time = 0;
|
||||
// counter of idle ticks since last indicator
|
||||
// used to allow or disallow heartbeat blink (to avoid interference)
|
||||
static uint32_t indicator_idle_ms = 0;
|
||||
#define IDLE_FOR_HEARTBEAT_MS 3000
|
||||
#define IDLE_FOR_HEARTBEAT_MS (3000-60)
|
||||
#define HB_MAX_SAFE_IVAL 500
|
||||
|
||||
static uint32_t hb_elapsed = 0;
|
||||
@@ -86,8 +86,7 @@ void Indicator_Tick(void)
|
||||
if (active_effect == STATUS_NONE) {
|
||||
indicator_idle_ms++;
|
||||
|
||||
if (hb_elapsed < HB_MAX_SAFE_IVAL && indicator_idle_ms > IDLE_FOR_HEARTBEAT_MS &&
|
||||
(indicator_idle_ms % 10 == 0)) {
|
||||
if (hb_elapsed < HB_MAX_SAFE_IVAL && indicator_idle_ms > IDLE_FOR_HEARTBEAT_MS && (indicator_idle_ms % 10 == 0)) {
|
||||
Indicator_Effect(STATUS_HEARTBEAT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#ifndef GEX_F072_TIMEBASE_H
|
||||
#define GEX_F072_TIMEBASE_H
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
/**
|
||||
* Precision timer: get microtime as uint64_t
|
||||
* This timestamp should be monotonously increasing with a precision of ±0.5µs
|
||||
|
||||
+8
-21
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "platform.h"
|
||||
#include <TinyFrame.h>
|
||||
#include "framework/unit.h"
|
||||
|
||||
/**
|
||||
* Queued job typedef
|
||||
@@ -26,27 +27,13 @@ typedef void (*ScheduledJobCb) (Job *job);
|
||||
struct sched_que_item {
|
||||
/** The callback */
|
||||
ScheduledJobCb cb;
|
||||
/** Data word 1 */
|
||||
union {
|
||||
TF_ID frame_id; // typically used to pass frame id to the callback
|
||||
void *data1; // arbitrary pointer or int
|
||||
uint32_t d32_1; // passing a number
|
||||
};
|
||||
/** Data word 2 */
|
||||
union {
|
||||
uint32_t d32; // passing a number
|
||||
uint32_t d32_2; // passing a number
|
||||
uint8_t *buf; // uchar buffer
|
||||
const uint8_t *cbuf; // const uchar buffer
|
||||
const char *str; // string
|
||||
void *data2; // arbitrary pointer
|
||||
};
|
||||
/** Data word 3 */
|
||||
union {
|
||||
uint32_t len; // typically length of the buffer
|
||||
void *data3; // arbitrary pointer
|
||||
uint32_t d32_3; // passing a number
|
||||
};
|
||||
|
||||
// Fields for data
|
||||
Unit *unit;
|
||||
uint64_t timestamp;
|
||||
uint32_t data1;
|
||||
uint32_t data2;
|
||||
uint32_t data3;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+17
-17
@@ -3,11 +3,7 @@
|
||||
//
|
||||
|
||||
#include "unit_base.h"
|
||||
#include "platform/irq_dispatcher.h"
|
||||
#include "comm/messages.h"
|
||||
#include "unit_din.h"
|
||||
#include "tasks/task_msg.h"
|
||||
#include "utils/avrlibc.h"
|
||||
|
||||
/** Private data structure */
|
||||
struct priv {
|
||||
@@ -168,18 +164,19 @@ static error_t DI_preInit(Unit *unit)
|
||||
*/
|
||||
static void ID_SendTriggerReportToMaster(Job *job)
|
||||
{
|
||||
Unit *unit = job->data1;
|
||||
|
||||
PayloadBuilder pb = pb_start(unit_tmp512, UNIT_TMP_LEN, NULL);
|
||||
pb_u8(&pb, unit->callsign);
|
||||
pb_u8(&pb, 0x00); // report type "Trigger"
|
||||
{
|
||||
pb_u16(&pb, (uint16_t) job->d32_2); // packed, 1 on the triggering pin
|
||||
pb_u16(&pb, (uint16_t) job->d32_3); // packed, snapshot
|
||||
// the snapshot can be used to capture the other input pins
|
||||
}
|
||||
pb_u16(&pb, (uint16_t) job->data1); // packed, 1 on the triggering pin
|
||||
pb_u16(&pb, (uint16_t) job->data2); // packed, snapshot
|
||||
assert_param(pb.ok);
|
||||
com_send_pb(MSG_UNIT_REPORT, &pb);
|
||||
|
||||
EventReport event = {
|
||||
.unit = job->unit,
|
||||
.timestamp = job->timestamp,
|
||||
.data = pb.start,
|
||||
.length = (uint16_t) pb_length(&pb),
|
||||
};
|
||||
|
||||
EventReport_Send(&event);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,6 +186,8 @@ static void ID_SendTriggerReportToMaster(Job *job)
|
||||
*/
|
||||
static void DI_handleExti(void *arg)
|
||||
{
|
||||
const uint64_t ts = PTIM_GetMicrotime();
|
||||
|
||||
Unit *unit = arg;
|
||||
struct priv *priv = unit->data;
|
||||
const uint16_t snapshot = (uint16_t) priv->port->IDR;
|
||||
@@ -215,9 +214,10 @@ static void DI_handleExti(void *arg)
|
||||
|
||||
if (trigger_map != 0) {
|
||||
Job j = {
|
||||
.data1 = unit,
|
||||
.d32_2 = pinmask_pack(trigger_map, priv->pins),
|
||||
.d32_3 = pinmask_pack(snapshot, priv->pins),
|
||||
.unit = unit,
|
||||
.timestamp = ts,
|
||||
.data1 = pinmask_pack(trigger_map, priv->pins),
|
||||
.data2 = pinmask_pack(snapshot, priv->pins),
|
||||
.cb = ID_SendTriggerReportToMaster
|
||||
};
|
||||
scheduleJob(&j);
|
||||
|
||||
+17
-18
@@ -2,31 +2,28 @@
|
||||
// Created by MightyPork on 2018/01/02.
|
||||
//
|
||||
|
||||
#include "platform.h"
|
||||
#include "comm/messages.h"
|
||||
#include "unit_base.h"
|
||||
#include "unit_usart.h"
|
||||
#include "tasks/task_msg.h"
|
||||
|
||||
#define UUSART_INTERNAL
|
||||
#include "_internal.h"
|
||||
|
||||
static void UUSART_SendReceivedDataToMaster(Job *job)
|
||||
{
|
||||
Unit *unit = job->data1;
|
||||
Unit *unit = job->unit;
|
||||
struct priv *priv = unit->data;
|
||||
|
||||
uint32_t readpos = job->d32;
|
||||
uint32_t count = job->len;
|
||||
uint16_t readpos = (uint16_t) job->data1;
|
||||
uint16_t count = (uint16_t) job->data2;
|
||||
|
||||
// TODO use TF's Multipart sending
|
||||
// TODO add API for building reports
|
||||
PayloadBuilder pb = pb_start(unit_tmp512, UNIT_TMP_LEN, NULL);
|
||||
pb_u8(&pb, unit->callsign);
|
||||
pb_u8(&pb, 0x00); // report type "Data received"
|
||||
pb_buf(&pb, (uint8_t *) (priv->rx_buffer + readpos), count);
|
||||
assert_param(pb.ok);
|
||||
com_send_pb(MSG_UNIT_REPORT, &pb);
|
||||
EventReport event = {
|
||||
.unit = job->unit,
|
||||
.timestamp = job->timestamp,
|
||||
.data = (uint8_t *) (priv->rx_buffer + readpos),
|
||||
.length = count,
|
||||
};
|
||||
|
||||
EventReport_Send(&event);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,6 +34,8 @@ static void UUSART_SendReceivedDataToMaster(Job *job)
|
||||
*/
|
||||
void UUSART_DMA_HandleRxFromIRQ(Unit *unit, uint16_t endpos)
|
||||
{
|
||||
const uint64_t ts = PTIM_GetMicrotime();
|
||||
|
||||
assert_param(unit);
|
||||
struct priv *priv = unit->data;
|
||||
assert_param(priv);
|
||||
@@ -47,11 +46,11 @@ void UUSART_DMA_HandleRxFromIRQ(Unit *unit, uint16_t endpos)
|
||||
uint16_t count = (endpos - readpos);
|
||||
|
||||
// We defer it to the job queue
|
||||
// FIXME this can starve the shared queue if full duplex is used, we need a second higher priority queue for those report jobs
|
||||
Job j = {
|
||||
.data1 = unit,
|
||||
.d32 = priv->rx_buf_readpos,
|
||||
.len = count,
|
||||
.unit = unit,
|
||||
.timestamp = ts,
|
||||
.data1 = priv->rx_buf_readpos,
|
||||
.data2 = count,
|
||||
.cb = UUSART_SendReceivedDataToMaster
|
||||
};
|
||||
scheduleJob(&j);
|
||||
|
||||
@@ -86,6 +86,36 @@ bool pb_u32(PayloadBuilder *pb, uint32_t word)
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Write uint64_t to the buffer. */
|
||||
bool pb_u64(PayloadBuilder *pb, uint64_t word)
|
||||
{
|
||||
pb_check_capacity(pb, 4);
|
||||
if (!pb->ok) return false;
|
||||
|
||||
if (pb->bigendian) {
|
||||
*pb->current++ = (uint8_t) ((word >> 56) & 0xFF);
|
||||
*pb->current++ = (uint8_t) ((word >> 48) & 0xFF);
|
||||
*pb->current++ = (uint8_t) ((word >> 40) & 0xFF);
|
||||
*pb->current++ = (uint8_t) ((word >> 32) & 0xFF);
|
||||
|
||||
*pb->current++ = (uint8_t) ((word >> 24) & 0xFF);
|
||||
*pb->current++ = (uint8_t) ((word >> 16) & 0xFF);
|
||||
*pb->current++ = (uint8_t) ((word >> 8) & 0xFF);
|
||||
*pb->current++ = (uint8_t) (word & 0xFF);
|
||||
} else {
|
||||
*pb->current++ = (uint8_t) (word & 0xFF);
|
||||
*pb->current++ = (uint8_t) ((word >> 8) & 0xFF);
|
||||
*pb->current++ = (uint8_t) ((word >> 16) & 0xFF);
|
||||
*pb->current++ = (uint8_t) ((word >> 24) & 0xFF);
|
||||
|
||||
*pb->current++ = (uint8_t) ((word >> 32) & 0xFF);
|
||||
*pb->current++ = (uint8_t) ((word >> 40) & 0xFF);
|
||||
*pb->current++ = (uint8_t) ((word >> 48) & 0xFF);
|
||||
*pb->current++ = (uint8_t) ((word >> 56) & 0xFF);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Write int8_t to the buffer. */
|
||||
bool pb_i8(PayloadBuilder *pb, int8_t byte)
|
||||
{
|
||||
@@ -104,8 +134,20 @@ bool pb_i32(PayloadBuilder *pb, int32_t word)
|
||||
return pb_u32(pb, ((union conv32){.i32 = word}).u32);
|
||||
}
|
||||
|
||||
/** Write int64_t to the buffer. */
|
||||
bool pb_i64(PayloadBuilder *pb, int64_t word)
|
||||
{
|
||||
return pb_u64(pb, ((union conv64){.i64 = word}).u64);
|
||||
}
|
||||
|
||||
/** Write 4-byte float to the buffer. */
|
||||
bool pb_float(PayloadBuilder *pb, float f)
|
||||
{
|
||||
return pb_u32(pb, ((union conv32){.f32 = f}).u32);
|
||||
}
|
||||
|
||||
/** Write 8-byte float to the buffer. */
|
||||
bool pb_double(PayloadBuilder *pb, double f)
|
||||
{
|
||||
return pb_u64(pb, ((union conv64){.f64 = f}).u64);
|
||||
}
|
||||
|
||||
@@ -98,6 +98,9 @@ bool pb_u16(PayloadBuilder *pb, uint16_t word);
|
||||
/** Write uint32_t to the buffer. */
|
||||
bool pb_u32(PayloadBuilder *pb, uint32_t word);
|
||||
|
||||
/** Write uint64_t to the buffer */
|
||||
bool pb_u64(PayloadBuilder *pb, uint64_t word);
|
||||
|
||||
/** Write int8_t to the buffer. */
|
||||
bool pb_i8(PayloadBuilder *pb, int8_t byte);
|
||||
|
||||
@@ -113,7 +116,13 @@ bool pb_i16(PayloadBuilder *pb, int16_t word);
|
||||
/** Write int32_t to the buffer. */
|
||||
bool pb_i32(PayloadBuilder *pb, int32_t word);
|
||||
|
||||
/** Write int64_t to the buffer. */
|
||||
bool pb_i64(PayloadBuilder *pb, int64_t word);
|
||||
|
||||
/** Write 4-byte float to the buffer. */
|
||||
bool pb_float(PayloadBuilder *pb, float f);
|
||||
|
||||
/** Write 8-byte float to the buffer. */
|
||||
bool pb_double(PayloadBuilder *pb, double f);
|
||||
|
||||
#endif // PAYLOAD_BUILDER_H
|
||||
|
||||
@@ -56,6 +56,37 @@ uint32_t pp_u32(PayloadParser *pp)
|
||||
return x;
|
||||
}
|
||||
|
||||
uint64_t pp_u64(PayloadParser *pp)
|
||||
{
|
||||
pp_check_capacity(pp, 4);
|
||||
if (!pp->ok) return 0;
|
||||
|
||||
uint64_t x = 0;
|
||||
|
||||
if (pp->bigendian) {
|
||||
x |= (uint64_t) ((uint64_t) *pp->current++ << 56);
|
||||
x |= (uint64_t) ((uint64_t) *pp->current++ << 48);
|
||||
x |= (uint64_t) ((uint64_t) *pp->current++ << 40);
|
||||
x |= (uint64_t) ((uint64_t) *pp->current++ << 32);
|
||||
|
||||
x |= (uint64_t) (*pp->current++ << 24);
|
||||
x |= (uint64_t) (*pp->current++ << 16);
|
||||
x |= (uint64_t) (*pp->current++ << 8);
|
||||
x |= *pp->current++;
|
||||
} else {
|
||||
x |= *pp->current++;
|
||||
x |= (uint64_t) (*pp->current++ << 8);
|
||||
x |= (uint64_t) (*pp->current++ << 16);
|
||||
x |= (uint64_t) (*pp->current++ << 24);
|
||||
|
||||
x |= (uint64_t) ((uint64_t) *pp->current++ << 32);
|
||||
x |= (uint64_t) ((uint64_t) *pp->current++ << 40);
|
||||
x |= (uint64_t) ((uint64_t) *pp->current++ << 48);
|
||||
x |= (uint64_t) ((uint64_t) *pp->current++ << 56);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
const uint8_t *pp_tail(PayloadParser *pp, uint32_t *length)
|
||||
{
|
||||
int32_t len = (int) (pp->end - pp->current);
|
||||
@@ -89,12 +120,24 @@ int32_t pp_i32(PayloadParser *pp)
|
||||
return ((union conv32) {.u32 = pp_u32(pp)}).i32;
|
||||
}
|
||||
|
||||
/** Read int364_t from the payload. */
|
||||
int64_t pp_i64(PayloadParser *pp)
|
||||
{
|
||||
return ((union conv64) {.u64 = pp_u64(pp)}).i64;
|
||||
}
|
||||
|
||||
/** Read 4-byte float from the payload. */
|
||||
float pp_float(PayloadParser *pp)
|
||||
{
|
||||
return ((union conv32) {.u32 = pp_u32(pp)}).f32;
|
||||
}
|
||||
|
||||
/** Read 8-byte float from the payload. */
|
||||
double pp_double(PayloadParser *pp)
|
||||
{
|
||||
return ((union conv64) {.u64 = pp_u64(pp)}).f64;
|
||||
}
|
||||
|
||||
/** Read a zstring */
|
||||
uint32_t pp_string(PayloadParser *pp, char *buffer, uint32_t maxlen)
|
||||
{
|
||||
|
||||
@@ -101,6 +101,9 @@ uint16_t pp_u16(PayloadParser *pp);
|
||||
/** Read uint32_t from the payload. */
|
||||
uint32_t pp_u32(PayloadParser *pp);
|
||||
|
||||
/** Read uint64_t from the payload. */
|
||||
uint64_t pp_u64(PayloadParser *pp);
|
||||
|
||||
/** Read int8_t from the payload. */
|
||||
int8_t pp_i8(PayloadParser *pp);
|
||||
|
||||
@@ -116,9 +119,15 @@ int16_t pp_i16(PayloadParser *pp);
|
||||
/** Read int32_t from the payload. */
|
||||
int32_t pp_i32(PayloadParser *pp);
|
||||
|
||||
/** Read int64_t from the payload. */
|
||||
int64_t pp_i64(PayloadParser *pp);
|
||||
|
||||
/** Read 4-byte float from the payload. */
|
||||
float pp_float(PayloadParser *pp);
|
||||
|
||||
/** Read 8-byte float from the payload. */
|
||||
double pp_double(PayloadParser *pp);
|
||||
|
||||
/**
|
||||
* Parse a zero-terminated string
|
||||
*
|
||||
|
||||
@@ -29,4 +29,10 @@ union conv32 {
|
||||
float f32;
|
||||
};
|
||||
|
||||
union conv64 {
|
||||
uint64_t u64;
|
||||
int64_t i64;
|
||||
double f64;
|
||||
};
|
||||
|
||||
#endif // TYPE_COERCE_H
|
||||
|
||||
Reference in New Issue
Block a user