2020-05-07 09:36:20 +02:00
|
|
|
/*
|
|
|
|
* Copyright(c) 2020 ADLINK Technology Limited and others
|
|
|
|
*
|
|
|
|
* This program and the accompanying materials are made available under the
|
|
|
|
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
|
|
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
|
|
|
* v. 1.0 which is available at
|
|
|
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
|
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <setjmp.h>
|
|
|
|
|
|
|
|
#include "dds/dds.h"
|
|
|
|
#include "dds/ddsrt/misc.h"
|
|
|
|
#include "dds/ddsrt/sync.h"
|
|
|
|
#include "dds/ddsrt/heap.h"
|
|
|
|
#include "dds/ddsrt/strtod.h"
|
|
|
|
#include "dds/ddsrt/string.h"
|
|
|
|
#include "dds/ddsrt/environ.h"
|
|
|
|
|
|
|
|
#include "dds__types.h"
|
|
|
|
#include "dds__entity.h"
|
2020-05-12 09:22:56 +02:00
|
|
|
#include "dds__writer.h"
|
|
|
|
#include "dds/ddsi/q_bswap.h"
|
2020-05-07 09:36:20 +02:00
|
|
|
#include "dds/ddsi/q_lease.h"
|
|
|
|
#include "dds/ddsi/q_xevent.h"
|
|
|
|
#include "dds/ddsi/ddsi_entity_index.h"
|
|
|
|
|
|
|
|
#include "test_common.h"
|
|
|
|
#include "test_oneliner.h"
|
|
|
|
|
|
|
|
#define MAXDOMS (sizeof (((struct oneliner_ctx){.result=0}).doms) / sizeof (((struct oneliner_ctx){.result=0}).doms[0]))
|
|
|
|
|
|
|
|
static const char knownentities[] = "PRWrstwxy";
|
|
|
|
typedef struct { char n[MAXDOMS + 1]; } entname_t;
|
|
|
|
|
|
|
|
#define DEFINE_STATUS_CALLBACK(name, NAME, kind) \
|
|
|
|
static void name##_cb (dds_entity_t kind, const dds_##name##_status_t status, void *arg) \
|
|
|
|
{ \
|
|
|
|
struct oneliner_cb *cb = arg; \
|
|
|
|
ddsrt_mutex_lock (&cb->ctx->g_mutex); \
|
|
|
|
cb->cb_##kind = kind; \
|
|
|
|
cb->cb_##name##_status = status; \
|
|
|
|
cb->cb_called[DDS_##NAME##_STATUS_ID]++; \
|
|
|
|
ddsrt_cond_broadcast (&cb->ctx->g_cond); \
|
|
|
|
ddsrt_mutex_unlock (&cb->ctx->g_mutex); \
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_STATUS_CALLBACK (inconsistent_topic, INCONSISTENT_TOPIC, topic)
|
|
|
|
DEFINE_STATUS_CALLBACK (liveliness_changed, LIVELINESS_CHANGED, reader)
|
|
|
|
DEFINE_STATUS_CALLBACK (liveliness_lost, LIVELINESS_LOST, writer)
|
|
|
|
DEFINE_STATUS_CALLBACK (offered_deadline_missed, OFFERED_DEADLINE_MISSED, writer)
|
|
|
|
DEFINE_STATUS_CALLBACK (offered_incompatible_qos, OFFERED_INCOMPATIBLE_QOS, writer)
|
|
|
|
DEFINE_STATUS_CALLBACK (publication_matched, PUBLICATION_MATCHED, writer)
|
|
|
|
DEFINE_STATUS_CALLBACK (requested_deadline_missed, REQUESTED_DEADLINE_MISSED, reader)
|
|
|
|
DEFINE_STATUS_CALLBACK (requested_incompatible_qos, REQUESTED_INCOMPATIBLE_QOS, reader)
|
|
|
|
DEFINE_STATUS_CALLBACK (sample_lost, SAMPLE_LOST, reader)
|
|
|
|
DEFINE_STATUS_CALLBACK (sample_rejected, SAMPLE_REJECTED, reader)
|
|
|
|
DEFINE_STATUS_CALLBACK (subscription_matched, SUBSCRIPTION_MATCHED, reader)
|
|
|
|
|
|
|
|
static void data_on_readers_cb (dds_entity_t subscriber, void *arg)
|
|
|
|
{
|
|
|
|
struct oneliner_cb *cb = arg;
|
|
|
|
ddsrt_mutex_lock (&cb->ctx->g_mutex);
|
|
|
|
cb->cb_subscriber = subscriber;
|
|
|
|
cb->cb_called[DDS_DATA_ON_READERS_STATUS_ID]++;
|
|
|
|
ddsrt_cond_broadcast (&cb->ctx->g_cond);
|
|
|
|
ddsrt_mutex_unlock (&cb->ctx->g_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void data_available_cb (dds_entity_t reader, void *arg)
|
|
|
|
{
|
|
|
|
struct oneliner_cb *cb = arg;
|
|
|
|
ddsrt_mutex_lock (&cb->ctx->g_mutex);
|
|
|
|
cb->cb_reader = reader;
|
|
|
|
cb->cb_called[DDS_DATA_AVAILABLE_STATUS_ID]++;
|
|
|
|
ddsrt_cond_broadcast (&cb->ctx->g_cond);
|
|
|
|
ddsrt_mutex_unlock (&cb->ctx->g_mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dummy_data_on_readers_cb (dds_entity_t subscriber, void *arg)
|
|
|
|
{
|
|
|
|
(void)subscriber;
|
|
|
|
(void)arg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dummy_data_available_cb (dds_entity_t reader, void *arg)
|
|
|
|
{
|
|
|
|
(void)reader;
|
|
|
|
(void)arg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dummy_subscription_matched_cb (dds_entity_t reader, const dds_subscription_matched_status_t status, void *arg)
|
|
|
|
{
|
|
|
|
(void)reader;
|
|
|
|
(void)status;
|
|
|
|
(void)arg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dummy_liveliness_changed_cb (dds_entity_t reader, const dds_liveliness_changed_status_t status, void *arg)
|
|
|
|
{
|
|
|
|
(void)reader;
|
|
|
|
(void)status;
|
|
|
|
(void)arg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dummy_cb (void)
|
|
|
|
{
|
|
|
|
// Used as a listener function in checking merging of listeners,
|
|
|
|
// and for that purpose, casting it to whatever function type is
|
|
|
|
// required is ok. It is not supposed to ever be called.
|
|
|
|
abort ();
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef DEFINE_STATUS_CALLBACK
|
|
|
|
|
|
|
|
// These had better match the corresponding type definitions!
|
|
|
|
// n uint32_t ...count
|
|
|
|
// c int32_t ...count_change
|
|
|
|
// I instance handle of a data instance
|
|
|
|
// P uint32_t QoS policy ID
|
|
|
|
// E instance handle of an entity
|
|
|
|
// R sample_rejected_status_kind
|
|
|
|
static const struct {
|
|
|
|
const char *name;
|
|
|
|
size_t size; // size of status struct
|
|
|
|
const char *desc; // description of status struct
|
|
|
|
dds_status_id_t id; // status id, entry in "cb_called"
|
|
|
|
size_t cb_entity_off; // which cb_... entity to look at
|
|
|
|
size_t cb_status_off; // cb_..._status to look at
|
|
|
|
} lldesc[] = {
|
|
|
|
#define S0(abbrev, NAME, entity) \
|
|
|
|
{ abbrev, 0, NULL, DDS_##NAME##_STATUS_ID, offsetof (struct oneliner_cb, cb_##entity), 0 }
|
|
|
|
#define S(abbrev, name, NAME, desc, entity) \
|
|
|
|
{ abbrev, sizeof (dds_##name##_status_t), desc, DDS_##NAME##_STATUS_ID, offsetof (struct oneliner_cb, cb_##entity), offsetof (struct oneliner_cb, cb_##name##_status) }
|
|
|
|
S0 ("da", DATA_AVAILABLE, reader),
|
|
|
|
S0 ("dor", DATA_ON_READERS, subscriber),
|
|
|
|
S ("it", inconsistent_topic, INCONSISTENT_TOPIC, "nc", topic),
|
|
|
|
S ("lc", liveliness_changed, LIVELINESS_CHANGED, "nnccE", reader),
|
|
|
|
S ("ll", liveliness_lost, LIVELINESS_LOST, "nc", writer),
|
|
|
|
S ("odm", offered_deadline_missed, OFFERED_DEADLINE_MISSED, "ncI", writer),
|
|
|
|
S ("oiq", offered_incompatible_qos, OFFERED_INCOMPATIBLE_QOS, "ncP", writer),
|
|
|
|
S ("pm", publication_matched, PUBLICATION_MATCHED, "ncncE", writer),
|
|
|
|
S ("rdm", requested_deadline_missed, REQUESTED_DEADLINE_MISSED, "ncI", reader),
|
|
|
|
S ("riq", requested_incompatible_qos, REQUESTED_INCOMPATIBLE_QOS, "ncP", reader),
|
|
|
|
S ("sl", sample_lost, SAMPLE_LOST, "nc", reader),
|
|
|
|
S ("sr", sample_rejected, SAMPLE_REJECTED, "ncRI", reader),
|
|
|
|
S ("sm", subscription_matched, SUBSCRIPTION_MATCHED, "ncncE", reader)
|
|
|
|
#undef S
|
|
|
|
#undef S0
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static const void *advance (const void *status, size_t *off, char code)
|
|
|
|
{
|
|
|
|
#define alignof(type_) offsetof (struct { char c; type_ d; }, d)
|
|
|
|
size_t align = 1, size = 1;
|
|
|
|
switch (code)
|
|
|
|
{
|
|
|
|
case 'n': case 'c': case 'P':
|
|
|
|
align = alignof (uint32_t); size = sizeof (uint32_t);
|
|
|
|
break;
|
|
|
|
case 'E': case 'I':
|
|
|
|
align = alignof (dds_instance_handle_t); size = sizeof (dds_instance_handle_t);
|
|
|
|
break;
|
|
|
|
case 'R':
|
|
|
|
align = alignof (dds_sample_rejected_status_kind); size = sizeof (dds_sample_rejected_status_kind);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
abort ();
|
|
|
|
}
|
|
|
|
#undef alignof
|
|
|
|
*off = (*off + align - 1) & ~(align - 1);
|
|
|
|
const void *p = (const char *) status + *off;
|
|
|
|
*off += size;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
static dds_return_t get_status (int ll, dds_entity_t ent, void *status)
|
|
|
|
{
|
|
|
|
dds_return_t ret;
|
|
|
|
switch (ll)
|
|
|
|
{
|
|
|
|
case 2: ret = dds_get_inconsistent_topic_status (ent, status); break;
|
|
|
|
case 3: ret = dds_get_liveliness_changed_status (ent, status); break;
|
|
|
|
case 4: ret = dds_get_liveliness_lost_status (ent, status); break;
|
|
|
|
case 5: ret = dds_get_offered_deadline_missed_status (ent, status); break;
|
|
|
|
case 6: ret = dds_get_offered_incompatible_qos_status (ent, status); break;
|
|
|
|
case 7: ret = dds_get_publication_matched_status (ent, status); break;
|
|
|
|
case 8: ret = dds_get_requested_deadline_missed_status (ent, status); break;
|
|
|
|
case 9: ret = dds_get_requested_incompatible_qos_status (ent, status); break;
|
|
|
|
case 10: ret = dds_get_sample_lost_status (ent, status); break;
|
|
|
|
case 11: ret = dds_get_sample_rejected_status (ent, status); break;
|
|
|
|
case 12: ret = dds_get_subscription_matched_status (ent, status); break;
|
|
|
|
default: return -1;
|
|
|
|
}
|
|
|
|
return (ret == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static dds_return_t check_status_change_fields_are_0 (int ll, dds_entity_t ent)
|
|
|
|
{
|
|
|
|
if (lldesc[ll].desc)
|
|
|
|
{
|
|
|
|
const char *d = lldesc[ll].desc;
|
|
|
|
void *status = malloc (lldesc[ll].size);
|
|
|
|
dds_return_t ret;
|
|
|
|
if ((ret = get_status (ll, ent, status)) <= 0)
|
|
|
|
{
|
|
|
|
free (status);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
size_t off = 0;
|
|
|
|
while (*d)
|
|
|
|
{
|
|
|
|
const uint32_t *p = advance (status, &off, *d);
|
|
|
|
if (*d == 'c' && *p != 0)
|
|
|
|
{
|
|
|
|
free (status);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
d++;
|
|
|
|
}
|
|
|
|
assert (off <= lldesc[ll].size);
|
|
|
|
free (status);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define TOK_END -1
|
|
|
|
#define TOK_NAME -2
|
|
|
|
#define TOK_INT -3
|
|
|
|
#define TOK_DURATION -4
|
|
|
|
#define TOK_TIMESTAMP -5
|
|
|
|
#define TOK_ELLIPSIS -6
|
|
|
|
#define TOK_INVALID -7
|
|
|
|
|
|
|
|
static int setresult (struct oneliner_ctx *ctx, int result, const char *msg, ...) ddsrt_attribute_format((printf, 3, 4));
|
|
|
|
static void error (struct oneliner_ctx *ctx, const char *msg, ...) ddsrt_attribute_format((printf, 2, 3));
|
|
|
|
static void error_dds (struct oneliner_ctx *ctx, dds_return_t ret, const char *msg, ...) ddsrt_attribute_format((printf, 3, 4));
|
|
|
|
static void testfail (struct oneliner_ctx *ctx, const char *msg, ...) ddsrt_attribute_format((printf, 2, 3));
|
|
|
|
|
|
|
|
static void vsetresult (struct oneliner_ctx *ctx, int result, const char *msg, va_list ap)
|
|
|
|
{
|
|
|
|
assert (result <= 0);
|
|
|
|
ctx->result = result;
|
|
|
|
vsnprintf (ctx->msg, sizeof (ctx->msg), msg, ap);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int setresult (struct oneliner_ctx *ctx, int result, const char *msg, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start (ap, msg);
|
|
|
|
vsetresult (ctx, result, msg, ap);
|
|
|
|
va_end (ap);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void error (struct oneliner_ctx *ctx, const char *msg, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start (ap, msg);
|
|
|
|
vsetresult (ctx, -1, msg, ap);
|
|
|
|
va_end (ap);
|
|
|
|
longjmp (ctx->jb, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void error_dds (struct oneliner_ctx *ctx, dds_return_t ret, const char *msg, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start (ap, msg);
|
|
|
|
vsetresult (ctx, -1, msg, ap);
|
|
|
|
va_end (ap);
|
|
|
|
size_t n = strlen (ctx->msg);
|
|
|
|
if (n < sizeof (ctx->msg))
|
|
|
|
snprintf (ctx->msg + n, sizeof (ctx->msg) - n, " (%s)", dds_strretcode (ret));
|
|
|
|
longjmp (ctx->jb, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void testfail (struct oneliner_ctx *ctx, const char *msg, ...)
|
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
va_start (ap, msg);
|
|
|
|
vsetresult (ctx, 0, msg, ap);
|
|
|
|
va_end (ap);
|
|
|
|
longjmp (ctx->jb, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void advancetok (struct oneliner_lex *l)
|
|
|
|
{
|
|
|
|
while (isspace ((unsigned char) *l->inp))
|
|
|
|
l->inp++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int issymchar0 (char c)
|
|
|
|
{
|
|
|
|
return isalpha ((unsigned char) c) || c == '_';
|
|
|
|
}
|
|
|
|
|
|
|
|
static int issymchar (char c)
|
|
|
|
{
|
|
|
|
return isalnum ((unsigned char) c) || c == '_' || c == '\'';
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool lookingatnum (const struct oneliner_lex *l)
|
|
|
|
{
|
|
|
|
return (isdigit ((unsigned char) l->inp[(l->inp[0] == '-')]));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nexttok_dur (struct oneliner_lex *l, union oneliner_tokval *v, bool expecting_duration)
|
|
|
|
{
|
|
|
|
advancetok (l);
|
|
|
|
if (l->inp[0] == 0)
|
|
|
|
{
|
|
|
|
l->tok = TOK_END;
|
|
|
|
}
|
|
|
|
else if (strncmp (l->inp, "...", 3) == 0)
|
|
|
|
{
|
|
|
|
l->inp += 3;
|
|
|
|
l->tok = TOK_ELLIPSIS;
|
|
|
|
}
|
|
|
|
else if (!expecting_duration && lookingatnum (l))
|
|
|
|
{
|
|
|
|
char *endp;
|
|
|
|
// strtol: [0-9]+ ; endp = l->inp if no digits present
|
|
|
|
l->v.i = (int) strtol (l->inp, &endp, 10);
|
|
|
|
l->inp = endp;
|
|
|
|
if (v) *v = l->v;
|
|
|
|
l->tok = TOK_INT;
|
|
|
|
}
|
|
|
|
else if (l->inp[0] == '@' || (expecting_duration && lookingatnum (l)))
|
|
|
|
{
|
|
|
|
const int ists = (l->inp[0] == '@');
|
|
|
|
char *endp;
|
|
|
|
if (!ists && strncmp (l->inp + ists, "inf", 3) == 0 && !issymchar (l->inp[ists + 3]))
|
|
|
|
{
|
|
|
|
l->inp += ists + 3;
|
|
|
|
l->v.d = DDS_INFINITY;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
double d;
|
|
|
|
if (ddsrt_strtod (l->inp + ists, &endp, &d) != DDS_RETCODE_OK)
|
|
|
|
return false;
|
|
|
|
if (!ists && d < 0)
|
|
|
|
return false;
|
|
|
|
if (d >= (double) (INT64_MAX / DDS_NSECS_IN_SEC))
|
|
|
|
l->v.d = DDS_INFINITY;
|
|
|
|
else if (d >= 0)
|
|
|
|
l->v.d = (int64_t) (d * 1e9 + 0.5);
|
|
|
|
else
|
|
|
|
l->v.d = -(int64_t) (-d * 1e9 + 0.5);
|
|
|
|
if (ists)
|
|
|
|
l->v.d += l->tref;
|
|
|
|
l->inp = endp;
|
|
|
|
}
|
|
|
|
if (v) *v = l->v;
|
|
|
|
l->tok = ists ? TOK_TIMESTAMP : TOK_DURATION;
|
|
|
|
}
|
|
|
|
else if (issymchar0 (l->inp[0]))
|
|
|
|
{
|
|
|
|
int p = 0;
|
|
|
|
while (issymchar (l->inp[p]))
|
|
|
|
{
|
|
|
|
if (p == (int) sizeof (l->v.n))
|
|
|
|
return TOK_INVALID;
|
|
|
|
l->v.n[p] = l->inp[p];
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
l->v.n[p] = 0;
|
|
|
|
l->inp += p;
|
|
|
|
if (v) *v = l->v;
|
|
|
|
l->tok = TOK_NAME;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
l->tok = *l->inp++;
|
|
|
|
}
|
|
|
|
return l->tok;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int nexttok (struct oneliner_lex *l, union oneliner_tokval *v)
|
|
|
|
{
|
|
|
|
return nexttok_dur (l, v, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int peektok (const struct oneliner_lex *l, union oneliner_tokval *v)
|
|
|
|
{
|
|
|
|
struct oneliner_lex l1 = *l;
|
|
|
|
return nexttok (&l1, v);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool nexttok_if (struct oneliner_lex *l, int tok)
|
|
|
|
{
|
|
|
|
if (peektok (l, NULL) != tok)
|
|
|
|
return false;
|
|
|
|
nexttok (l, NULL);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool nexttok_int (struct oneliner_lex *l, int *dst)
|
|
|
|
{
|
|
|
|
if (peektok (l, NULL) != TOK_INT)
|
|
|
|
return false;
|
|
|
|
(void) nexttok (l, NULL);
|
|
|
|
*dst = l->v.i;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct kvarg {
|
|
|
|
const char *k;
|
|
|
|
size_t klen;
|
|
|
|
int v;
|
|
|
|
bool (*arg) (struct oneliner_lex *l, void *dst); // *inp unchanged when false
|
|
|
|
void (*def) (void *dst);
|
|
|
|
};
|
|
|
|
|
|
|
|
static void def_kvarg_int0 (void *dst) { *(int *)dst = 0; }
|
|
|
|
static void def_kvarg_int1 (void *dst) { *(int *)dst = 1; }
|
|
|
|
static void def_kvarg_dur_inf (void *dst) { *(dds_duration_t *)dst = DDS_INFINITY; }
|
|
|
|
static void def_kvarg_dur_100ms (void *dst) { *(dds_duration_t *)dst = DDS_MSECS (100); }
|
|
|
|
|
|
|
|
static bool read_kvarg_int (struct oneliner_lex *l, void *dst)
|
|
|
|
{
|
|
|
|
return nexttok_int (l, dst);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool read_kvarg_posint (struct oneliner_lex *l, void *dst)
|
|
|
|
{
|
|
|
|
return nexttok_int (l, dst) && l->v.i > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool read_kvarg_dur (struct oneliner_lex *l, void *dst)
|
|
|
|
{
|
|
|
|
dds_duration_t *x = dst;
|
|
|
|
struct oneliner_lex l1 = *l;
|
|
|
|
if (nexttok_dur (&l1, NULL, true) != TOK_DURATION)
|
|
|
|
return false;
|
|
|
|
*x = l1.v.d;
|
|
|
|
*l = l1;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool read_kvarg_3len (struct oneliner_lex *l, void *dst)
|
|
|
|
{
|
|
|
|
struct oneliner_lex l1 = *l;
|
|
|
|
int *x = dst, i = 0;
|
|
|
|
x[0] = x[1] = x[2] = DDS_LENGTH_UNLIMITED;
|
|
|
|
do {
|
|
|
|
if (!nexttok_int (&l1, &x[i]) || (x[i] <= 0 && x[i] != DDS_LENGTH_UNLIMITED))
|
|
|
|
return false;
|
|
|
|
} while (++i < 3 && nexttok_if (&l1, '/'));
|
|
|
|
*l = l1;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool read_kvarg (const struct kvarg *ks, size_t sizeof_ks, struct oneliner_lex *l, int *v, void *arg)
|
|
|
|
{
|
|
|
|
// l points at name, *inp is , or ) terminated; *l unchanged when false
|
|
|
|
const struct kvarg *kend = ks + sizeof_ks / sizeof (*ks);
|
|
|
|
struct oneliner_lex l1 = *l;
|
|
|
|
advancetok (&l1);
|
|
|
|
for (const struct kvarg *k = ks; k < kend; k++)
|
|
|
|
{
|
|
|
|
assert (strlen (k->k) == k->klen);
|
|
|
|
*v = k->v;
|
|
|
|
if (k->klen == 0)
|
|
|
|
{
|
|
|
|
assert (k->arg != 0 && k->def == 0);
|
|
|
|
struct oneliner_lex l2 = l1;
|
|
|
|
if (k->arg (&l2, arg) && (peektok (&l2, NULL) == ',' || peektok (&l2, NULL) == ')'))
|
|
|
|
{
|
|
|
|
*l = l2;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strncmp (l1.inp, k->k, k->klen) != 0)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* skip symbol */
|
|
|
|
struct oneliner_lex l2 = l1;
|
|
|
|
l2.inp += k->klen;
|
|
|
|
if (peektok (&l2, NULL) == ',' || peektok (&l2, NULL) == ')')
|
|
|
|
{
|
|
|
|
if (k->arg == 0 || k->def != 0)
|
|
|
|
{
|
|
|
|
if (k->def) k->def (arg);
|
|
|
|
*l = l2;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (k->arg != 0 && nexttok (&l2, NULL) == ':')
|
|
|
|
{
|
|
|
|
if (k->arg (&l2, arg) && (peektok (&l2, NULL) == ',' || peektok (&l2, NULL) == ')'))
|
|
|
|
{
|
|
|
|
*l = l2;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool qos_durability (struct oneliner_lex *l, dds_qos_t *q)
|
|
|
|
{
|
|
|
|
static const struct kvarg ks[] = {
|
|
|
|
{ "v", 1, (int) DDS_DURABILITY_VOLATILE },
|
|
|
|
{ "tl", 2, (int) DDS_DURABILITY_TRANSIENT_LOCAL },
|
|
|
|
{ "t", 1, (int) DDS_DURABILITY_TRANSIENT },
|
|
|
|
{ "p", 1, (int) DDS_DURABILITY_PERSISTENT }
|
|
|
|
};
|
|
|
|
int v;
|
|
|
|
if (!read_kvarg (ks, sizeof ks, l, &v, NULL))
|
|
|
|
return false;
|
|
|
|
dds_qset_durability (q, (dds_durability_kind_t) v);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct kvarg ks_history[] = {
|
|
|
|
{ "all", 3, (int) DDS_HISTORY_KEEP_ALL, .def = def_kvarg_int1 },
|
|
|
|
{ "", 0, (int) DDS_HISTORY_KEEP_LAST, .arg = read_kvarg_posint }
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool qos_history (struct oneliner_lex *l, dds_qos_t *q)
|
|
|
|
{
|
|
|
|
int v, x = 1;
|
|
|
|
if (!read_kvarg (ks_history, sizeof ks_history, l, &v, &x))
|
|
|
|
return false;
|
|
|
|
dds_qset_history (q, (dds_history_kind_t) v, x);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool qos_destination_order (struct oneliner_lex *l, dds_qos_t *q)
|
|
|
|
{
|
|
|
|
static const struct kvarg ks[] = {
|
|
|
|
{ "r", 1, (int) DDS_DESTINATIONORDER_BY_RECEPTION_TIMESTAMP },
|
|
|
|
{ "s", 1, (int) DDS_DESTINATIONORDER_BY_SOURCE_TIMESTAMP }
|
|
|
|
};
|
|
|
|
int v;
|
|
|
|
if (!read_kvarg (ks, sizeof ks, l, &v, NULL))
|
|
|
|
return false;
|
|
|
|
dds_qset_destination_order (q, (dds_destination_order_kind_t) v);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool qos_ownership (struct oneliner_lex *l, dds_qos_t *q)
|
|
|
|
{
|
|
|
|
static const struct kvarg ks[] = {
|
|
|
|
{ "s", 1, (int) DDS_OWNERSHIP_SHARED, .def = def_kvarg_int0 },
|
|
|
|
{ "x", 1, (int) DDS_OWNERSHIP_EXCLUSIVE, .arg = read_kvarg_int, .def = def_kvarg_int0 }
|
|
|
|
};
|
|
|
|
int v, x;
|
|
|
|
if (!read_kvarg (ks, sizeof ks, l, &v, &x))
|
|
|
|
return false;
|
|
|
|
dds_qset_ownership (q, (dds_ownership_kind_t) v);
|
|
|
|
dds_qset_ownership_strength (q, x);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool qos_transport_priority (struct oneliner_lex *l, dds_qos_t *q)
|
|
|
|
{
|
|
|
|
static const struct kvarg k = { "", 0, 0, .arg = read_kvarg_int };
|
|
|
|
int v, x;
|
|
|
|
if (!read_kvarg (&k, sizeof k, l, &v, &x))
|
|
|
|
return false;
|
|
|
|
dds_qset_transport_priority (q, x);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool qos_reliability (struct oneliner_lex *l, dds_qos_t *q)
|
|
|
|
{
|
|
|
|
static const struct kvarg ks[] = {
|
|
|
|
{ "be", 2, (int) DDS_RELIABILITY_BEST_EFFORT, .def = def_kvarg_dur_100ms },
|
|
|
|
{ "r", 1, (int) DDS_RELIABILITY_RELIABLE, .def = def_kvarg_dur_100ms, .arg = read_kvarg_dur }
|
|
|
|
};
|
|
|
|
int v;
|
|
|
|
dds_duration_t x;
|
|
|
|
if (!read_kvarg (ks, sizeof ks, l, &v, &x))
|
|
|
|
return false;
|
|
|
|
dds_qset_reliability (q, (dds_reliability_kind_t) v, x);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool qos_liveliness (struct oneliner_lex *l, dds_qos_t *q)
|
|
|
|
{
|
|
|
|
static const struct kvarg ks[] = {
|
|
|
|
{ "a", 1, (int) DDS_LIVELINESS_AUTOMATIC, .def = def_kvarg_dur_inf, .arg = read_kvarg_dur },
|
|
|
|
{ "p", 1, (int) DDS_LIVELINESS_MANUAL_BY_PARTICIPANT, .arg = read_kvarg_dur },
|
|
|
|
{ "w", 1, (int) DDS_LIVELINESS_MANUAL_BY_TOPIC, .arg = read_kvarg_dur }
|
|
|
|
};
|
|
|
|
int v;
|
|
|
|
dds_duration_t x;
|
|
|
|
if (!read_kvarg (ks, sizeof ks, l, &v, &x))
|
|
|
|
return false;
|
|
|
|
dds_qset_liveliness (q, (dds_liveliness_kind_t) v, x);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool qos_simple_duration (struct oneliner_lex *l, dds_qos_t *q, void (*set) (dds_qos_t * __restrict q, dds_duration_t dur))
|
|
|
|
{
|
|
|
|
static const struct kvarg k = { "", 0, 0, .arg = read_kvarg_dur };
|
|
|
|
int v;
|
|
|
|
dds_duration_t x;
|
|
|
|
if (!read_kvarg (&k, sizeof k, l, &v, &x))
|
|
|
|
return false;
|
|
|
|
set (q, x);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool qos_latency_budget (struct oneliner_lex *l, dds_qos_t *q)
|
|
|
|
{
|
|
|
|
return qos_simple_duration (l, q, dds_qset_latency_budget);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool qos_deadline (struct oneliner_lex *l, dds_qos_t *q)
|
|
|
|
{
|
|
|
|
return qos_simple_duration (l, q, dds_qset_deadline);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool qos_lifespan (struct oneliner_lex *l, dds_qos_t *q)
|
|
|
|
{
|
|
|
|
return qos_simple_duration (l, q, dds_qset_lifespan);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool qos_resource_limits (struct oneliner_lex *l, dds_qos_t *q)
|
|
|
|
{
|
|
|
|
int rl[3];
|
|
|
|
if (!read_kvarg_3len (l, rl))
|
|
|
|
return false;
|
|
|
|
dds_qset_resource_limits (q, rl[0], rl[1], rl[2]);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool qos_durability_service (struct oneliner_lex *l, dds_qos_t *q)
|
|
|
|
{
|
|
|
|
struct oneliner_lex l1 = *l;
|
|
|
|
dds_duration_t scd;
|
|
|
|
int hk = DDS_HISTORY_KEEP_LAST, hd = 1, rl[3];
|
|
|
|
if (!read_kvarg_dur (&l1, &scd))
|
|
|
|
return false;
|
|
|
|
if (peektok (&l1, NULL) == '/')
|
|
|
|
{
|
|
|
|
(void) nexttok (&l1, NULL);
|
|
|
|
if (!read_kvarg (ks_history, sizeof ks_history, &l1, &hk, &hd))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (peektok (&l1, NULL) != '/')
|
|
|
|
rl[0] = rl[1] = rl[2] = DDS_LENGTH_UNLIMITED;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
(void) nexttok (&l1, NULL);
|
|
|
|
if (!read_kvarg_3len (&l1, rl))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
dds_qset_durability_service (q, scd, (dds_history_kind_t) hk, hd, rl[0], rl[1], rl[2]);
|
|
|
|
*l = l1;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool qos_presentation (struct oneliner_lex *l, dds_qos_t *q)
|
|
|
|
{
|
|
|
|
static const struct kvarg ks[] = {
|
|
|
|
{ "i", 1, (int) DDS_PRESENTATION_INSTANCE, .def = def_kvarg_int0 },
|
|
|
|
{ "t", 1, (int) DDS_PRESENTATION_TOPIC, .def = def_kvarg_int1 },
|
|
|
|
{ "g", 1, (int) DDS_PRESENTATION_GROUP, .def = def_kvarg_int1 }
|
|
|
|
};
|
|
|
|
int v, x;
|
|
|
|
if (!read_kvarg (ks, sizeof ks, l, &v, &x))
|
|
|
|
return false;
|
|
|
|
dds_qset_presentation (q, (dds_presentation_access_scope_kind_t) v, x, 0);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool qos_autodispose_unregistered_instances (struct oneliner_lex *l, dds_qos_t *q)
|
|
|
|
{
|
|
|
|
static const struct kvarg ks[] = {
|
|
|
|
{ "y", 1, 1 },
|
|
|
|
{ "n", 1, 0 }
|
|
|
|
};
|
|
|
|
int v;
|
|
|
|
if (!read_kvarg (ks, sizeof ks, l, &v, NULL))
|
|
|
|
return false;
|
|
|
|
dds_qset_writer_data_lifecycle (q, !!v);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
char *abbrev;
|
|
|
|
size_t n;
|
|
|
|
bool (*fn) (struct oneliner_lex *l, dds_qos_t *q);
|
|
|
|
dds_qos_policy_id_t id;
|
|
|
|
} qostab[] = {
|
|
|
|
{ "ll", 2, qos_liveliness, DDS_LIVELINESS_QOS_POLICY_ID },
|
|
|
|
{ "d", 1, qos_durability, DDS_DURABILITY_QOS_POLICY_ID },
|
|
|
|
{ "dl", 2, qos_deadline, DDS_DEADLINE_QOS_POLICY_ID },
|
|
|
|
{ "h", 1, qos_history, DDS_HISTORY_QOS_POLICY_ID },
|
|
|
|
{ "lb", 2, qos_latency_budget, DDS_LATENCYBUDGET_QOS_POLICY_ID },
|
|
|
|
{ "ls", 2, qos_lifespan, DDS_LIFESPAN_QOS_POLICY_ID },
|
|
|
|
{ "do", 2, qos_destination_order, DDS_DESTINATIONORDER_QOS_POLICY_ID },
|
|
|
|
{ "o", 1, qos_ownership, DDS_OWNERSHIP_QOS_POLICY_ID },
|
|
|
|
{ "tp", 2, qos_transport_priority, DDS_OWNERSHIPSTRENGTH_QOS_POLICY_ID },
|
|
|
|
{ "p", 1, qos_presentation, DDS_PRESENTATION_QOS_POLICY_ID },
|
|
|
|
{ "r", 1, qos_reliability, DDS_RELIABILITY_QOS_POLICY_ID },
|
|
|
|
{ "rl", 2, qos_resource_limits, DDS_RESOURCELIMITS_QOS_POLICY_ID },
|
|
|
|
{ "ds", 2, qos_durability_service, DDS_DURABILITYSERVICE_QOS_POLICY_ID },
|
|
|
|
{ "ad", 2, qos_autodispose_unregistered_instances, DDS_WRITERDATALIFECYCLE_QOS_POLICY_ID }
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool setqos (struct oneliner_lex *l, dds_qos_t *q)
|
|
|
|
{
|
|
|
|
struct oneliner_lex l1 = *l;
|
|
|
|
dds_reset_qos (q);
|
|
|
|
// no whitespace between name & QoS
|
|
|
|
if (*l1.inp != '(')
|
|
|
|
return true;
|
|
|
|
nexttok (&l1, NULL); // eat '('
|
|
|
|
do {
|
|
|
|
size_t i;
|
|
|
|
union oneliner_tokval name;
|
|
|
|
if (nexttok (&l1, &name) != TOK_NAME || nexttok (&l1, NULL) != '=')
|
|
|
|
return false;
|
|
|
|
for (i = 0; i < sizeof (qostab) / sizeof (qostab[0]); i++)
|
|
|
|
{
|
|
|
|
assert (strlen (qostab[i].abbrev) == qostab[i].n);
|
|
|
|
if (strcmp (name.n, qostab[i].abbrev) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i == sizeof (qostab) / sizeof (qostab[0]))
|
|
|
|
return false;
|
|
|
|
if (!qostab[i].fn (&l1, q))
|
|
|
|
return false;
|
|
|
|
} while (nexttok_if (&l1, ','));
|
|
|
|
if (nexttok (&l1, NULL) != ')')
|
|
|
|
return false;
|
|
|
|
*l = l1;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parse_entity1 (struct oneliner_lex *l, dds_qos_t *qos)
|
|
|
|
{
|
|
|
|
struct oneliner_lex l1 = *l;
|
|
|
|
if (nexttok (&l1, NULL) != TOK_NAME)
|
|
|
|
return -1;
|
|
|
|
const char *p;
|
|
|
|
if ((p = strchr (knownentities, l1.v.n[0])) == NULL)
|
|
|
|
return -1;
|
|
|
|
int ent = (int) (p - knownentities);
|
|
|
|
int i;
|
|
|
|
for (i = 1; l1.v.n[i] == '\''; i++)
|
|
|
|
ent += (int) sizeof (knownentities) - 1;
|
|
|
|
if (l1.v.n[i] != 0)
|
|
|
|
return -1;
|
|
|
|
if (ent / 9 >= (int) MAXDOMS)
|
|
|
|
return -1;
|
|
|
|
if (!setqos (&l1, qos))
|
|
|
|
return -1;
|
|
|
|
*l = l1;
|
|
|
|
return ent;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parse_entity (struct oneliner_ctx *ctx)
|
|
|
|
{
|
|
|
|
return parse_entity1 (&ctx->l, ctx->rwqos);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parse_listener1 (struct oneliner_lex *l)
|
|
|
|
{
|
|
|
|
struct oneliner_lex l1 = *l;
|
|
|
|
size_t i;
|
|
|
|
if (nexttok (&l1, NULL) != TOK_NAME)
|
|
|
|
return -1;
|
|
|
|
for (i = 0; i < sizeof (lldesc) / sizeof (lldesc[0]); i++)
|
|
|
|
if (strcmp (l1.v.n, lldesc[i].name) == 0)
|
|
|
|
break;
|
|
|
|
if (i == sizeof (lldesc) / sizeof (lldesc[0]))
|
|
|
|
return -1;
|
|
|
|
*l = l1;
|
|
|
|
return (int) i;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int parse_listener (struct oneliner_ctx *ctx)
|
|
|
|
{
|
|
|
|
return parse_listener1 (&ctx->l);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *getentname (entname_t *name, int ent)
|
|
|
|
{
|
|
|
|
DDSRT_STATIC_ASSERT (sizeof (knownentities) == 10);
|
|
|
|
DDSRT_STATIC_ASSERT (MAXDOMS == 3);
|
|
|
|
name->n[0] = knownentities[ent % 9];
|
|
|
|
const int dom = ent / 9;
|
|
|
|
int i;
|
|
|
|
for (i = 1; i <= dom; i++)
|
|
|
|
name->n[i] = '\'';
|
|
|
|
name->n[i] = 0;
|
|
|
|
return name->n;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void make_participant (struct oneliner_ctx *ctx, int ent, dds_listener_t *list)
|
|
|
|
{
|
|
|
|
const dds_domainid_t domid = (dds_domainid_t) (ent / 9);
|
|
|
|
char *conf = ddsrt_expand_envvars ("${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}<Discovery><ExternalDomainId>0</ExternalDomainId></Discovery>", domid);
|
|
|
|
entname_t name;
|
|
|
|
printf ("create domain %"PRIu32, domid);
|
|
|
|
fflush (stdout);
|
|
|
|
if ((ctx->doms[domid] = dds_create_domain (domid, conf)) <= 0)
|
|
|
|
error_dds (ctx, ctx->doms[domid], "make_participant: create domain %"PRIu32" failed", domid);
|
|
|
|
ddsrt_free (conf);
|
|
|
|
printf (" create participant %s", getentname (&name, ent));
|
|
|
|
fflush (stdout);
|
|
|
|
if ((ctx->es[ent] = dds_create_participant (domid, NULL, list)) <= 0)
|
|
|
|
error_dds (ctx, ctx->es[ent], "make_participant: create participant failed in domain %"PRIu32, domid);
|
|
|
|
if ((ctx->tps[domid] = dds_create_topic (ctx->es[ent], &Space_Type1_desc, ctx->topicname, ctx->qos, NULL)) <= 0)
|
|
|
|
error_dds (ctx, ctx->tps[domid], "make_participant: create topic failed in domain %"PRIu32, domid);
|
|
|
|
|
|
|
|
// Create the built-in topic readers with a dummy listener to avoid any event (data available comes to mind)
|
|
|
|
// from propagating to the normal data available listener, in case it has been set on the participant.
|
|
|
|
//
|
|
|
|
// - dummy_cb aborts when it is invoked, but all reader-related listeners that can possibly trigger are set
|
|
|
|
// separately (incompatible qos, deadline missed, sample lost and sample rejected are all impossible by
|
|
|
|
// construction)
|
|
|
|
// - regarding data_on_readers: Cyclone handles listeners installed on an ancestor by *inheriting* them,
|
|
|
|
// rather than by walking up ancestor chain. Setting data_on_readers on the reader therefore overrides the
|
|
|
|
// listener set on the subscriber. It is a nice feature!
|
|
|
|
dds_listener_t *dummylist = dds_create_listener (ctx);
|
|
|
|
dds_lset_data_available (dummylist, dummy_data_available_cb);
|
|
|
|
dds_lset_data_on_readers (dummylist, dummy_data_on_readers_cb);
|
|
|
|
dds_lset_inconsistent_topic (dummylist, (dds_on_inconsistent_topic_fn) dummy_cb);
|
|
|
|
dds_lset_liveliness_changed (dummylist, dummy_liveliness_changed_cb);
|
|
|
|
dds_lset_liveliness_lost (dummylist, (dds_on_liveliness_lost_fn) dummy_cb);
|
|
|
|
dds_lset_offered_deadline_missed (dummylist, (dds_on_offered_deadline_missed_fn) dummy_cb);
|
|
|
|
dds_lset_offered_incompatible_qos (dummylist, (dds_on_offered_incompatible_qos_fn) dummy_cb);
|
|
|
|
dds_lset_publication_matched (dummylist, (dds_on_publication_matched_fn) dummy_cb);
|
|
|
|
dds_lset_requested_deadline_missed (dummylist, (dds_on_requested_deadline_missed_fn) dummy_cb);
|
|
|
|
dds_lset_requested_incompatible_qos (dummylist, (dds_on_requested_incompatible_qos_fn) dummy_cb);
|
|
|
|
dds_lset_sample_lost (dummylist, (dds_on_sample_lost_fn) dummy_cb);
|
|
|
|
dds_lset_sample_rejected (dummylist, (dds_on_sample_rejected_fn) dummy_cb);
|
|
|
|
dds_lset_subscription_matched (dummylist, dummy_subscription_matched_cb);
|
|
|
|
if ((ctx->pubrd[domid] = dds_create_reader (ctx->es[ent], DDS_BUILTIN_TOPIC_DCPSPUBLICATION, NULL, dummylist)) <= 0)
|
|
|
|
error_dds (ctx, ctx->pubrd[domid], "make_participant: create DCPSPublication reader in domain %"PRIu32, domid);
|
|
|
|
if ((ctx->subrd[domid] = dds_create_reader (ctx->es[ent], DDS_BUILTIN_TOPIC_DCPSSUBSCRIPTION, NULL, dummylist)) <= 0)
|
|
|
|
error_dds (ctx, ctx->subrd[domid], "make_participant: create DCPSSubscription reader in domain %"PRIu32, domid);
|
|
|
|
dds_delete_listener (dummylist);
|
|
|
|
//printf ("pubrd %"PRId32" subrd %"PRId32" sub %"PRId32"\n", es->pubrd[domid], es->subrd[domid], dds_get_parent (es->pubrd[domid]));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void make_entity1 (struct oneliner_ctx *ctx, int ent, dds_listener_t *list)
|
|
|
|
{
|
|
|
|
entname_t wrname;
|
|
|
|
dds_return_t ret;
|
|
|
|
int domid = ent / 9;
|
|
|
|
int ent1 = ent % 9;
|
|
|
|
switch (ent1)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
make_participant (ctx, ent, list);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
if (ctx->es[ent-1] == 0)
|
|
|
|
{
|
|
|
|
printf ("[");
|
|
|
|
make_entity1 (ctx, ent-1, NULL);
|
|
|
|
printf ("] ");
|
|
|
|
}
|
|
|
|
printf ("create subscriber %s", getentname (&wrname, ent));
|
|
|
|
fflush (stdout);
|
|
|
|
ctx->es[ent] = dds_create_subscriber (ctx->es[ent-1], NULL, list);
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
if (ctx->es[ent-2] == 0)
|
|
|
|
{
|
|
|
|
printf ("[");
|
|
|
|
make_entity1 (ctx, ent-2, NULL);
|
|
|
|
printf ("] ");
|
|
|
|
}
|
|
|
|
printf ("create publisher %s", getentname (&wrname, ent));
|
|
|
|
fflush (stdout);
|
|
|
|
ctx->es[ent] = dds_create_publisher (ctx->es[ent-2], NULL, list);
|
|
|
|
break;
|
|
|
|
case 3: case 4: case 5:
|
|
|
|
if (ctx->es[9*domid+1] == 0)
|
|
|
|
{
|
|
|
|
printf ("[");
|
|
|
|
make_entity1 (ctx, 9*domid+1, NULL);
|
|
|
|
printf ("] ");
|
|
|
|
}
|
|
|
|
printf ("create reader %s", getentname (&wrname, ent));
|
|
|
|
fflush (stdout);
|
|
|
|
ctx->es[ent] = dds_create_reader (ctx->es[9*domid+1], ctx->tps[domid], ctx->rwqos, list);
|
|
|
|
break;
|
|
|
|
case 6: case 7: case 8:
|
|
|
|
if (ctx->es[9*domid+2] == 0)
|
|
|
|
{
|
|
|
|
printf ("[");
|
|
|
|
make_entity1 (ctx, 9*domid+2, NULL);
|
|
|
|
printf ("] ");
|
|
|
|
}
|
|
|
|
printf ("create writer %s", getentname (&wrname, ent));
|
|
|
|
fflush (stdout);
|
|
|
|
ctx->es[ent] = dds_create_writer (ctx->es[9*domid+2], ctx->tps[domid], ctx->rwqos, list);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
abort ();
|
|
|
|
}
|
|
|
|
printf (" = %"PRId32, ctx->es[ent]);
|
|
|
|
fflush (stdout);
|
|
|
|
if (ctx->es[ent] <= 0)
|
|
|
|
error_dds (ctx, ctx->es[ent], "create entity %d failed", ent);
|
|
|
|
if ((ret = dds_get_instance_handle (ctx->es[ent], &ctx->esi[ent])) != 0)
|
|
|
|
error_dds (ctx, ret, "get instance handle for entity %"PRId32" failed", ctx->es[ent]);
|
|
|
|
//printf (" %"PRIx64, es->esi[ent]);
|
|
|
|
//fflush (stdout);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void make_entity (struct oneliner_ctx *ctx, int ent, dds_listener_t *list)
|
|
|
|
{
|
|
|
|
make_entity1 (ctx, ent, list);
|
|
|
|
printf ("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void setlistener (struct oneliner_ctx *ctx, struct oneliner_lex *l, int ll, int ent)
|
|
|
|
{
|
|
|
|
printf ("set listener:");
|
|
|
|
dds_return_t ret;
|
|
|
|
int dom = ent / 9;
|
|
|
|
dds_listener_t *list = ctx->cb[dom].list;
|
|
|
|
dds_reset_listener (list);
|
|
|
|
do {
|
|
|
|
printf (" %s", lldesc[ll].name);
|
|
|
|
switch (ll)
|
|
|
|
{
|
|
|
|
case 0: dds_lset_data_available (list, data_available_cb); break;
|
|
|
|
case 1: dds_lset_data_on_readers (list, data_on_readers_cb); break;
|
|
|
|
case 2: dds_lset_inconsistent_topic (list, inconsistent_topic_cb); break;
|
|
|
|
case 3: dds_lset_liveliness_changed (list, liveliness_changed_cb); break;
|
|
|
|
case 4: dds_lset_liveliness_lost (list, liveliness_lost_cb); break;
|
|
|
|
case 5: dds_lset_offered_deadline_missed (list, offered_deadline_missed_cb); break;
|
|
|
|
case 6: dds_lset_offered_incompatible_qos (list, offered_incompatible_qos_cb); break;
|
|
|
|
case 7: dds_lset_publication_matched (list, publication_matched_cb); break;
|
|
|
|
case 8: dds_lset_requested_deadline_missed (list, requested_deadline_missed_cb); break;
|
|
|
|
case 9: dds_lset_requested_incompatible_qos (list, requested_incompatible_qos_cb); break;
|
|
|
|
case 10: dds_lset_sample_lost (list, sample_lost_cb); break;
|
|
|
|
case 11: dds_lset_sample_rejected (list, sample_rejected_cb); break;
|
|
|
|
case 12: dds_lset_subscription_matched (list, subscription_matched_cb); break;
|
|
|
|
default: abort ();
|
|
|
|
}
|
|
|
|
} while (l && (ll = parse_listener1 (l)) >= 0);
|
|
|
|
if (ctx->es[ent] == 0)
|
|
|
|
{
|
|
|
|
printf (" for ");
|
|
|
|
make_entity (ctx, ent, list);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dds_listener_t *tmplist = dds_create_listener (&ctx->cb[dom]);
|
|
|
|
if ((ret = dds_get_listener (ctx->es[ent], tmplist)) != 0)
|
|
|
|
{
|
|
|
|
dds_delete_listener (tmplist);
|
|
|
|
error_dds (ctx, ret, "set listener: dds_get_listener failed on %"PRId32, ctx->es[ent]);
|
|
|
|
}
|
|
|
|
dds_merge_listener (list, tmplist);
|
|
|
|
dds_delete_listener (tmplist);
|
|
|
|
printf (" on entity %"PRId32"\n", ctx->es[ent]);
|
|
|
|
if ((ret = dds_set_listener (ctx->es[ent], list)) != 0)
|
|
|
|
error_dds (ctx, ret, "set listener: dds_set_listener failed on %"PRId32, ctx->es[ent]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static dds_instance_handle_t lookup_insthandle (const struct oneliner_ctx *ctx, int ent, int ent1)
|
|
|
|
{
|
|
|
|
// if both are in the same domain, it's easy
|
|
|
|
if (ent / 9 == ent1 / 9)
|
|
|
|
return ctx->esi[ent1];
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// if they aren't ... find GUID from instance handle in the one domain,
|
|
|
|
// then find instance handle for GUID in the other
|
|
|
|
dds_entity_t rd1 = 0, rd2 = 0;
|
|
|
|
switch (ent1 % 9)
|
|
|
|
{
|
|
|
|
case 3: case 4: case 5: rd1 = ctx->subrd[ent1/9]; rd2 = ctx->subrd[ent/9]; break;
|
|
|
|
case 6: case 7: case 8: rd1 = ctx->pubrd[ent1/9]; rd2 = ctx->pubrd[ent/9]; break;
|
|
|
|
default: return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
dds_builtintopic_endpoint_t keysample;
|
|
|
|
//printf ("(in %"PRId32" %"PRIx64" -> ", rd1, es->esi[ent1]);
|
|
|
|
//fflush (stdout);
|
|
|
|
if (dds_instance_get_key (rd1, ctx->esi[ent1], &keysample) != 0)
|
|
|
|
return 0;
|
|
|
|
// In principle, only key fields are set in sample returned by get_key;
|
|
|
|
// in the case of a built-in topic that is extended to the participant
|
|
|
|
// key. The qos and topic/type names should not be set, and there is no
|
|
|
|
// (therefore) memory allocated for the sample.
|
|
|
|
assert (keysample.qos == NULL);
|
|
|
|
assert (keysample.topic_name == NULL);
|
|
|
|
assert (keysample.type_name == NULL);
|
|
|
|
//for (size_t j = 0; j < sizeof (keysample.key.v); j++)
|
|
|
|
// printf ("%s%02x", (j > 0 && j % 4 == 0) ? ":" : "", keysample.key.v[j]);
|
|
|
|
const dds_instance_handle_t ih = dds_lookup_instance (rd2, &keysample);
|
|
|
|
//printf (" -> %"PRIx64")", ih);
|
|
|
|
//fflush (stdout);
|
|
|
|
return ih;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print_timestamp (struct oneliner_ctx *ctx, dds_time_t ts)
|
|
|
|
{
|
|
|
|
dds_time_t dt = ts - ctx->l.tref;
|
|
|
|
if ((dt % DDS_NSECS_IN_SEC) == 0)
|
|
|
|
printf ("@%"PRId64, dt / DDS_NSECS_IN_SEC);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
unsigned frac = (unsigned) (dt % DDS_NSECS_IN_SEC);
|
|
|
|
int digs = 9;
|
|
|
|
while ((frac % 10) == 0)
|
|
|
|
{
|
|
|
|
digs--;
|
|
|
|
frac /= 10;
|
|
|
|
}
|
|
|
|
printf ("@%"PRId64".%0*u", dt / DDS_NSECS_IN_SEC, digs, frac);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool parse_sample_value (struct oneliner_ctx *ctx, Space_Type1 *s, bool *valid_data, int def)
|
|
|
|
{
|
|
|
|
s->long_1 = s->long_2 = s->long_3 = def;
|
|
|
|
if (nexttok (&ctx->l, NULL) == TOK_INT) // key value (invalid sample)
|
|
|
|
{
|
|
|
|
if (ctx->l.v.i < 0)
|
|
|
|
return false;
|
|
|
|
s->long_1 = ctx->l.v.i;
|
|
|
|
*valid_data = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (ctx->l.tok == '(')
|
|
|
|
{
|
|
|
|
if (nexttok (&ctx->l, NULL) != TOK_INT || ctx->l.v.i < 0)
|
|
|
|
return false;
|
|
|
|
s->long_1 = ctx->l.v.i;
|
|
|
|
if (nexttok (&ctx->l, NULL) != ',' || nexttok (&ctx->l, NULL) != TOK_INT || ctx->l.v.i < 0)
|
|
|
|
return false;
|
|
|
|
s->long_2 = ctx->l.v.i;
|
|
|
|
if (nexttok (&ctx->l, NULL) != ',' || nexttok (&ctx->l, NULL) != TOK_INT || ctx->l.v.i < 0)
|
|
|
|
return false;
|
|
|
|
s->long_3 = ctx->l.v.i;
|
|
|
|
*valid_data = true;
|
|
|
|
return nexttok (&ctx->l, NULL) == ')';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct doreadlike_sample {
|
|
|
|
uint32_t state;
|
|
|
|
bool valid_data;
|
|
|
|
dds_time_t ts;
|
|
|
|
int wrent;
|
|
|
|
dds_instance_handle_t wrih;
|
|
|
|
Space_Type1 data;
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool wrname_from_pubhandle (const struct oneliner_ctx *ctx, int ent, dds_instance_handle_t pubhandle, entname_t *wrname)
|
|
|
|
{
|
|
|
|
dds_builtintopic_endpoint_t inf, inf1;
|
|
|
|
if (dds_instance_get_key (ctx->pubrd[ent/9], pubhandle, &inf) != 0)
|
|
|
|
return false;
|
|
|
|
for (int j = 0; j < (int) (sizeof (ctx->doms) / sizeof (ctx->doms[0])); j++)
|
|
|
|
{
|
|
|
|
for (int k = 6; k < 9; k++)
|
|
|
|
{
|
|
|
|
if (ctx->esi[9*j+k] != 0)
|
|
|
|
{
|
|
|
|
if (dds_instance_get_key (ctx->pubrd[j], ctx->esi[9*j+k], &inf1) != 0)
|
|
|
|
return false;
|
|
|
|
if (memcmp (&inf.key, &inf1.key, sizeof (inf.key)) == 0)
|
|
|
|
{
|
|
|
|
getentname (wrname, 9*j+k);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool doreadlike_parse_sample (struct oneliner_ctx *ctx, struct doreadlike_sample *s)
|
|
|
|
{
|
|
|
|
static const char *statechars = "fsaudno";
|
|
|
|
static const uint32_t statemap[] = {
|
|
|
|
DDS_NOT_READ_SAMPLE_STATE, DDS_READ_SAMPLE_STATE,
|
|
|
|
DDS_ALIVE_INSTANCE_STATE, DDS_NOT_ALIVE_NO_WRITERS_INSTANCE_STATE, DDS_NOT_ALIVE_DISPOSED_INSTANCE_STATE,
|
|
|
|
DDS_NEW_VIEW_STATE, DDS_NOT_NEW_VIEW_STATE
|
|
|
|
};
|
|
|
|
// syntax: [state]k[pubhandle][@ts] or [state](k,l,m)[pubhandle][@ts]
|
|
|
|
// the first is an invalid sample, the second a valid one, the third says anything goes
|
|
|
|
// state is a combination of: sample state (F,S fresh/stale), instance state (A,U,D), view state (N,O)
|
|
|
|
// unspecified: don't care
|
|
|
|
s->state = 0;
|
|
|
|
s->ts = -1;
|
|
|
|
s->wrent = -1;
|
|
|
|
s->wrih = 0;
|
|
|
|
struct oneliner_lex l1 = ctx->l;
|
|
|
|
if (nexttok_if (&ctx->l, TOK_NAME))
|
|
|
|
{
|
|
|
|
char *inp1 = ctx->l.v.n;
|
|
|
|
char *p;
|
|
|
|
while (*inp1 && (p = strchr (statechars, *inp1)) != NULL)
|
|
|
|
{
|
|
|
|
s->state |= statemap[(int) (p - statechars)];
|
|
|
|
inp1++;
|
|
|
|
}
|
|
|
|
if (*inp1 == 0)
|
|
|
|
;
|
|
|
|
else if (!isdigit (*inp1))
|
|
|
|
return false;
|
|
|
|
else // rewind input to digit
|
|
|
|
ctx->l.inp = l1.inp + (inp1 - ctx->l.v.n);
|
|
|
|
}
|
|
|
|
// missing states: allow everything
|
|
|
|
if ((s->state & (statemap[0] | statemap[1])) == 0)
|
|
|
|
s->state |= statemap[0] | statemap[1];
|
|
|
|
if ((s->state & (statemap[2] | statemap[3] | statemap[4])) == 0)
|
|
|
|
s->state |= statemap[2] | statemap[3] | statemap[4];
|
|
|
|
if ((s->state & (statemap[5] | statemap[6])) == 0)
|
|
|
|
s->state |= statemap[5] | statemap[6];
|
|
|
|
if (!parse_sample_value (ctx, &s->data, &s->valid_data, -1))
|
|
|
|
return false;
|
|
|
|
s->wrent = parse_entity1 (&ctx->l, NULL);
|
|
|
|
if (nexttok_if (&ctx->l, TOK_TIMESTAMP))
|
|
|
|
s->ts = ctx->l.v.d;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool doreadlike_ismatch (const dds_sample_info_t *si, const Space_Type1 *s, const struct doreadlike_sample *exp)
|
|
|
|
{
|
|
|
|
return (si->valid_data == exp->valid_data &&
|
|
|
|
(si->sample_state & exp->state) != 0 &&
|
|
|
|
(si->instance_state & exp->state) != 0 &&
|
|
|
|
(si->view_state & exp->state) != 0 &&
|
|
|
|
(exp->data.long_1 < 0 || s->long_1 == exp->data.long_1) &&
|
|
|
|
(!exp->valid_data || exp->data.long_2 < 0 || s->long_2 == exp->data.long_2) &&
|
|
|
|
(!exp->valid_data || exp->data.long_3 < 0 || s->long_3 == exp->data.long_3) &&
|
|
|
|
(exp->ts < 0 || si->source_timestamp == exp->ts) &&
|
|
|
|
(exp->wrent < 0 || si->publication_handle == exp->wrih));
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool doreadlike_matchstep (const dds_sample_info_t *si, const Space_Type1 *s, const struct doreadlike_sample *exp, int nexp, bool ellipsis, unsigned *tomatch, int *cursor, dds_instance_handle_t *lastih, int *matchidx)
|
|
|
|
{
|
|
|
|
if (si->instance_handle != *lastih)
|
|
|
|
{
|
|
|
|
*lastih = si->instance_handle;
|
|
|
|
*cursor = -1;
|
|
|
|
for (int m = 0; m < nexp; m++)
|
|
|
|
{
|
|
|
|
if ((*tomatch & (1u << m)) && s->long_1 == exp[m].data.long_1)
|
|
|
|
{
|
|
|
|
*cursor = m;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (*cursor < 0 || *cursor >= nexp)
|
|
|
|
{
|
|
|
|
*matchidx = ellipsis ? nexp : -1;
|
|
|
|
return ellipsis;
|
|
|
|
}
|
|
|
|
else if (doreadlike_ismatch (si, s, &exp[*cursor]))
|
|
|
|
{
|
|
|
|
*matchidx = *cursor;
|
|
|
|
*tomatch &= ~(1u << *cursor);
|
|
|
|
(*cursor)++;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (ellipsis)
|
|
|
|
{
|
|
|
|
*matchidx = nexp;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*matchidx = -1;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void doreadlike (struct oneliner_ctx *ctx, const char *name, dds_return_t (*fn) (dds_entity_t, void **buf, dds_sample_info_t *, size_t, uint32_t))
|
|
|
|
{
|
|
|
|
#define MAXN 10
|
|
|
|
struct doreadlike_sample exp[MAXN];
|
|
|
|
int nexp = 0;
|
|
|
|
bool ellipsis = false;
|
|
|
|
int exp_nvalid = -1, exp_ninvalid = -1;
|
|
|
|
int ent;
|
|
|
|
switch (peektok (&ctx->l, NULL))
|
|
|
|
{
|
|
|
|
default: // no expectations
|
|
|
|
ellipsis = true;
|
|
|
|
break;
|
|
|
|
case '(': // (# valid, # invalid)
|
|
|
|
nexttok (&ctx->l, NULL);
|
|
|
|
if (!(nexttok_int (&ctx->l, &exp_nvalid) && nexttok_if (&ctx->l, ',') && nexttok_int (&ctx->l, &exp_ninvalid) && nexttok_if (&ctx->l, ')')))
|
|
|
|
error (ctx, "%s: expecting (NINVALID, NVALID)", name);
|
|
|
|
ellipsis = true;
|
|
|
|
break;
|
|
|
|
case '{':
|
|
|
|
nexttok (&ctx->l, NULL);
|
|
|
|
if (!nexttok_if (&ctx->l, '}'))
|
|
|
|
{
|
|
|
|
do {
|
|
|
|
if (nexttok_if (&ctx->l, TOK_ELLIPSIS)) {
|
|
|
|
ellipsis = true; break;
|
|
|
|
} else if (nexp == MAXN) {
|
|
|
|
error (ctx, "%s: too many samples specified", name);
|
|
|
|
} else if (!doreadlike_parse_sample (ctx, &exp[nexp++])) {
|
|
|
|
error (ctx, "%s: expecting sample", name);
|
|
|
|
}
|
|
|
|
} while (nexttok_if (&ctx->l, ','));
|
|
|
|
if (!nexttok_if (&ctx->l, '}'))
|
|
|
|
error (ctx, "%s: expecting '}'", name);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ((ent = parse_entity1 (&ctx->l, NULL)) < 0)
|
|
|
|
error (ctx, "%s: entity required", name);
|
|
|
|
|
|
|
|
for (int i = 0; i < nexp; i++)
|
|
|
|
{
|
|
|
|
if (exp[i].wrent >= 0 && (exp[i].wrih = lookup_insthandle (ctx, ent, exp[i].wrent)) == 0)
|
|
|
|
error (ctx, "%s: instance lookup failed", name);
|
|
|
|
}
|
|
|
|
|
|
|
|
printf ("entity %"PRId32": %s: ", ctx->es[ent], (fn == dds_take) ? "take" : "read");
|
|
|
|
fflush (stdout);
|
|
|
|
Space_Type1 data[MAXN];
|
|
|
|
void *raw[MAXN];
|
|
|
|
for (int i = 0; i < MAXN; i++)
|
|
|
|
raw[i] = &data[i];
|
|
|
|
int matchidx[MAXN];
|
|
|
|
dds_sample_info_t si[MAXN];
|
|
|
|
DDSRT_STATIC_ASSERT (MAXN < CHAR_BIT * sizeof (unsigned));
|
|
|
|
const uint32_t maxs = (uint32_t) (sizeof (raw) / sizeof (raw[0]));
|
|
|
|
const int32_t n = fn (ctx->es[ent], raw, si, maxs, maxs);
|
|
|
|
if (n < 0)
|
|
|
|
error_dds (ctx, n, "%s: failed on %"PRId32, name, ctx->es[ent]);
|
|
|
|
unsigned tomatch = (1u << nexp) - 1; // used to track result entries matched by spec
|
|
|
|
dds_instance_handle_t lastih = 0;
|
|
|
|
int cursor = -1;
|
|
|
|
int count[2] = { 0, 0 };
|
|
|
|
bool matchok = true;
|
|
|
|
printf ("{");
|
|
|
|
for (int i = 0; i < n; i++)
|
|
|
|
{
|
|
|
|
const Space_Type1 *s = raw[i];
|
|
|
|
entname_t wrname;
|
|
|
|
count[si[i].valid_data]++;
|
|
|
|
printf ("%s%c%c%c",
|
|
|
|
(i > 0) ? "," : "",
|
|
|
|
(si[i].sample_state == DDS_NOT_READ_SAMPLE_STATE) ? 'f' : 's',
|
|
|
|
(si[i].instance_state == DDS_ALIVE_INSTANCE_STATE) ? 'a' : (si[i].instance_state == DDS_NOT_ALIVE_NO_WRITERS_INSTANCE_STATE) ? 'u' : 'd',
|
|
|
|
(si[i].view_state == DDS_NEW_VIEW_STATE) ? 'n' : 'o');
|
|
|
|
if (si[i].valid_data)
|
|
|
|
printf ("(%"PRId32",%"PRId32",%"PRId32")", s->long_1, s->long_2, s->long_3);
|
|
|
|
else
|
|
|
|
printf ("%"PRId32, s->long_1);
|
|
|
|
if (!wrname_from_pubhandle (ctx, ent, si[i].publication_handle, &wrname))
|
|
|
|
error (ctx, "%s: unknown publication handle received", name);
|
|
|
|
printf ("%s", wrname.n);
|
|
|
|
print_timestamp (ctx, si[i].source_timestamp);
|
|
|
|
if (!doreadlike_matchstep (&si[i], s, exp, nexp, ellipsis, &tomatch, &cursor, &lastih, &matchidx[i]))
|
|
|
|
matchok = false;
|
|
|
|
}
|
|
|
|
printf ("}:");
|
|
|
|
for (int i = 0; i < n; i++)
|
|
|
|
printf (" %d", matchidx[i]);
|
|
|
|
if (tomatch != 0)
|
|
|
|
{
|
|
|
|
printf (" (samples missing)");
|
|
|
|
matchok = false;
|
|
|
|
}
|
|
|
|
printf (" valid %d %d invalid %d %d", count[1], exp_nvalid, count[0], exp_ninvalid);
|
|
|
|
if (exp_nvalid >= 0 && (count[1] != exp_nvalid))
|
|
|
|
matchok = false;
|
|
|
|
if (exp_ninvalid >= 0 && (count[0] != exp_ninvalid))
|
|
|
|
matchok = false;
|
|
|
|
printf ("\n");
|
|
|
|
fflush (stdout);
|
|
|
|
if (!matchok)
|
|
|
|
testfail (ctx, "%s: mismatch between actual and expected set\n", name);
|
|
|
|
#undef MAXN
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dotake (struct oneliner_ctx *ctx) { doreadlike (ctx, "take", dds_take); }
|
|
|
|
static void doread (struct oneliner_ctx *ctx) { doreadlike (ctx, "read", dds_read); }
|
|
|
|
|
|
|
|
static void dowritelike (struct oneliner_ctx *ctx, const char *name, bool fail, dds_return_t (*fn) (dds_entity_t wr, const void *sample, dds_time_t ts))
|
|
|
|
{
|
|
|
|
dds_return_t ret;
|
|
|
|
dds_time_t ts = dds_time ();
|
|
|
|
bool valid_data;
|
|
|
|
int ent;
|
|
|
|
Space_Type1 sample;
|
|
|
|
if ((ent = parse_entity (ctx)) < 0)
|
|
|
|
error (ctx, "%s: expecting entity", name);
|
|
|
|
if (ctx->es[ent] == 0)
|
|
|
|
make_entity (ctx, ent, NULL);
|
|
|
|
if (!parse_sample_value (ctx, &sample, &valid_data, 0))
|
|
|
|
error (ctx, "%s: expecting sample value", name);
|
|
|
|
if (nexttok_if (&ctx->l, TOK_TIMESTAMP))
|
|
|
|
ts = ctx->l.v.d;
|
|
|
|
printf ("entity %"PRId32": %s (%"PRId32",%"PRId32",%"PRId32")", ctx->es[ent], name, sample.long_1, sample.long_2, sample.long_3);
|
|
|
|
print_timestamp (ctx, ts);
|
|
|
|
printf ("\n");
|
|
|
|
ret = fn (ctx->es[ent], &sample, ts);
|
|
|
|
if (!fail)
|
|
|
|
{
|
|
|
|
if (ret != 0)
|
|
|
|
error_dds (ctx, ret, "%s: failed", name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (ret == 0)
|
|
|
|
testfail (ctx, "%s: succeeded unexpectedly", name);
|
|
|
|
else if (ret != DDS_RETCODE_TIMEOUT)
|
|
|
|
error_dds (ctx, ret, "%s: failed", name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dowr (struct oneliner_ctx *ctx) { dowritelike (ctx, "wr", false, dds_write_ts); }
|
|
|
|
static void dowrfail (struct oneliner_ctx *ctx) { dowritelike (ctx, "wrfail", true, dds_write_ts); }
|
|
|
|
static void dowrdisp (struct oneliner_ctx *ctx) { dowritelike (ctx, "wrdisp", false, dds_writedispose_ts); }
|
|
|
|
static void dowrdispfail (struct oneliner_ctx *ctx) { dowritelike (ctx, "wrdispfail", true, dds_writedispose_ts); }
|
|
|
|
static void dodisp (struct oneliner_ctx *ctx) { dowritelike (ctx, "disp", false, dds_dispose_ts); }
|
|
|
|
static void dodispfail (struct oneliner_ctx *ctx) { dowritelike (ctx, "dispfail", true, dds_dispose_ts); }
|
|
|
|
static void dounreg (struct oneliner_ctx *ctx) { dowritelike (ctx, "unreg", false, dds_unregister_instance_ts); }
|
|
|
|
static void dounregfail (struct oneliner_ctx *ctx) { dowritelike (ctx, "unregfail", true, dds_unregister_instance_ts); }
|
|
|
|
|
|
|
|
static int checkstatus (struct oneliner_ctx *ctx, int ll, int ent, struct oneliner_lex *argl, const void *status)
|
|
|
|
{
|
|
|
|
assert (lldesc[ll].desc != NULL);
|
|
|
|
const char *d = lldesc[ll].desc;
|
|
|
|
int field = 0;
|
|
|
|
const char *sep = "(";
|
|
|
|
size_t off = 0;
|
|
|
|
if (nexttok (argl, NULL) != '(')
|
|
|
|
abort ();
|
|
|
|
while (*d)
|
|
|
|
{
|
|
|
|
const void *p = advance (status, &off, *d);
|
|
|
|
int i;
|
|
|
|
switch (*d)
|
|
|
|
{
|
|
|
|
case 'n':
|
|
|
|
if (!nexttok_int (argl, &i) || i < 0)
|
|
|
|
return setresult (ctx, -1, "checkstatus: field %d expecting non-negative integer", field);
|
|
|
|
printf ("%s%"PRIu32" %d", sep, *(uint32_t *)p, i); fflush (stdout);
|
|
|
|
if (*(uint32_t *)p != (uint32_t)i)
|
|
|
|
return setresult (ctx, 0, "checkstatus: field %d has actual %"PRIu32" expected %d", field, *(uint32_t *)p, i);
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
if (!nexttok_int (argl, &i))
|
|
|
|
return setresult (ctx, -1, "checkstatus: field %d expecting integer", field);
|
|
|
|
printf ("%s%"PRId32" %d", sep, *(int32_t *)p, i); fflush (stdout);
|
|
|
|
if (*(int32_t *)p != i)
|
|
|
|
return setresult (ctx, 0, "checkstatus: field %d has actual %"PRId32" expected %d", field, *(int32_t *)p, i);
|
|
|
|
break;
|
|
|
|
case 'P':
|
|
|
|
if (nexttok (argl, NULL) != TOK_NAME)
|
|
|
|
return setresult (ctx, -1, "checkstatus: field %d expecting policy name", field);
|
|
|
|
size_t polidx;
|
|
|
|
for (polidx = 0; polidx < sizeof (qostab) / sizeof (qostab[0]); polidx++)
|
|
|
|
if (strcmp (argl->v.n, qostab[polidx].abbrev) == 0)
|
|
|
|
break;
|
|
|
|
if (polidx == sizeof (qostab) / sizeof (qostab[0]))
|
|
|
|
return setresult (ctx, -1, "checkstatus: field %d expecting policy name", field);
|
|
|
|
printf ("%s%"PRIu32" %"PRIu32, sep, *(uint32_t *)p, (uint32_t) qostab[polidx].id); fflush (stdout);
|
|
|
|
if (*(uint32_t *)p != (uint32_t) qostab[polidx].id)
|
|
|
|
return setresult (ctx, 0, "checkstatus: field %d has actual %"PRIu32" expected %d", field, *(uint32_t *)p, (int) qostab[polidx].id);
|
|
|
|
break;
|
|
|
|
case 'R':
|
|
|
|
if (nexttok (argl, NULL) != TOK_NAME)
|
|
|
|
return setresult (ctx, -1, "checkstatus: field %d expecting reason", field);
|
|
|
|
if (strcmp (argl->v.n, "i") == 0) i = (int) DDS_REJECTED_BY_INSTANCES_LIMIT;
|
|
|
|
else if (strcmp (argl->v.n, "s") == 0) i = (int) DDS_REJECTED_BY_SAMPLES_LIMIT;
|
|
|
|
else if (strcmp (argl->v.n, "spi") == 0) i = (int) DDS_REJECTED_BY_SAMPLES_PER_INSTANCE_LIMIT;
|
|
|
|
else return setresult (ctx, -1, "checkstatus: field %d expecting reason", field);
|
|
|
|
printf ("%s%d %d", sep, (int) *(dds_sample_rejected_status_kind *)p, i); fflush (stdout);
|
|
|
|
if (*(dds_sample_rejected_status_kind *)p != (dds_sample_rejected_status_kind) i)
|
|
|
|
return setresult (ctx, 0, "checkstatus: field %d has actual %d expected %d", field, (int) (*(dds_sample_rejected_status_kind *)p), i);
|
|
|
|
break;
|
|
|
|
case 'I': // instance handle is too complicated
|
|
|
|
break;
|
|
|
|
case 'E': {
|
|
|
|
int ent1 = -1;
|
|
|
|
dds_instance_handle_t esi1 = 0;
|
|
|
|
if (nexttok_if (argl, '*'))
|
|
|
|
ent1 = -1;
|
|
|
|
else if ((ent1 = parse_entity1 (argl, NULL)) < 0)
|
|
|
|
return setresult (ctx, -1, "checkstatus: field %d expecting * or entity name", field);
|
|
|
|
else if ((esi1 = lookup_insthandle (ctx, ent, ent1)) == 0)
|
|
|
|
return setresult (ctx, -1, "checkstatus: field %d instance handle lookup failed", field);
|
|
|
|
printf ("%s%"PRIx64" %"PRIx64, sep, *(dds_instance_handle_t *)p, esi1); fflush (stdout);
|
|
|
|
if (ent1 >= 0 && *(dds_instance_handle_t *)p != esi1)
|
|
|
|
return setresult (ctx, 0, "checkstatus: field %d has actual %"PRIx64" expected %"PRIx64, field, *(dds_instance_handle_t *)p, esi1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
return DDS_RETCODE_BAD_PARAMETER;
|
|
|
|
}
|
|
|
|
sep = ", ";
|
|
|
|
if (*d != 'I')
|
|
|
|
field++;
|
|
|
|
++d;
|
|
|
|
if (*d && *d != 'I' && !nexttok_if (argl, ','))
|
|
|
|
return setresult (ctx, -1, "checkstatus: field %d expecting ','", field);
|
|
|
|
}
|
|
|
|
printf (")");
|
|
|
|
if (!nexttok_if (argl, ')'))
|
|
|
|
return setresult (ctx, -1, "checkstatus: field %d expecting ')'", field);
|
|
|
|
assert (off <= lldesc[ll].size);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void checklistener (struct oneliner_ctx *ctx, int ll, int ent, struct oneliner_lex *argl)
|
|
|
|
{
|
|
|
|
bool signalled = true;
|
|
|
|
uint32_t min_cnt = 1, max_cnt = UINT32_MAX;
|
|
|
|
uint32_t status;
|
|
|
|
const int dom = ent / 9;
|
|
|
|
dds_return_t ret;
|
|
|
|
printf ("listener %s: check called for entity %"PRId32, lldesc[ll].name, ctx->es[ent]);
|
|
|
|
fflush (stdout);
|
|
|
|
if (argl && lldesc[ll].cb_status_off == 0)
|
|
|
|
{
|
|
|
|
// those that don't have a status can check the number of invocations
|
|
|
|
int cnt = -1;
|
|
|
|
if (!(nexttok_if (argl, '(') && nexttok_int (argl, &cnt) && nexttok_if (argl, ')')))
|
|
|
|
error (ctx, "listener %s: expecting (COUNT)", lldesc[ll].name);
|
|
|
|
if (cnt < 0)
|
|
|
|
error (ctx, "listener %s: invocation count must be at least 0", lldesc[ll].name);
|
|
|
|
min_cnt = max_cnt = (uint32_t) cnt;
|
|
|
|
}
|
|
|
|
ddsrt_mutex_lock (&ctx->g_mutex);
|
|
|
|
bool cnt_ok = (ctx->cb[dom].cb_called[lldesc[ll].id] >= min_cnt && ctx->cb[dom].cb_called[lldesc[ll].id] <= max_cnt);
|
|
|
|
while (ctx->cb[dom].cb_called[lldesc[ll].id] < min_cnt && signalled)
|
|
|
|
{
|
|
|
|
signalled = ddsrt_cond_waitfor (&ctx->g_cond, &ctx->g_mutex, DDS_SECS (5));
|
|
|
|
cnt_ok = (ctx->cb[dom].cb_called[lldesc[ll].id] >= min_cnt && ctx->cb[dom].cb_called[lldesc[ll].id] <= max_cnt);
|
|
|
|
}
|
|
|
|
printf (" cb_called %"PRIu32" (%s)", ctx->cb[dom].cb_called[lldesc[ll].id], cnt_ok ? "ok" : "fail");
|
|
|
|
fflush (stdout);
|
|
|
|
if (!cnt_ok)
|
|
|
|
{
|
|
|
|
ddsrt_mutex_unlock (&ctx->g_mutex);
|
|
|
|
testfail (ctx, "listener %s: not invoked [%"PRIu32",%"PRIu32"] times", lldesc[ll].name, min_cnt, max_cnt);
|
|
|
|
}
|
|
|
|
dds_entity_t * const cb_entity = (dds_entity_t *) ((char *) &ctx->cb[dom] + lldesc[ll].cb_entity_off);
|
|
|
|
printf (" cb_entity %"PRId32" %"PRId32" (%s)", *cb_entity, ctx->es[ent], (*cb_entity == ctx->es[ent]) ? "ok" : "fail");
|
|
|
|
fflush (stdout);
|
|
|
|
if (*cb_entity != ctx->es[ent])
|
|
|
|
{
|
|
|
|
ddsrt_mutex_unlock (&ctx->g_mutex);
|
|
|
|
testfail (ctx, "listener %s: invoked on %"PRId32" instead of %"PRId32, lldesc[ll].name, *cb_entity, ctx->es[ent]);
|
|
|
|
}
|
|
|
|
if (!(ctx->doms[0] && ctx->doms[1]))
|
|
|
|
{
|
|
|
|
// FIXME: two domains: listener invocation happens on another thread and we can observe non-0 "change" fields
|
|
|
|
// they get updated, listener gets invoked, then they get reset -- pretty sure it is allowed by the spec, but
|
|
|
|
// not quite elegant
|
|
|
|
if ((ret = check_status_change_fields_are_0 (ll, ctx->es[ent])) <= 0)
|
|
|
|
{
|
|
|
|
ddsrt_mutex_unlock (&ctx->g_mutex);
|
|
|
|
if (ret == 0)
|
|
|
|
testfail (ctx, "listener %s: status contains non-zero change fields", lldesc[ll].name);
|
|
|
|
else if (ret < 0)
|
|
|
|
error_dds (ctx, ret, "listener %s: get entity status failed", lldesc[ll].name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (argl && lldesc[ll].cb_status_off != 0)
|
|
|
|
{
|
|
|
|
void *cb_status = (char *) &ctx->cb[dom] + lldesc[ll].cb_status_off;
|
|
|
|
if (checkstatus (ctx, ll, ent, argl, cb_status) <= 0)
|
|
|
|
{
|
|
|
|
ddsrt_mutex_unlock (&ctx->g_mutex);
|
|
|
|
longjmp (ctx->jb, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
printf ("\n");
|
|
|
|
ctx->cb[dom].cb_called[lldesc[ll].id] = 0;
|
|
|
|
ddsrt_mutex_unlock (&ctx->g_mutex);
|
|
|
|
if ((ret = dds_get_status_changes (ctx->es[ent], &status)) != 0)
|
|
|
|
error_dds (ctx, ret, "listener %s: dds_get_status_change on %"PRId32, lldesc[ll].name, ctx->es[ent]);
|
|
|
|
if ((status & (1u << lldesc[ll].id)) != 0)
|
|
|
|
testfail (ctx, "listener %s: status mask not cleared", lldesc[ll].name);
|
|
|
|
}
|
|
|
|
|
2020-05-12 09:22:56 +02:00
|
|
|
static void dowaitforack (struct oneliner_ctx *ctx)
|
2020-05-07 09:36:20 +02:00
|
|
|
{
|
2020-05-12 09:22:56 +02:00
|
|
|
dds_return_t ret;
|
|
|
|
int ent, ent1 = -1;
|
|
|
|
union { dds_guid_t x; ddsi_guid_t i; } rdguid;
|
|
|
|
if (*ctx->l.inp == '(') // reader present
|
2020-05-07 09:36:20 +02:00
|
|
|
{
|
2020-05-12 09:22:56 +02:00
|
|
|
nexttok (&ctx->l, NULL);
|
|
|
|
if ((ent1 = parse_entity (ctx)) < 0)
|
|
|
|
error (ctx, "wait for ack: expecting entity");
|
|
|
|
if ((ent1 % 9) < 3 || (ent1 % 9) > 5 || ctx->es[ent1] == 0)
|
|
|
|
error (ctx, "wait for ack: expecting existing reader as argument");
|
|
|
|
if ((ret = dds_get_guid (ctx->es[ent1], &rdguid.x)) != 0)
|
|
|
|
error_dds (ctx, ret, "wait for ack: failed to get GUID for reader %"PRId32, ctx->es[ent1]);
|
|
|
|
rdguid.i = nn_ntoh_guid (rdguid.i);
|
|
|
|
if (!nexttok_if (&ctx->l, ')'))
|
|
|
|
error (ctx, "wait for ack: expecting ')'");
|
2020-05-07 09:36:20 +02:00
|
|
|
}
|
2020-05-12 09:22:56 +02:00
|
|
|
if ((ent = parse_entity (ctx)) < 0)
|
|
|
|
error (ctx, "wait for ack: expecting writer");
|
|
|
|
if (ent1 >= 0 && ent / 9 == ent1 / 9)
|
|
|
|
error (ctx, "wait for ack: reader and writer must be in different domains");
|
|
|
|
if (ctx->es[ent] == 0)
|
|
|
|
make_entity (ctx, ent, NULL);
|
|
|
|
printf ("wait for ack %"PRId32" reader %"PRId32"\n", ctx->es[ent], ent1 < 0 ? 0 : ctx->es[ent1]);
|
|
|
|
|
|
|
|
// without a reader argument a simple dds_wait_for_acks (ctx->es[ent], DDS_SECS (5)) suffices
|
|
|
|
struct dds_entity *x;
|
|
|
|
if ((ret = dds_entity_pin (ctx->es[ent], &x)) < 0)
|
|
|
|
error_dds (ctx, ret, "wait for ack: pin entity failed %"PRId32, ctx->es[ent]);
|
|
|
|
if (dds_entity_kind (x) != DDS_KIND_WRITER)
|
|
|
|
error_dds (ctx, ret, "wait for ack: %"PRId32" is not a writer", ctx->es[ent]);
|
2020-05-07 09:36:20 +02:00
|
|
|
else
|
2020-05-12 09:22:56 +02:00
|
|
|
ret = dds__writer_wait_for_acks ((struct dds_writer *) x, (ent1 < 0) ? NULL : &rdguid.i, dds_time () + DDS_SECS (5));
|
|
|
|
dds_entity_unpin (x);
|
|
|
|
if (ret != 0)
|
2020-05-07 09:36:20 +02:00
|
|
|
{
|
2020-05-12 09:22:56 +02:00
|
|
|
if (ret == DDS_RETCODE_TIMEOUT)
|
|
|
|
testfail (ctx, "wait for acks timed out on entity %"PRId32, ctx->es[ent]);
|
|
|
|
else
|
|
|
|
error_dds (ctx, ret, "wait for acks failed on entity %"PRId32, ctx->es[ent]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dowaitfornolistener (struct oneliner_ctx *ctx, int ll)
|
|
|
|
{
|
|
|
|
printf ("listener %s: check not called", lldesc[ll].name);
|
|
|
|
fflush (stdout);
|
|
|
|
ddsrt_mutex_lock (&ctx->g_mutex);
|
|
|
|
bool ret = true;
|
|
|
|
for (int i = 0; i < (int) (sizeof (ctx->doms) / sizeof (ctx->doms[0])); i++)
|
|
|
|
{
|
|
|
|
printf (" %"PRIu32, ctx->cb[i].cb_called[lldesc[ll].id]);
|
|
|
|
if (ctx->cb[i].cb_called[lldesc[ll].id] != 0)
|
|
|
|
ret = false;
|
|
|
|
}
|
|
|
|
printf (" (%s)\n", ret ? "ok" : "fail");
|
|
|
|
ddsrt_mutex_unlock (&ctx->g_mutex);
|
|
|
|
if (!ret)
|
|
|
|
testfail (ctx, "callback %s invoked unexpectedly", lldesc[ll].name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dowaitforlistener (struct oneliner_ctx *ctx, int ll)
|
|
|
|
{
|
|
|
|
struct oneliner_lex l1 = ctx->l;
|
|
|
|
// no whitespace between name and args
|
|
|
|
const bool have_args = (*ctx->l.inp == '(');
|
|
|
|
if (have_args)
|
|
|
|
{
|
|
|
|
// skip args: we need the entity before we can interpret them
|
|
|
|
int tok;
|
|
|
|
while ((tok = nexttok (&ctx->l, NULL)) != EOF && tok != ')')
|
|
|
|
;
|
|
|
|
}
|
|
|
|
const int ent = parse_entity (ctx);
|
|
|
|
if (ent < 0)
|
|
|
|
error (ctx, "check listener: requires an entity");
|
|
|
|
if (ctx->es[ent] == 0)
|
|
|
|
setlistener (ctx, NULL, ll, ent);
|
|
|
|
checklistener (ctx, ll, ent, have_args ? &l1 : NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dowait (struct oneliner_ctx *ctx)
|
|
|
|
{
|
|
|
|
union oneliner_tokval tokval;
|
|
|
|
if (peektok (&ctx->l, &tokval) == TOK_NAME && strcmp (tokval.n, "ack") == 0)
|
|
|
|
{
|
|
|
|
nexttok (&ctx->l, NULL);
|
|
|
|
dowaitforack (ctx);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const bool expectclear = nexttok_if (&ctx->l, '!');
|
|
|
|
const int ll = parse_listener (ctx);
|
|
|
|
if (ll < 0)
|
|
|
|
error (ctx, "check listener: requires listener name");
|
|
|
|
if (expectclear)
|
|
|
|
dowaitfornolistener (ctx, ll);
|
|
|
|
else
|
|
|
|
dowaitforlistener (ctx, ll);
|
2020-05-07 09:36:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dodelete (struct oneliner_ctx *ctx)
|
|
|
|
{
|
|
|
|
dds_return_t ret;
|
|
|
|
int ent;
|
|
|
|
if ((ent = parse_entity (ctx)) < 0)
|
|
|
|
error (ctx, "delete: requires entity");
|
|
|
|
if ((ret = dds_delete (ctx->es[ent])) != 0)
|
|
|
|
error_dds (ctx, ret, "delete: failed on %"PRId32, ctx->es[ent]);
|
|
|
|
ctx->es[ent] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dodeaf (struct oneliner_ctx *ctx)
|
|
|
|
{
|
|
|
|
dds_return_t ret;
|
|
|
|
entname_t name;
|
|
|
|
int ent;
|
|
|
|
if ((ent = parse_entity (ctx)) < 0 || (ent % 9) != 0)
|
|
|
|
error (ctx, "deaf: requires participant");
|
|
|
|
printf ("deaf: %s\n", getentname (&name, ent));
|
|
|
|
if ((ret = dds_domain_set_deafmute (ctx->es[ent], true, false, DDS_INFINITY)) != 0)
|
|
|
|
error_dds (ctx, ret, "deaf: dds_domain_set_deafmute failed on %"PRId32, ctx->es[ent]);
|
|
|
|
// speed up the process by forcing lease expiry
|
|
|
|
dds_entity *x, *xprime;
|
|
|
|
if ((ret = dds_entity_pin (ctx->es[ent], &x)) < 0)
|
|
|
|
error_dds (ctx, ret, "deaf: pin participant failed %"PRId32, ctx->es[ent]);
|
|
|
|
for (int i = 0; i < (int) (sizeof (ctx->doms) / sizeof (ctx->doms[0])); i++)
|
|
|
|
{
|
|
|
|
if (i == ent / 9 || ctx->es[9*i] == 0)
|
|
|
|
continue;
|
|
|
|
if ((ret = dds_entity_pin (ctx->es[9*i], &xprime)) < 0)
|
|
|
|
{
|
|
|
|
dds_entity_unpin (x);
|
|
|
|
error_dds (ctx, ret, "deaf: pin counterpart participant failed %"PRId32, ctx->es[9*i]);
|
|
|
|
}
|
|
|
|
thread_state_awake (lookup_thread_state (), &x->m_domain->gv);
|
|
|
|
delete_proxy_participant_by_guid (&x->m_domain->gv, &xprime->m_guid, ddsrt_time_wallclock (), true);
|
|
|
|
thread_state_asleep (lookup_thread_state ());
|
|
|
|
dds_entity_unpin (xprime);
|
|
|
|
}
|
|
|
|
dds_entity_unpin (x);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dohearing (struct oneliner_ctx *ctx)
|
|
|
|
{
|
|
|
|
dds_return_t ret;
|
|
|
|
entname_t name;
|
|
|
|
int ent;
|
|
|
|
if ((ent = parse_entity (ctx)) < 0 || (ent % 9) != 0)
|
|
|
|
error (ctx, "hearing: requires participant");
|
|
|
|
printf ("hearing: %s\n", getentname (&name, ent));
|
|
|
|
if ((ret = dds_domain_set_deafmute (ctx->es[ent], false, false, DDS_INFINITY)) != 0)
|
|
|
|
error_dds (ctx, ret, "hearing: dds_domain_set_deafmute failed %"PRId32, ctx->es[ent]);
|
|
|
|
// speed up the process by forcing SPDP publication on the remote
|
|
|
|
for (int i = 0; i < (int) (sizeof (ctx->doms) / sizeof (ctx->doms[0])); i++)
|
|
|
|
{
|
|
|
|
if (i == ent / 9 || ctx->es[9*i] == 0)
|
|
|
|
continue;
|
|
|
|
dds_entity *xprime;
|
|
|
|
struct participant *pp;
|
|
|
|
if ((ret = dds_entity_pin (ctx->es[9*i], &xprime)) < 0)
|
|
|
|
error_dds (ctx, ret, "hearing: pin counterpart participant failed %"PRId32, ctx->es[9*i]);
|
|
|
|
thread_state_awake (lookup_thread_state (), &xprime->m_domain->gv);
|
|
|
|
if ((pp = entidx_lookup_participant_guid (xprime->m_domain->gv.entity_index, &xprime->m_guid)) != NULL)
|
|
|
|
resched_xevent_if_earlier (pp->spdp_xevent, ddsrt_mtime_add_duration (ddsrt_time_monotonic (), DDS_MSECS (100)));
|
|
|
|
thread_state_asleep (lookup_thread_state ());
|
|
|
|
dds_entity_unpin (xprime);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dosleep (struct oneliner_ctx *ctx)
|
|
|
|
{
|
|
|
|
if (nexttok_dur (&ctx->l, NULL, true) != TOK_DURATION)
|
|
|
|
error (ctx, "sleep: invalid duration");
|
|
|
|
dds_sleepfor (ctx->l.v.d);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dispatchcmd (struct oneliner_ctx *ctx)
|
|
|
|
{
|
|
|
|
static const struct {
|
|
|
|
const char *name;
|
|
|
|
void (*fn) (struct oneliner_ctx *ct);
|
|
|
|
} cs[] = {
|
|
|
|
{ "-", dodelete },
|
2020-05-12 09:22:56 +02:00
|
|
|
{ "?", dowait },
|
2020-05-07 09:36:20 +02:00
|
|
|
{ "wr", dowr },
|
|
|
|
{ "wrdisp", dowrdisp },
|
|
|
|
{ "disp", dodisp },
|
|
|
|
{ "unreg", dounreg },
|
|
|
|
{ "wrfail", dowrfail },
|
|
|
|
{ "wrdispfail", dowrdispfail },
|
|
|
|
{ "dispfail", dodispfail },
|
|
|
|
{ "unregfail", dounregfail },
|
|
|
|
{ "take", dotake },
|
|
|
|
{ "read", doread },
|
|
|
|
{ "deaf", dodeaf },
|
|
|
|
{ "hearing", dohearing },
|
|
|
|
{ "sleep", dosleep }
|
|
|
|
};
|
|
|
|
size_t i;
|
|
|
|
if (ctx->l.tok > 0)
|
|
|
|
{
|
|
|
|
// convert single-character token to string
|
|
|
|
ctx->l.v.n[0] = (char) ctx->l.tok;
|
|
|
|
ctx->l.v.n[1] = 0;
|
|
|
|
}
|
|
|
|
for (i = 0; i < sizeof (cs) / sizeof (cs[0]); i++)
|
|
|
|
if (strcmp (ctx->l.v.n, cs[i].name) == 0)
|
|
|
|
break;
|
|
|
|
if (i == sizeof (cs) / sizeof (cs[0]))
|
|
|
|
error (ctx, "%s: unknown command", ctx->l.v.n);
|
|
|
|
cs[i].fn (ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dosetlistener (struct oneliner_ctx *ctx, int ll)
|
|
|
|
{
|
|
|
|
int ent;
|
|
|
|
struct oneliner_lex l1 = ctx->l;
|
|
|
|
// scan past listener names to get at the entity, which we need
|
|
|
|
// to get the right listener object (and hence argument)
|
|
|
|
while (parse_listener1 (&ctx->l) >= 0)
|
|
|
|
;
|
|
|
|
if ((ent = parse_entity (ctx)) < 0)
|
|
|
|
error (ctx, "set listener: entity required");
|
|
|
|
setlistener (ctx, &l1, ll, ent);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void test_oneliner_step1 (struct oneliner_ctx *ctx)
|
|
|
|
{
|
|
|
|
while (peektok (&ctx->l, NULL) != TOK_END)
|
|
|
|
{
|
|
|
|
int ent, ll;
|
|
|
|
if (nexttok_if (&ctx->l, ';'))
|
|
|
|
; // skip ;s
|
|
|
|
else if ((ent = parse_entity (ctx)) >= 0)
|
|
|
|
make_entity (ctx, ent, NULL);
|
|
|
|
else if ((ll = parse_listener (ctx)) >= 0)
|
|
|
|
dosetlistener (ctx, ll);
|
|
|
|
else if (nexttok (&ctx->l, NULL) == TOK_NAME || ctx->l.tok > 0)
|
|
|
|
dispatchcmd (ctx);
|
|
|
|
else
|
|
|
|
error (ctx, "unexpected token %d", ctx->l.tok);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void test_oneliner_init (struct oneliner_ctx *ctx)
|
|
|
|
{
|
|
|
|
dds_qos_t *qos = dds_create_qos ();
|
|
|
|
dds_qset_reliability (qos, DDS_RELIABILITY_RELIABLE, DDS_MSECS (100));
|
|
|
|
dds_qset_destination_order (qos, DDS_DESTINATIONORDER_BY_SOURCE_TIMESTAMP);
|
|
|
|
dds_qset_history (qos, DDS_HISTORY_KEEP_ALL, 0);
|
|
|
|
|
|
|
|
*ctx = (struct oneliner_ctx) {
|
|
|
|
.l = { .tref = dds_time () },
|
|
|
|
.qos = qos,
|
|
|
|
.rwqos = dds_create_qos (),
|
|
|
|
.result = 1,
|
|
|
|
.cb = {
|
|
|
|
[0] = { .ctx = ctx, .list = dds_create_listener (&ctx->cb[0]) },
|
|
|
|
[1] = { .ctx = ctx, .list = dds_create_listener (&ctx->cb[1]) },
|
|
|
|
[2] = { .ctx = ctx, .list = dds_create_listener (&ctx->cb[2]) }
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
ddsrt_mutex_init (&ctx->g_mutex);
|
|
|
|
ddsrt_cond_init (&ctx->g_cond);
|
|
|
|
|
|
|
|
create_unique_topic_name ("ddsc_listener_test", ctx->topicname, sizeof (ctx->topicname));
|
|
|
|
}
|
|
|
|
|
|
|
|
int test_oneliner_step (struct oneliner_ctx *ctx, const char *ops)
|
|
|
|
{
|
|
|
|
if (ctx->result > 0 && setjmp (ctx->jb) == 0)
|
|
|
|
{
|
|
|
|
ctx->l.inp = ops;
|
|
|
|
test_oneliner_step1 (ctx);
|
|
|
|
}
|
|
|
|
return ctx->result;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *test_oneliner_message (const struct oneliner_ctx *ctx)
|
|
|
|
{
|
|
|
|
return ctx->msg;
|
|
|
|
}
|
|
|
|
|
|
|
|
int test_oneliner_fini (struct oneliner_ctx *ctx)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; i < sizeof (ctx->cb) / sizeof (ctx->cb[0]); i++)
|
|
|
|
dds_delete_listener (ctx->cb[i].list);
|
|
|
|
dds_delete_qos (ctx->rwqos);
|
|
|
|
dds_delete_qos ((dds_qos_t *) ctx->qos);
|
|
|
|
// prevent any listeners from being invoked so we can safely delete the
|
|
|
|
// mutex and the condition variable -- must do this going down the
|
|
|
|
// hierarchy, or listeners may remain set through inheritance
|
|
|
|
dds_return_t ret;
|
|
|
|
for (size_t i = 0; i < sizeof (ctx->es) / sizeof (ctx->es[0]); i++)
|
|
|
|
if (ctx->es[i] && (ret = dds_set_listener (ctx->es[i], NULL)) != 0)
|
|
|
|
setresult (ctx, ret, "terminate: reset listener failed on %"PRId32, ctx->es[i]);
|
|
|
|
if (ctx->result == 0)
|
|
|
|
{
|
2020-05-12 09:22:56 +02:00
|
|
|
printf ("\n-- dumping content of readers after failure --\n");
|
2020-05-07 09:36:20 +02:00
|
|
|
for (int i = 0; i < (int) (sizeof (ctx->doms) / sizeof (ctx->doms[0])); i++)
|
|
|
|
{
|
|
|
|
for (int j = 3; j <= 5; j++)
|
|
|
|
{
|
|
|
|
if (ctx->es[9*i + j])
|
|
|
|
{
|
|
|
|
const char *inp_orig = ctx->l.inp;
|
|
|
|
entname_t n;
|
|
|
|
ctx->l.inp = getentname (&n, 9*i + j);
|
|
|
|
doreadlike (ctx, "read", dds_read);
|
|
|
|
ctx->l.inp = inp_orig;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ddsrt_mutex_destroy (&ctx->g_mutex);
|
|
|
|
ddsrt_cond_destroy (&ctx->g_cond);
|
|
|
|
for (size_t i = 0; i < sizeof (ctx->doms) / sizeof (ctx->doms[0]); i++)
|
|
|
|
if (ctx->doms[i] && (ret = dds_delete (ctx->doms[i])) != 0)
|
|
|
|
setresult (ctx, ret, "terminate: delete domain on %"PRId32, ctx->doms[i]);
|
|
|
|
return ctx->result;
|
|
|
|
}
|
|
|
|
|
|
|
|
int test_oneliner (const char *ops)
|
|
|
|
{
|
|
|
|
struct oneliner_ctx ctx;
|
|
|
|
printf ("dotest: %s\n", ops);
|
|
|
|
test_oneliner_init (&ctx);
|
|
|
|
test_oneliner_step (&ctx, ops);
|
|
|
|
if (test_oneliner_fini (&ctx) <= 0)
|
|
|
|
fprintf (stderr, "FAIL: %s\n", test_oneliner_message (&ctx));
|
|
|
|
return ctx.result;
|
|
|
|
}
|