implemented precision timestamps in report messages

sipo
Ondřej Hruška 6 years ago
parent 94e87c74d3
commit 04e32860f7
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 53
      comm/event_reports.c
  2. 32
      comm/event_reports.h
  3. 1
      comm/messages.h
  4. 5
      framework/unit_base.h
  5. 5
      platform/status_led.c
  6. 2
      platform/timebase.h
  7. 29
      tasks/sched_queue.h
  8. 34
      units/digital_in/unit_din.c
  9. 37
      units/usart/unit_usart.c
  10. 42
      utils/payload_builder.c
  11. 9
      utils/payload_builder.h
  12. 43
      utils/payload_parser.c
  13. 9
      utils/payload_parser.h
  14. 6
      utils/type_coerce.h

@ -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

@ -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;
};
/**

@ -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);

@ -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;
// 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);
uint16_t readpos = (uint16_t) job->data1;
uint16_t count = (uint16_t) job->data2;
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

Loading…
Cancel
Save