blob: 23ddc9536bafe5a6be056e312a000c8f7902022d [file] [log] [blame]
#ifndef __AOS_MESSAGES_QUEUE_HOLDER_H_
#define __AOS_MESSAGES_QUEUE_HOLDER_H_
#include <stddef.h>
#include <string.h>
#include <algorithm>
#include "aos/common/control_loop/Timing.h"
#include "aos/common/byteorder.h"
#include "aos/common/time.h"
#include "aos/common/type_traits.h"
#include "aos/common/logging/logging.h"
#ifndef __VXWORKS__
#include "aos/atom_code/ipc_lib/queue.h"
#endif
namespace aos {
// Specializations of TypeOperator and QueueBuilder that are actually
// implemented are created in the generated code for all message value types.
// These specializations have the same functions declared in the actual types.
// Defines a way for types to be manipulated.
template<typename T> class TypeOperator {
public:
// Sets all fields to their default constructor.
static void Zero(T &t_);
// Returns the size of buffer NToH and HToN use.
static size_t Size();
// Converts everything from network to host byte order.
// input must have Size() bytes available in it.
static void NToH(char *input, T &t_);
// Converts everything from host to network byte order and puts it into output.
// output must have Size() bytes available in it.
static void HToN(const T &t_, char *output);
// Creates a string with the names and values of all the fields.
// The return value might will be to a static buffer.
static const char *Print(const T &t_);
};
template<typename T> class QueueHolder;
// An easy way to set values for queue messages.
// Each specialization has chainable setter functions for building a message
// of type T to put into a queue (like QueueBuilder<T> &field(int value);).
template<class T> class QueueBuilder {
public:
QueueBuilder(QueueHolder<T> &holder);
bool Send();
};
// Internal class to make implementing identical behavior with structs that go
// into queues easier. Also a central location for the documentation.
//
// When compiled for the cRIO, everything except Clear does nothing (and Get
// turns into just a Clear) which means that the internal T instance is the only one.
// Also, the internal instance becomes static.
//
// To look at the current message, you Get a copy and then View the result.
// To make changes, you modify the message that you can View (after possibly
// Clearing it) and then you Send it. You can also just Clear the message, put
// data into it, and then Send it. A way to modify the local message is using
// the Builder function.
// Note that there is no way to avoid potentially overwriting other changes between
// when you Get one and when you Send it (mainly applicable to 1-length queues).
//
// T must be POD and have a "timespec set_time;" field.
//
// This first class doesn't have the builder; QueueHolder does.
#define aos_check_rv __attribute__((warn_unused_result))
template<typename T> class QueueHolderNoBuilder {
#ifndef __VXWORKS__
aos_queue *const queue_;
static_assert(shm_ok<T>::value, "T must be able to"
" go through shared memory and memcpy");
T t_;
#else
static T t_;
#endif
public:
#ifndef __VXWORKS__
explicit QueueHolderNoBuilder(aos_queue *queue) : queue_(queue) {}
#else
QueueHolderNoBuilder() {}
#endif
// Gets the current value and stores it in View().
// check_time is whether or not to check to see if the last time a value was Sent
// was too long ago (returns false if it was)
// Returns true if View() is now the current value.
// IMPORTANT: If this function returns false, the contents of View() are
// either the same as they were before or (if check_time is true) the current
// message. That is why it creates compile-time warnings if the return value
// is not checked.
bool Get(bool check_time) aos_check_rv;
// Looks at the current value. Starts out undefined.
// If the last Get call returned false, then this the contents of the
// return value are undefined.
#ifdef __VXWORKS__
static
#endif
inline T &View() { return t_; }
// Clears (calls the default constructor of) all the fields of the current
// Goal.
void Clear() { TypeOperator<T>::Zero(t_); }
// Sends the current value. Does not affect the current value.
// Returns whether or not the Send succeeded.
bool Send();
// Returns a string containing the values of all the fields in the current
// value.
// The return value is valid until Print is called again. The class owns the
// memory.
const char *Print() const { return TypeOperator<T>::Print(t_); }
};
template<typename T>
bool QueueHolderNoBuilder<T>::Get(bool check_time) {
#ifdef __VXWORKS__
(void)check_time;
return true;
#else
const T *msg = static_cast<const T *>(aos_queue_read_msg(queue_,
PEEK | NON_BLOCK));
if (msg == NULL) {
return false;
}
static_assert(sizeof(t_) == sizeof(*msg), "something is wrong here");
memcpy(&t_, msg, sizeof(t_));
aos_queue_free_msg(queue_, msg);
if (check_time && !((time::Time::Now() - time::Time(t_.set_time)) > time::Time::InMS(45))) {
LOG(WARNING, "too long since last Set msg={%s}\n", Print());
return false;
} else {
return true;
}
#endif
}
template<typename T>
bool QueueHolderNoBuilder<T>::Send() {
#ifndef __VXWORKS__
T *msg = static_cast<T *>(aos_queue_get_msg(queue_));
if (msg == NULL) {
return false;
}
static_assert(sizeof(*msg) == sizeof(t_), "something is wrong here");
memcpy(msg, &t_, sizeof(t_));
msg->set_time = ::aos::time::Time::Now().ToTimespec();
return aos_queue_write_msg_free(queue_, msg, OVERRIDE) == 0;
#else
return true;
#endif
}
#ifdef __VXWORKS__
template<typename T> T QueueHolderNoBuilder<T>::t_;
#endif
template<typename T> class QueueHolder : public QueueHolderNoBuilder<T> {
QueueBuilder<T> builder_;
public:
#ifndef __VXWORKS__
explicit QueueHolder(aos_queue *queue) : QueueHolderNoBuilder<T>(queue),
builder_(*this) {}
#else
QueueHolder() : builder_(*this) {}
#endif
// Clears the current Goal and returns an object that allows setting various
// fields with chained method calls and then calling Send() on it.
QueueBuilder<T> &Builder() {
QueueHolderNoBuilder<T>::Clear();
return builder_;
}
};
} // namespace aos
#endif