Skip to content

Commit

Permalink
[FUS-1598] Add C++ request_version example. (#270)
Browse files Browse the repository at this point in the history
  • Loading branch information
axlan authored Oct 5, 2023
2 parents cc3c018 + d82be6d commit b37a46e
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 1 deletion.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ The `examples/` directory contains example applications demonstrating how to use
- `external_cmake_project` - Download a copy of the FusionEngine Client library from the public repository and import
it into a CMake project using `FetchContent`.
- `generate_data` - Generate a binary file containing a fixed set of messages.
- `lband_decode` - Example of decoding RTCM corrections from a recorded file containing LBandFrameMessage.
- `request_version` - Simulate sending a request for a version info message, and waiting for a response.
- `tcp_client` - Connect to a device over TCP and display the received FusionEngine messages.
- `udp_client` - Connect to a device over UDP and display the received FusionEngine messages.

Expand Down
1 change: 1 addition & 0 deletions examples/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ filegroup(
srcs = [
"//generate_data",
"//message_decode",
"//request_version",
"//tcp_client",
"//udp_client",
]
Expand Down
3 changes: 2 additions & 1 deletion examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
add_subdirectory(common)

add_subdirectory(generate_data)
add_subdirectory(message_decode)
add_subdirectory(lband_decode)
add_subdirectory(message_decode)
add_subdirectory(request_version)
add_subdirectory(tcp_client)
add_subdirectory(udp_client)

Expand Down
22 changes: 22 additions & 0 deletions examples/common/print_message.cc
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,35 @@ void PrintMessage(const MessageHeader& header, const void* payload_in) {
sv.azimuth_deg);
printf(" In solution: %s\n", sv.usage > 0 ? "yes" : "no");
}
} else if (header.message_type == MessageType::VERSION_INFO) {
auto& contents = *reinterpret_cast<const VersionInfoMessage*>(payload);
payload += sizeof(contents);

double system_time = contents.system_time_ns * 1e-9;
printf(
"Version info message @ System time %.3f seconds. [sequence=%u, "
"size=%zu B]\n",
system_time, header.sequence_number, message_size);
printf(" Firmware version: %.*s\n", contents.fw_version_length,
reinterpret_cast<const char*>(payload));
} else {
printf("Received message type %s. [sequence=%u, %zu bytes]\n",
to_string(header.message_type), header.sequence_number,
message_size);
}
}

/******************************************************************************/
void PrintHex(const void* data, size_t data_len_bytes) {
const uint8_t* data_ptr = static_cast<const uint8_t*>(data);
for (size_t i = 0; i < data_len_bytes; ++i) {
printf("%02x", data_ptr[i]);
if (i < data_len_bytes - 1) {
printf(" ");
}
}
}

} // namespace examples
} // namespace fusion_engine
} // namespace point_one
2 changes: 2 additions & 0 deletions examples/common/print_message.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ namespace examples {
void PrintMessage(const fusion_engine::messages::MessageHeader& header,
const void* payload);

void PrintHex(const void* data, size_t data_len_bytes);

} // namespace examples
} // namespace fusion_engine
} // namespace point_one
12 changes: 12 additions & 0 deletions examples/request_version/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package(default_visibility = ["//visibility:public"])

cc_binary(
name = "request_version",
srcs = [
"request_version.cc",
],
deps = [
"//common:print_message",
"@fusion_engine_client",
],
)
3 changes: 3 additions & 0 deletions examples/request_version/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
add_executable(request_version request_version.cc)
target_link_libraries(request_version PUBLIC fusion_engine_client)
target_link_libraries(request_version PUBLIC print_message)
138 changes: 138 additions & 0 deletions examples/request_version/request_version.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/**************************************************************************/ /**
* @brief Simulate sending a request for a version info message, and waiting for
* a response.
* @file
******************************************************************************/

#include <cmath>
#include <cstdio>
#include <cstring>

#include <point_one/fusion_engine/messages/core.h>
#include <point_one/fusion_engine/messages/crc.h>
#include <point_one/fusion_engine/parsers/fusion_engine_framer.h>

#include "../common/print_message.h"

using namespace point_one::fusion_engine::examples;
using namespace point_one::fusion_engine::messages;
using namespace point_one::fusion_engine::parsers;

bool message_found = false;

// Enforce a 4-byte aligned address.
alignas(4) uint8_t storage[4096];

// Fake Send/Receive functions.
/******************************************************************************/
void SendData(void* data, size_t data_len_bytes) {
(void)data;
(void)data_len_bytes;
}

/******************************************************************************/
size_t ReceiveData(uint8_t* buffer, size_t read_size) {
static size_t offset = 0;
if (offset + read_size < sizeof(storage)) {
// We're using this data as if it were received from the device.
memcpy(buffer, storage + offset, read_size);
offset += read_size;
return read_size;
} else {
return 0;
}
}

/******************************************************************************/
int main(int argc, const char* argv[]) {
if (argc != 1) {
printf("Usage: %s\n", argv[0]);
printf(R"EOF(
Simulate sending a version request, and parsing the response.
)EOF");
return 0;
}

//////////////////////////////////////////////////////////////////////////////
// Write a VersionInfoMessage request.
//////////////////////////////////////////////////////////////////////////////

uint8_t* buffer = storage;
auto header = reinterpret_cast<MessageHeader*>(buffer);
buffer += sizeof(MessageHeader);
*header = MessageHeader();

header->sequence_number = 0;
header->message_type = MessageType::MESSAGE_REQUEST;
header->payload_size_bytes = sizeof(MessageRequest);

auto req_message = reinterpret_cast<MessageRequest*>(buffer);
*req_message = MessageRequest();

req_message->message_type = VersionInfoMessage::MESSAGE_TYPE;

header->crc = CalculateCRC(storage);

printf("Sending VersionInfoMessage request:\n ");
PrintHex(storage, sizeof(MessageHeader) + sizeof(MessageRequest));
// This data would be sent over serial to the device.
SendData(storage, sizeof(MessageHeader) + sizeof(MessageRequest));

printf("\n");

//////////////////////////////////////////////////////////////////////////////
// Generate an example response of the data a device would send back.
//////////////////////////////////////////////////////////////////////////////

static constexpr char VERSION_STR[] = {'t', 'e', 's', 't'};

// @ref ReceiveData will read data from `storage`.
buffer = storage;
header = reinterpret_cast<MessageHeader*>(buffer);
buffer += sizeof(MessageHeader);
*header = MessageHeader();

header->sequence_number = 0;
header->message_type = MessageType::VERSION_INFO;
header->payload_size_bytes = sizeof(VersionInfoMessage) + sizeof(VERSION_STR);

auto version_message = reinterpret_cast<VersionInfoMessage*>(buffer);
*version_message = VersionInfoMessage();
version_message->fw_version_length = sizeof(VERSION_STR);
buffer += sizeof(VersionInfoMessage);

char* version_str_ptr = reinterpret_cast<char*>(buffer);
// NOTE: Not NULL terminated.
memcpy(version_str_ptr, VERSION_STR, sizeof(VERSION_STR));

header->crc = CalculateCRC(storage);

//////////////////////////////////////////////////////////////////////////////
// Receive example response.
//////////////////////////////////////////////////////////////////////////////

printf("Waiting for response\n");
// In a real application, you'd need to do the bookkeeping to trigger a
// timeout if no response is received after a couple seconds.
bool has_timed_out = false;
uint8_t read_buffer[10];

FusionEngineFramer framer(1024);
framer.SetMessageCallback(
[](const MessageHeader& header, const void* payload) {
// Ignore messages besides the expected response type.
if (header.message_type == VersionInfoMessage::MESSAGE_TYPE) {
PrintMessage(header, payload);
message_found = true;
}
});

while (!has_timed_out && !message_found) {
size_t data_read = ReceiveData(read_buffer, sizeof(read_buffer));
framer.OnData(read_buffer, data_read);
}

printf("Response received.\n");

return 0;
}

0 comments on commit b37a46e

Please sign in to comment.