Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FUS-1598] Add C++ request_version example. #270

Merged
merged 4 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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));
axlan marked this conversation as resolved.
Show resolved Hide resolved
// 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;
}
Loading