I am currently working on creating a network of multisensors (measuring temp, humidity ect). There will be tens or in some buildings even hundreds of sensors measuring at the same time. All these sensors send their data via a http GET request to a local esp32 server that processes the data and converts it into whatever the building's contol system can work with (KNX, BACnet, MODbus). Now I stress tested this server and found out that it can process around 1400 requests per minute before the sender gets no response anymore. This seems like a high amount but if a sensor sends its data every 2 seconds it means there will be a limit of around 45 sensors. I need to find a way how to process such a request quicker, this is the code I currently use:
server.on("/get-data", HTTP_GET, [](AsyncWebServerRequest *request)
{handle_get_data(request); request->send(200); });
void handle_get_data(AsyncWebServerRequest *request)
{
packetval++;
sensorData.humidity = request->arg("humidity").toFloat();
sensorData.temperature = request->arg("temperature").toFloat();
sensorData.isMovement = request->arg("isMovement");
sensorData.isSound = request->arg("isSound");
sensorData.luxValue = request->arg("luxValue").toDouble();
sensorData.RSSI = request->arg("signalValue").toInt();
sensorData.deviceID = request->arg("deviceID");
sensorData.btList = request->arg("btList");
if (deviceList.indexOf(sensorData.deviceID) == -1)
{
deviceList += sensorData.deviceID;
activeSensors++;
}
if (sensorData.isMovement || sensorData.isSound)
{
sendDataFlag = true;
}
}
I use the AsyncTCP library.
Now I measured the execution time of the function handle_get_data() and it turns out it is only ~175uS which is very quick. However the time between two calls of handle_get_data() is around 6ms which is really slow but it still doesnt explain why I can only process 1400 per minute or 24 per second (6ms = 155Hz why is my limit 24Hz?). Other than that I do not use any other code during the processing of a request, is it perhaps a limitation in the library? Is there another way to process such a request?
A request looks like this: http://192.168.6.51:80/get-data?humidity=32.0&temperature=32.0&isMovement=1&isSound=1&luxValue=123&RSSI=32&deviceID=XX:XX:XX:XX:XX:XX&btList=d1d2d3d4d5d6d7
If there is really nothing I can do I can always switch to a raspberry pi to process everything but I would rather stick to esp32 since I want to easily create an own PCB.
Thanks for all the help!
Creating a websocket instead of using http requests solved the issue for me:
AsyncWebSocket ws("/ws");
void setup()
{
ws.onEvent(onWsEvent);
server.addHandler(&ws);
}
AsyncWebSocketClient *wsClient;
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len)
{
if (type == WS_EVT_DATA)
{
AwsFrameInfo *info = (AwsFrameInfo *)arg;
String msg = "";
packetval++;
if (info->final && info->index == 0 && info->len == len)
{
if (info->opcode == WS_TEXT)
{
for (size_t i = 0; i < info->len; i++)
{
msg += (char)data[i];
}
}
}
sensorData.humidity = msg.substring(msg.indexOf("<hum>") + 5, msg.indexOf("</hum>")).toFloat();
sensorData.temperature = msg.substring(msg.indexOf("<tem>") + 5, msg.indexOf("</tem>")).toFloat();
sensorData.isMovement = (msg.substring(msg.indexOf("<isMov>") + 7, msg.indexOf("</isMov>")) == "1");
sensorData.isSound = (msg.substring(msg.indexOf("<isSnd>") + 7, msg.indexOf("</isSnd>")) == "1");
sensorData.luxValue = msg.substring(msg.indexOf("<lux>") + 5, msg.indexOf("</lux>")).toDouble();
sensorData.RSSI = msg.substring(msg.indexOf("<RSSI>") + 6, msg.indexOf("</RSSI>")).toInt();
sensorData.deviceID = msg.substring(msg.indexOf("<dID>") + 5, msg.indexOf("</dID>"));
sensorData.btList = msg.substring(msg.indexOf("<bt>") + 4, msg.indexOf("</bt>"));
if (deviceList.indexOf(sensorData.deviceID) == -1)
{
deviceList += sensorData.deviceID;
activeSensors++;
}
if (sensorData.isMovement || sensorData.isSound)
{
sendDataFlag = true;
}
}
}
This will process more than 11000 packets per minute (200kb/s). The execution time of void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) takes ~500uS now which means there is definitly optimising to do in this function but the time between two calls is reduced all the way to 1ms.
Related
My ESP32-based custom-PCB BLE peripheral is advertising LiFP batteries dynamic physical values, such as current or SoC (State of Charge).
Basically, the code is as follows:
/// Returns the manufacturer data as a String
void Ble :: setAdvertisingManufacturerData(BLEAdvertisementData *advertisementData) {
const float soc = battery.getSoc();
log("Advertising Soc %d%%", soc);
const char bytes[] = {
(manCode>>8)&0xff, manCode&0xff,
// SoC: 2 bytes | 0~2 bytes
(soc>>8)&0xff, soc&0xff,
};
advertisementData->setManufacturerData(std::string(bytes, sizeof(bytes)));
}
/// Prepares the advertising manif data
void Ble :: advertise() {
BLEAdvertisementData advertisementData;
advertisementData.setFlags(0x6);
setAdvertisingManufacturerData(&advertisementData);
pAdvertising = BLEDevice::getAdvertising();
pAdvertising->setScanResponse(true);
pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
pAdvertising->setMinPreferred(0x12);
pAdvertising->setAdvertisementData(advertisementData);
pAdvertising->start();
}
void Ble :: setup() {
// == Start the advertising
advertise();
}
/// Dynamically advertises every seconds
void Ble :: loop() {
// == Dynamically advertise
static unsigned lastAdvertised = 0;
const unsigned now = millis();
if (!lastAdvertised || now - lastAdvertised > 1000) {
lastAdvertised = now;
BLEAdvertisementData scanResponse;
setAdvertisingManufacturerData(&scanResponse);
pAdvertising->stop();
pAdvertising->setScanResponseData(scanResponse);
pAdvertising->start();
}
}
So far so good. But from the Flutter app, the advertisement manufacturer data still shows SoC to be zero (aka the initial value) despite the evolving value I see in my ESP32 logs.
I probably made a mistake, any help welcome!
[UPDATE] With the nRF mobile app I get this:
And I see there are two sections with type 0x09: the first one is "empty" while the second has the right data.
I finally did that, that makes my advertising being dynamic.
I removed the setScanResponseData()and replaced by another call of setAdvertisementData() as I do in advertise().
But I still don't get what setScanResponseData() is for.
void Ble :: loop() {
// == Dynamically advertise
static unsigned lastAdvertised = 0;
const unsigned now = millis();
if (!lastAdvertised || now - lastAdvertised > 5000) {
lastAdvertised = now;
log("Dynamic advertising");
BLEAdvertisementData scanResponse;
std::string md = getAdvertisingManufacturerData();
setAdvertisingManufacturerData(md, &scanResponse);
pAdvertising->stop();
//pAdvertising->setScanResponseData(scanResponse);
pAdvertising->setAdvertisementData(scanResponse);
pAdvertising->start();
}
}
Editing the problem statement to highlight more on the core logic
We are seeing performance issues when doing destination based rate limiting.
We maintain state for every {destination-src} pair (max of 100 destinations and 2^16 sources). We have an array of 100 nodes and at each node we have a rte_hash*. This hash table is going to maintain the state of every source ip seen by that destination. We have a mapping for every destination seen (0 to 100) and this is used to index into the array. If a particular source exceeds a threshold defined for this destination in a second, we block the source, else we allow the source. At runtime, when we see only traffic for 2 or 3 destinations, there are no issues, but when we go beyond 5, we are seeing lot of drops. Our function has to do a lookup and identify the flow matching the dest_ip and src_ip. Process the flow and decide whether it needs dropping. If the flow is not found, add it to the hash.
struct flow_state {
struct rte_hash* hash;
};
struct flow_state flow_state_arr[100];
// am going to create these hash tables using rte_hash_create at pipeline_init and free them during pipeline_free.
Am outlining what we do in pseudocode.
run()
{
1) do rx
2) from the pkt, get index into the flow_state_arr and retrieve the rte_hash* handle
3) rte_hash_lookup_data(hash, src_ip,flow_data)
4) if entry found, take decision on the flow (the decision is simply say rate limiting the flow)
5) else rte_hash_add_data(hash,src_ip,new_flow_data) to add the flow to table and forward
}
Please guide if we can have these multiple hash table objects in data path or whats the best way if we need to handle states for every destination separately.
Edit
Thanks for answering. I will be glad to share the code snippets and our gathered results. I don't have comparison results for other DPDK versions, but below are some of the results for our tests using 17.11.1.
Test Setup
Am using IXIA traffic gen (using two 10G links to generate 12Mpps) for 3 destinations 14.143.156.x (in this case - 101,102,103). Each destination's traffic comes from 2^16 different sources. This is the traffic gen setup.
Code Snippet
struct flow_state_t {
struct rte_hash* hash;
uint32_t size;
uint64_t threshold;
};
struct flow_data_t {
uint8_t curr_state; // 0 if blocked, 1 if allowed
uint64_t pps_count;
uint64_t src_first_seen;
};
struct pipeline_ratelimit {
struct pipeline p;
struct pipeline_ratelimit_params params;
rte_table_hash_op_hash f_hash;
uint32_t swap_field0_offset[SWAP_DIM];
uint32_t swap_field1_offset[SWAP_DIM];
uint64_t swap_field_mask[SWAP_DIM];
uint32_t swap_n_fields;
pipeline_msg_req_handler custom_handlers[2]; // handlers for add and del
struct flow_state_t flow_state_arr[100];
struct flow_data_t flows[100][65536];
} __rte_cache_aligned;
/*
add_handler(pipeline,msg) -- msg includes index and threshold
In the add handler
a rule/ threshold is added for a destination
rte_hash_create and store rte_hash* in flow_state_arr[index]
max of 100 destinations or rules are allowed
previous pipelines add the ID (index) to the packet to look in to the
flow_state_arr for the rule
*/
/*
del_handler(pipeline,msg) -- msg includes index
In the del handler
a rule/ threshold #index is deleted
the associated rte_hash* is also freed
the slot is made free
*/
#define ALLOWED 1
#define BLOCKED 0
#define TABLE_MAX_CAPACITY 65536
int do_rate_limit(struct pipeline_ratelimit* ps, uint32_t id, unsigned char* pkt)
{
uint64_t curr_time_stamp = rte_get_timer_cycles();
struct iphdr* iph = (struct iphdr*)pkt;
uint32_t src_ip = rte_be_to_cpu_32(iph->saddr);
struct flow_state_t* node = &ps->flow_state_arr[id];
struct flow_data_t* flow = NULL
rte_hash_lookup_data(node->hash, &src_ip, (void**)&flow);
if (flow != NULL)
{
if (flow->curr_state == ALLOWED)
{
if (flow->pps_count++ > node->threshold)
{
uint64_t seconds_elapsed = (curr_time_stamp - flow->src_first_seen) / CYCLES_IN_1SEC;
if (seconds_elapsed)
{
flow->src_first_seen += seconds_elapsed * CYCLES_IN_1_SEC;
flow->pps_count = 1;
return ALLOWED;
}
else
{
flow->pps_count = 0;
flow->curr_state = BLOCKED;
return BLOCKED;
}
}
return ALLOWED;
}
else
{
uint64_t seconds_elapsed = (curr_time_stamp - flow->src_first_seen) / CYCLES_IN_1SEC;
if (seconds_elapsed > 120)
{
flow->curr_state = ALLOWED;
flow->pps_count = 0;
flow->src_first_seen += seconds_elapsed * CYCLES_IN_1_SEC;
return ALLOWED;
}
return BLOCKED;
}
}
int index = node->size;
// If entry not found and we have reached capacity
// Remove the rear element and mark it as the index for the new node
if (node->size == TABLE_MAX_CAPACITY)
{
rte_hash_reset(node->hash);
index = node->size = 0;
}
// Add new element #packet_flows[mit_id][index]
struct flow_data_t* flow_data = &ps->flows[id][index];
*flow_data = { ALLOWED, 1, curr_time_stamp };
node->size++;
// Add the new key to hash
rte_hash_add_key_data(node->hash, (void*)&src_ip, (void*)flow_data);
return ALLOWED;
}
static int pipeline_ratelimit_run(void* pipeline)
{
struct pipeline_ratelimit* ps = (struct pipeline_ratelimit*)pipeline;
struct rte_port_in* port_in = p->port_in_next;
struct rte_port_out* port_out = &p->ports_out[0];
struct rte_port_out* port_drop = &p->ports_out[2];
uint8_t valid_pkt_cnt = 0, invalid_pkt_cnt = 0;
struct rte_mbuf* valid_pkts[RTE_PORT_IN_BURST_SIZE_MAX];
struct rte_mbuf* invalid_pkts[RTE_PORT_IN_BURST_SIZE_MAX];
memset(valid_pkts, 0, sizeof(valid_pkts));
memset(invalid_pkts, 0, sizeof(invalid_pkts));
uint64_t n_pkts;
if (unlikely(port_in == NULL)) {
return 0;
}
/* Input port RX */
n_pkts = port_in->ops.f_rx(port_in->h_port, p->pkts,
port_in->burst_size);
if (n_pkts == 0)
{
p->port_in_next = port_in->next;
return 0;
}
uint32_t rc = 0;
char* rx_pkt = NULL;
for (j = 0; j < n_pkts; j++) {
struct rte_mbuf* m = p->pkts[j];
rx_pkt = rte_pktmbuf_mtod(m, char*);
uint32_t id = rte_be_to_cpu_32(*(uint32_t*)(rx_pkt - sizeof(uint32_t)));
unsigned short packet_len = rte_be_to_cpu_16(*((unsigned short*)(rx_pkt + 16)));
struct flow_state_t* node = &(ps->flow_state_arr[id]);
if (node->hash && node->threshold != 0)
{
// Decide whether to allow of drop the packet
// returns allow - 1, drop - 0
if (do_rate_limit(ps, id, (unsigned char*)(rx_pkt + 14)))
valid_pkts[valid_pkt_count++] = m;
else
invalid_pkts[invalid_pkt_count++] = m;
}
else
valid_pkts[valid_pkt_count++] = m;
if (invalid_pkt_cnt) {
p->pkts_mask = 0;
rte_memcpy(p->pkts, invalid_pkts, sizeof(invalid_pkts));
p->pkts_mask = RTE_LEN2MASK(invalid_pkt_cnt, uint64_t);
rte_pipeline_action_handler_port_bulk_mod(p, p->pkts_mask, port_drop);
}
p->pkts_mask = 0;
memset(p->pkts, 0, sizeof(p->pkts));
if (valid_pkt_cnt != 0)
{
rte_memcpy(p->pkts, valid_pkts, sizeof(valid_pkts));
p->pkts_mask = RTE_LEN2MASK(valid_pkt_cnt, uint64_t);
}
rte_pipeline_action_handler_port_bulk_mod(p, p->pkts_mask, port_out);
/* Pick candidate for next port IN to serve */
p->port_in_next = port_in->next;
return (int)n_pkts;
}
}
RESULTS
When generated traffic for only one destination from 60000 sources with threshold of 14Mpps, there were no drops. We were able to send 12Mpps from IXIA and recv 12Mpps
Drops were observed after adding 3 or more destinations (each configured to recv traffic from 60000 sources). The throughput was only 8-9 Mpps. When sent for 100 destinations (60000 src each), only 6.4Mpps were handled. 50% drop was seen.
On running it through vtune-profiler, it reported rte_hash_lookup_data as the hotspot and mostly memory bound (DRAM bound). I will attach the vtune report soon.
Based on the update from internal testing, rte_hash library is not causing performance drops. Hence as suggested in comment is more likely due to current pattern and algorithm design which might be leading cache misses and lesser Instruction per Cycle.
To identify whether it is frontend stall or backend pipeline stall or memory stall please either use perf or vtune. Also try to minimize branching and use more likely and prefetch too.
I have 3 devices which send 8 bytes of data over CAN interface. To read the buffer from CAN I am using a while loop which looks something like this:
void CanServer::ReadFromCAN() {
data_from_buffer_.clear();
can_frame frame;
read_can_port_ = read(soc_, &frame, sizeof(struct can_frame));
if (read_can_port_ < 0) return;
id_ = frame.can_id&0x1FFFFFFF;
dlc_ = frame.can_dlc;
for (const auto& byte : frame.data)
data_from_buffer_.push_back(byte);
}
while (ros::ok()) {
std_msgs::Int32MultiArray tachometer_array;
std::vector<__u8> data_from_can;
/***
* Read for the Radar1
*/
this->ReadFromCAN();
if (read_can_port_ < 0) continue;
//ROS_INFO("Read from CAN");
if (id_ == can_id::RadarFrame1)
for (int i = 0; i < dlc_; i++) {
radar1_bytes_[i] = data_from_buffer_[i];
radar1_buffer_.push_back(data_from_buffer_[i]);
}
if (IsMagicWord(radar1_bytes_, 0)) {
frame_id = "radar1_link";
this->PulbishRadarPCL(frame_id, radar1_pub_, radar1_buffer_, 0);
radar1_buffer_.clear();
canFrame_.can_dlc = 0;
}
}
if (id_ == can_id::RadarFrame2) {
for (int i = 0; i < dlc_; i++) {
radar2_bytes_[i] = data_from_buffer_[i];
radar2_buffer_.push_back(data_from_buffer_[i]);
}
if (IsMagicWord(radar2_bytes_, 1)) {
frame_id = "radar2_link";
this->PulbishRadarPCL(frame_id, radar2_pub_, radar2_buffer_, 1);
radar2_buffer_.clear();
canFrame_.can_dlc = 0;
}
}
if (id_ == can_id::RadarFrame3) {
for (int i = 0; i < dlc_; i++) {
radar3_bytes_[i] = data_from_buffer_[i];
radar3_buffer_.push_back(data_from_buffer_[i]);
}
if (IsMagicWord(radar3_bytes_, 2)) {
frame_id = "radar3_link";
this->PulbishRadarPCL(frame_id, radar3_pub_, radar3_buffer_, 2);
radar3_buffer_.clear();
canFrame_.can_dlc = 0;
}
}
rate.sleep();
}
Where rate.sleep() is similar to sleep() function in C++.
Right now, I am running this while loop in 5 MHz however I think this is an overkill and I am getting almost 100% CPU usage on a 1 core.
I tried to play around with the delay time but I think this is highly inefficient and I wonder is there any other way to handle this?
It turns out that poll is what you need. Here is my example.
First, create a pollfd structure from <poll.h> header in Linux. I have decided to create a class member but you can create however you like:
pollfd poll_;
poll_.fd = soc_;
poll_.events = POLLIN;
poll_.revents = 0;
Here, soc_ is a socket and POLLIN means that you want to read from the socket.
Then, in my while loop, instead of delaying I just used this function at the beginning of my while loop:
poll_int = poll(&poll_, 1, 100);
if (poll_int <= 0) continue;
So poll() function returns value of 1 if the read was succesful and I made a timeout of 100ms (just a random number, I know that the data are coming at much higher rate)
With that, you will only read the data from socket whenever poll returns a value greater that 0.
Results? 3% CPU usage and if you want to add more data into your socket flow, poll will optimize for you so this is a scalable way of reading something like CAN bus.
We're developing a high frequency trading platform with C++ and we've tried implementing grpc with protobuf but we saw that a single network call tooks approximately 200-300 microseconds which is too long for us. What we are expecting to have as serializing/deserializing data through network socket is approximately 50-60 microseconds.
Than we 've tried to use protobuf with native c++ sockets (with using non blocking i/o), we saw that this time performance became approximately 150-200 microseconds which was not enough for us. Than we saw flatbuffers and implemented it as described in below. However during our tests we saw that only serializing (also same in deserializing) tooks approximately 50 microseconds and also transferring the data tooks 30-40 microseconds so totatly it tooks approximately 100-150 microseconds. So I wondered if we are doing something wrong in our implementation of flatbuffers.
In the below example, I've calculated the difference betwen timestamp logs are :
Timestamp 1 -> Timestamp 2 = 16 microseconds
Timestamp 2 -> Timestamp 3 = 24 microseconds
Total serialization = 40 microseconds
Do you know any other way to increase the performance
Example code for serializing data with flatbuffers in C++:
const char* MAHelper::getRequest(BaseRequest *request,int& size) {
const char *result;
flatbuffers::FlatBufferBuilder builder(10240);
if (request->orderType == OrderTypes::TYPE_LoginRequest){
std::cout<<"Timestamp 1: "<<getCurrentTimestamp()<<std::endl;
LoginRequest *loginRequest = (LoginRequest*) request;
std::cout<<"Converting Login Request 1: "<<getCurrentTimestamp()<<std::endl;
auto username = builder.CreateString(loginRequest->userName);
auto password = builder.CreateString(loginRequest->password);
auto application = getApplication(loginRequest->applicationType);
std::cout<<"Timestamp 2: "<<getCurrentTimestamp()<<std::endl;
auto loginReq = piramit::orders::fb::CreateLoginRequest(builder,username,password,application);
auto loginOrderBase = piramit::orders::fb::CreateRequestHolder(builder,piramit::orders::fb::BaseRequest_LoginRequest,loginReq.Union());
builder.Finish(loginOrderBase);
std::cout<<"Timestamp 3:"<<getCurrentTimestamp()<<std::endl;
} else if (request->orderType == OrderTypes::TYPE_EnterOrderRequest) {
EnterOrderRequest *enterOrderRequest = (EnterOrderRequest*) request;
auto strategyIdentifier = builder.CreateString(enterOrderRequest->strategyIdentifier);
auto passThrough = builder.CreateString(enterOrderRequest->passThrough);
auto account = builder.CreateString(enterOrderRequest->account);
auto authToken = builder.CreateString(enterOrderRequest->baseRequest.authToken);
auto enterOrderReq = piramit::orders::fb::CreateEnterOrder(builder,enterOrderRequest->orderbookId,enterOrderRequest->quantity,enterOrderRequest->price,account,
getStrategyType(enterOrderRequest->strategyType),strategyIdentifier,getSide(enterOrderRequest->side),getTimeInForce(enterOrderRequest->timeInForce),passThrough,getOrderType(enterOrderRequest->orderType));
auto enterOrderBase = piramit::orders::fb::CreateRequestHolder(builder,piramit::orders::fb::BaseRequest_EnterOrder,enterOrderReq.Union(),authToken);
builder.Finish(enterOrderBase);
} else if (request->orderType == OrderTypes::TYPE_ReplaceOrderRequest) {
ReplaceOrderRequest *replaceOrderRequest = (ReplaceOrderRequest*) request;
auto orderToken = builder.CreateString(replaceOrderRequest->orderToken);
auto authToken = builder.CreateString(replaceOrderRequest->baseRequest.authToken);
auto replaceOrderReq = piramit::orders::fb::CreateReplaceOrder(builder,orderToken,replaceOrderRequest->quantity,replaceOrderRequest->price);
auto replaceOrderBase = piramit::orders::fb::CreateRequestHolder(builder,piramit::orders::fb::BaseRequest_ReplaceOrder,replaceOrderReq.Union(),authToken);
builder.Finish(replaceOrderBase);
} else if (request->orderType == OrderTypes::TYPE_CancelOrderRequest) {
CancelOrderRequest *cancelOrderRequest = (CancelOrderRequest*) request;
auto orderToken = builder.CreateString(cancelOrderRequest->orderToken);
auto authToken = builder.CreateString(cancelOrderRequest->baseRequest.authToken);
auto cancelOrderReq = piramit::orders::fb::CreateCancelOrder(builder,orderToken);
auto cancelOrderBase = piramit::orders::fb::CreateRequestHolder(builder,piramit::orders::fb::BaseRequest_CancelOrder,cancelOrderReq.Union(),authToken);
builder.Finish(cancelOrderBase);
} else if (request->orderType == OrderTypes::TYPE_BasicOrderRequest) {
BasicOrderRequest *basicOrderRequest = (BasicOrderRequest*) request;
auto authToken = builder.CreateString(basicOrderRequest->baseRequest.authToken);
auto basicOrderReq = piramit::orders::fb::CreateOrderRequest(builder,getOperationType(basicOrderRequest->operation),basicOrderRequest->orderId,getOrderType(basicOrderRequest->orderTypes));
auto basicOrderBase = piramit::orders::fb::CreateRequestHolder(builder,piramit::orders::fb::BaseRequest_OrderRequest,basicOrderReq.Union(),authToken);
builder.Finish(basicOrderBase);
} else if (request->orderType == OrderTypes::TYPE_AccountStrategyRequest) {
AccountStrategyRequest *accountStrategyRequest = (AccountStrategyRequest*) request;
flatbuffers::Offset<flatbuffers::String> account = 0;
flatbuffers::Offset<flatbuffers::String> strategyIdentifier = 0;
auto authToken = builder.CreateString(accountStrategyRequest->baseRequest.authToken);
if (accountStrategyRequest->operation == OPERATION_SET) {
account = builder.CreateString(accountStrategyRequest->accountStrategy.account);
strategyIdentifier = builder.CreateString(accountStrategyRequest->accountStrategy.strategyIdentifier);
}
flatbuffers::Offset<piramit::orders::fb::AccountStrategy> accountStrategy = piramit::orders::fb::CreateAccountStrategy(builder,accountStrategyRequest->accountStrategy.orderBookId,account,getStrategyType(accountStrategyRequest->accountStrategy.strategyType),strategyIdentifier);
auto accountStrategyReq = piramit::orders::fb::CreateAccountStrategyRequest(builder,getOperationType(accountStrategyRequest->operation),accountStrategy);
auto accountStrategyBase = piramit::orders::fb::CreateRequestHolder(builder,piramit::orders::fb::BaseRequest_AccountStrategyRequest,accountStrategyReq.Union(),authToken);
builder.Finish(accountStrategyBase);
} else if (request->orderType == OrderTypes::TYPE_OrderBookStateRequest) {
OrderBookStateRequest *orderBookStateRequest = (OrderBookStateRequest*) request;
auto stateName = builder.CreateString(orderBookStateRequest->stateName);
auto orderBookStateReq = piramit::orders::fb::CreateOrderBookStateRequest(builder,stateName,orderBookStateRequest->orderBookId,orderBookStateRequest->timestamp);
auto orderBookStateBase = piramit::orders::fb::CreateRequestHolder(builder,piramit::orders::fb::BaseRequest_OrderBookStateRequest,orderBookStateReq.Union());
builder.Finish(orderBookStateBase);
}
uint8_t *requestBuffer = builder.GetBufferPointer();
result = (const char*) requestBuffer;
size = builder.GetSize();
return result;
}
And also this is part of our schema in flatbuffers
union BaseRequest { LoginRequest,EnterOrder,CancelOrder,ReplaceOrder,OrderRequest,AccountStrategyRequest,OrderBookStateRequest }
table RequestHolder {
request:BaseRequest;
authToken:string;
}
table LoginRequest {
username:string;
password:string;
application:Application = APP_UNKNOWN;
}
table EnterOrder{
order_book_id:uint;
quantity:ulong;
price:int;
account:string;
strategy:StrategyType;
strategy_identifier:string;
side:Side;
time_in_force:TimeInForce;
pass_through:string;
order_type:OrderType;
}
root_type RequestHolder;
For serializing:
You can save yourself some time by reusing the FlatBufferBuilder accross, just call Reset() on it to clear.
You are doing HFT in C++, yet a lot of your data consists of strings? FlatBuffers has all sorts of really efficient ways of representing data, with scalars, structs and enums. Try to find better representations of your data if speed really matters.
For deserializing:
Deserializing in FlatBuffers costs 0ms, since there is no need to do anything. You can access in place. If what you're doing is copying all incoming FlatBuffers data into your own data structures, you are throwing away one of FlatBuffers biggest advantages. Instead, make the code acting on the incoming data work directly with the incoming FlatBuffer.
I'm new in Omnet Veins. I try to create my own application. So first of all, I have done this in the existing TraciDemo11p files (I have just kept the files name and modify the code).
In the first step, I want to make all nodes sending a HelloMsg (a new packet that I have created .msg .h and .cc).
To well understand how messages are exchanged between nodes, I launched the simulation and all is well, but I cannot realize if the messages are received by nodes or not.
This is a screenshot of what I have:
enter image description here
I followed the transmission of the message between the application, mac and phy layers. I can see that the message is successfully transmitted by node1 for example. But does the message on node[0] "packet was not detected by the card. power was under sensitivity threshold" mean that the packet was not received by node[0]?. If it is the case, how can I fix that? Also, I cannot find the source file of this message (apparently, in PhyLayer80211p.cc or BasehyLayer.cc but I cannot find it).
In the second step, I want to use two RSUs. Nodes broadcast a helloMessage and then each RSU will repeat the received signal. To clarify more, this exactly what I have:
First of all. I add another RSU to the veins example as follows:
##########################################################
# RSU SETTINGS #
# #
# #
##########################################################
*.rsu[0].mobility.x = 6490
*.rsu[0].mobility.y = 1000
*.rsu[0].mobility.z = 3
*.rsu[1].mobility.x = 7491
*.rsu[1].mobility.y = 1000
*.rsu[1].mobility.z = 3
*.rsu[*].applType = "TraCIDemoRSU11p"
*.rsu[*].appl.headerLength = 80 bit
*.rsu[*].appl.sendBeacons = false
*.rsu[*].appl.dataOnSch = false
*.rsu[*].appl.beaconInterval = 1s
*.rsu[*].appl.beaconUserPriority = 7
*.rsu[*].appl.dataUserPriority = 5
Also, I made two maxInterferenceDistance, one of the nodes and the other for the RSUs:
##########################################################
# 11p specific parameters #
# #
# NIC-Settings #
##########################################################
*.connectionManager.sendDirect = true
*.connectionManager.maxInterfDist = 1000m #2600m
*.connectionManager.drawMaxIntfDist = false #false
*.connectionManager.maxInterfDistNodes = 300m
*.connectionManager.drawMaxIntfDistNodes = false
*.**.nic.mac1609_4.useServiceChannel = false
*.**.nic.mac1609_4.txPower = 20mW
*.**.nic.mac1609_4.bitrate = 6Mbps
*.**.nic.phy80211p.sensitivity = -89dBm
*.**.nic.phy80211p.useThermalNoise = true
*.**.nic.phy80211p.thermalNoise = -110dBm
*.**.nic.phy80211p.decider = xmldoc("config.xml")
*.**.nic.phy80211p.analogueModels = xmldoc("config.xml")
*.**.nic.phy80211p.usePropagationDelay = true
*.**.nic.phy80211p.antenna = xmldoc("antenna.xml", "/root/Antenna[#id='monopole']")
To make the transmission range of RSU different on that of nodes, I made this change in the isInRange function of the baseConnectionMannager:
bool BaseConnectionManager::isInRange(BaseConnectionManager::NicEntries::mapped_type pFromNic, BaseConnectionManager::NicEntries::mapped_type pToNic)
{
double dDistance = 0.0;
if ((pFromNic->hostId == 7) || (pFromNic->hostId == 8)) {
EV<<"RSU In range from: "<<pFromNic->getName()<<" "<<pFromNic->hostId<<" to: "<<pToNic->getName()<<" "<<pToNic->hostId<<"\n";
if(useTorus) {
dDistance = sqrTorusDist(pFromNic->pos, pToNic->pos, *playgroundSize);
} else {
dDistance = pFromNic->pos.sqrdist(pToNic->pos);
}
return (dDistance <= maxDistSquared);
} else {
if(useTorus) {
dDistance = sqrTorusDist(pFromNic->pos, pToNic->pos, *playgroundSize);
} else {
dDistance = pFromNic->pos.sqrdist(pToNic->pos);
}
return (dDistance <= maxDistSquaredNodes);
}
}
Where node IDs 7 and 8 are the RSUs in the scenario I run.
In addition, I have the TraciDemo11p (for nodes) and TraciDemoRSU11p (for RSUs) modified as follow:
- In the TraciDemo11p, nodes when enter the network broadcast a Hello message to all their neighbors. The code is:
void TraCIDemo11p::initialize(int stage) {
BaseWaveApplLayer::initialize(stage);
if (stage == 0) {
HelloMsg *msg = createMsg();
SendHello(msg);
}
}
HelloMsg* TraCIDemo11p::createMsg() {
int source_id = myId;
double t0 = 0;
int port = 0;
char msgName[20];
sprintf(msgName, "send Hello from %d at %f from gate %d",source_id, t0, port);
HelloMsg* msg = new HelloMsg(msgName);
populateWSM(msg);
return msg;
}
void TraCIDemo11p::SendHello(HelloMsg* msg) {
findHost()->getDisplayString().updateWith("r=16,green");
msg->setSource_id(myId);
cMessage* mm = dynamic_cast<cMessage*>(msg);
scheduleAt(simTime() + 10 + uniform(0.01, 0.02), mm);
}
void TraCIDemo11p::handleSelfMsg(cMessage* msg) {
if (dynamic_cast<HelloMsg*>(msg)) {
HelloMsg* recv = dynamic_cast<HelloMsg*>(msg);
ASSERT(recv);
int sender = recv->getSource_id();
if (sender == myId) {
EV <<myId <<" broadcasting Hello Message \n";
recv->setT0(SIMTIME_DBL(simTime()));
sendDown(recv->dup());
}
}
else {
BaseWaveApplLayer::handleSelfMsg(msg);
}
}
void TraCIDemo11p::onHelloMsg(HelloMsg* hmsg) {
if ((hmsg->getSource_id() == 7) || (hmsg->getSource_id() == 8)) {
EV <<"Node: "<<myId<<" receiving HelloMsg from rsu: "<<hmsg->getSource_id()<<"\n";
} else {
EV <<"Node: "<<myId<<" receiving HelloMsg "<<hmsg->getKind()<<" from node: "<<hmsg->getSource_id()<<"\n";
NBneighbors++;
neighbors.push_back(hmsg->getSource_id());
EV <<"Node: "<<myId<<" neighbors list: ";
list<int>::iterator it = neighbors.begin();
while (it != neighbors.end()) {
EV <<*it<<" ";
it++;
}
}
}
void TraCIDemo11p::handlePositionUpdate(cObject* obj) {
BaseWaveApplLayer::handlePositionUpdate(obj);
}
On the other hand, RSUs just repeat the message they received from nodes. So, I have on the TraciDemoRSU11p:
void TraCIDemoRSU11p::onHelloMsg(HelloMsg* hmsg) {
if ((hmsg->getSource_id() != 7) && (hmsg->getSource_id() != 8))
{
EV <<"RSU: "<<myId<<" receiving HelloMsg "<<hmsg->getKind()<<" from node: "<<hmsg->getSource_id()<<" at: "<<SIMTIME_DBL(simTime())<<" \n";
//HelloMsg *msg = createMsg();
//SendHello(msg);
hmsg->setSenderAddress(myId);
hmsg->setSource_id(myId);
sendDelayedDown(hmsg->dup(), 2 + uniform(0.01,0.2));
}
else {
EV<<"Successful connection between RSUs \n";
EV <<"RSU: "<<myId<<" receiving HelloMsg "<<hmsg->getKind()<<" from node: "<<hmsg->getSource_id()<<"\n";
}
}
After the execution of this code, I can see:
a few numbers of vehicles receiving the hello message from their neighbors.
also, just a few messages were received by the two RSUs.
Each RSUs repeats the signal it receives, but there is no communication between the two RSU, which are supposed in the transmission of one another.
And always I have a lot of this message "packet was not detected by the card. power was under sensitivity threshold" printed on my screen.
Is there any problem in the transmission range or it is a question of interference? Also, I would like to mention that in the analysis there is no packet loss.
Thanks in advance.
Please help.