Repository navigation
Syncing a time-sensitive API application with Unet's DiscreteEventSimulator #8
ScenarioI have written an external C++ application that uses the UnetSocket API to send packets at random. The application relies on a framework written in C++, so I cannot easily rewrite it in Groovy. My application keeps track of time on the Unet node via incoming messages, which are sent by a simple My implementationMy application polls the connected Unet node for messages on a known topic, which I call extern "C" {
#include "unet.h"
}
#include <format>
#include <iostream>
#include <string>
#include <random>
int main(int argc, char** argv) {
// initalisation phase
std::string ip = argc > 1 ? argv[1] : "localhost";
int port = argc > 2 ? std::stoi(argv[2]) : 1100;
long local_time = 0L;
long next_tx = 5000L;
const long min_wait = 5000L;
const long ticker_ms = 50L; // at most agent's TickerBehavior period
// random number generation
std::seed_seq sseq{2, 4, 6, 8, port};
std::mt19937 rng;
rng.seed(sseq);
std::uniform_int_distribution<long> dist(1, ticker_ms);
// startup-like phase
unetsocket_t sock = unetsocket_open(ip.c_str(), port);
if (sock == NULL) {
std::clog << std::format("Could not open socket to {}:{}",
ip, port) << std::endl;
return 1;
}
// we want to send some Fjage messages directly
fjage_gw_t gw = unetsocket_get_gateway(sock);
const fjage_aid_t self = fjage_aid_topic("mweTic"); // inbound traffic
const fjage_aid_t node = fjage_aid_topic("mweToc"); // outbound traffic
fjage_subscribe(gw, self);
fjage_msg_t ntf_in;
fjage_msg_t req_out;
fjage_msg_t ntf_out;
// agent loop
while (true) {
// receive a message and get when it was sent
ntf_in = fjage_receive(gw, "org.arl.fjage.Message", NULL, 1000L);
if (ntf_in == NULL) {
continue;
}
local_time = fjage_msg_get_long(ntf_in, "sentAt", local_time);
fjage_msg_destroy(ntf_in);
// check if inbound message told us it's time to send, and send if so
if (local_time >= next_tx) {
char buf[5] = "MWE!";
unetsocket_send(sock, reinterpret_cast<uint8_t*>(&buf[0]),
5, 0, 0);
next_tx += min_wait;
}
// publish "we are done" on outbound topic to end block on node
ntf_out = fjage_msg_create("org.arl.fjage.Message", FJAGE_INFORM);
fjage_msg_set_recipient(ntf_out, node);
fjage_send(gw, ntf_out);
next_tx += dist(rng); // delay TX by random amount
}
}I also have a package com.example.mwe
import org.arl.fjage.AgentID
import org.arl.fjage.Performative
import org.arl.fjage.TickerBehavior
import org.arl.fjage.Message
import org.arl.unet.UnetAgent
class TimeBrokerAgent extends UnetAgent {
final String name = "Timing Broker"
final String description = ("Agent that should help time-dependent"
+ " external applications to use the discrete-event Unet simulator.")
static final AgentID EXT_TOPIC = new AgentID("mweTic", true)
static final AgentID INT_TOPIC = new AgentID("mweToc", true)
int interval = 50 // at least application's maximum random delay
@Override
void startup() {
// announce that we are interested in "tocs"
subscribe(INT_TOPIC)
add new TickerBehavior(interval, {
// send "tic" to the external application
// its "sentAt" property is used by the external application
send new Message(recipient: EXT_TOPIC,
performative: Performative.INFORM)
block(10000) // blocking forever might not be a good idea
})
}
@Override
void processMessage(Message ntf) {
// should only trigger when the external application sends a "toc"
wake()
}
}
ProblemMy implementation introduces an unwanted trade-off between artificial lag and simulation speed, when I run the application to connect to a Unet running in a QuestionIs there a better method to synchronise my external application with the |
Replies: 1 comment 3 replies
|
The essential problem here is that your application isn't running in discrete time. By having the time sync |
I thought I would think loudly to help future Unet-application devs. What I was suggesting to do was something along these lines:
TimeBrokerAgentsends a request to the application, e.g.GetNextEventReq, asking for when the next event is expected. It uses theMessage request(Message[, long])method.GetNextEventReq. The message, e.g.NextEventRsp, has thenextTxproperty.TimeBrokerAgentreceives the reply from the application and schedules the next time sync point with aWaker…