mirror of
https://github.com/nasa/trick.git
synced 2024-12-18 20:57:55 +00:00
Parser and test for var_binary (#1425)
This commit is contained in:
parent
a8daf4514c
commit
97cf685b6a
5
.gitignore
vendored
5
.gitignore
vendored
@ -37,4 +37,7 @@ civet_server_error.log
|
||||
server.pem
|
||||
archive/
|
||||
.venv
|
||||
trickops_logs/
|
||||
trickops_logs/
|
||||
*.gcda
|
||||
*.gcno
|
||||
coverage.info
|
22
Makefile
22
Makefile
@ -96,13 +96,16 @@ UTILS_DIRS := \
|
||||
${TRICK_HOME}/trick_source/trick_utils/shm \
|
||||
${TRICK_HOME}/trick_source/trick_utils/math \
|
||||
${TRICK_HOME}/trick_source/trick_utils/units \
|
||||
${TRICK_HOME}/trick_source/trick_utils/unicode
|
||||
${TRICK_HOME}/trick_source/trick_utils/unicode \
|
||||
${TRICK_HOME}/trick_source/trick_utils/var_binary_parser
|
||||
|
||||
UTILS_OBJS := $(addsuffix /object_$(TRICK_HOST_CPU)/*.o ,$(UTILS_DIRS))
|
||||
|
||||
# filter out the directories that make their own libraries
|
||||
UTILS_OBJS := $(filter-out ${TRICK_HOME}/trick_source/trick_utils/comm/%, $(UTILS_OBJS))
|
||||
UTILS_OBJS := $(filter-out ${TRICK_HOME}/trick_source/trick_utils/math/%, $(UTILS_OBJS))
|
||||
UTILS_OBJS := $(filter-out ${TRICK_HOME}/trick_source/trick_utils/units/%, $(UTILS_OBJS))
|
||||
UTILS_OBJS := $(filter-out ${TRICK_HOME}/trick_source/trick_utils/var_binary_parser/%, $(UTILS_OBJS))
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Specify the contents of: libtrick_pyip.a
|
||||
@ -306,9 +309,20 @@ sim_test:
|
||||
pytest:
|
||||
make -C share/trick/pymods/trick
|
||||
|
||||
code-coverage: test
|
||||
lcov --capture --directory trick_source/sim_services --output-file coverage_large.info
|
||||
lcov --remove coverage_large.info '/Library/*' '/usr/*' '*/io_src/*' '*/test/*' -o coverage.info
|
||||
COVERAGE_DIRS = trick_source/sim_services \
|
||||
trick_source/trick_utils/var_binary_parser \
|
||||
trick_source/trick_utils/unicode \
|
||||
trick_source/trick_utils/units \
|
||||
trick_source/trick_utils/interpolator \
|
||||
trick_source/trick_utils/comm \
|
||||
trick_source/trick_utils/SAIntegrator
|
||||
|
||||
extra-coverage-builds:
|
||||
@ $(MAKE) test -C trick_source/trick_utils/SAIntegrator
|
||||
|
||||
code-coverage: test extra-coverage-builds
|
||||
lcov --capture $(addprefix --directory , $(COVERAGE_DIRS)) --output-file coverage_large.info
|
||||
lcov --remove coverage_large.info '/Library/*' '/usr/*' '*/io_src/*' '*/test/*' '*/unittest/*' -o coverage.info
|
||||
rm coverage_large.info
|
||||
lcov --list coverage.info
|
||||
|
||||
|
120
include/trick/var_binary_parser.hh
Normal file
120
include/trick/var_binary_parser.hh
Normal file
@ -0,0 +1,120 @@
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <exception>
|
||||
|
||||
#include "trick/parameter_types.h"
|
||||
|
||||
class IncorrectUsageException : public std::exception
|
||||
{
|
||||
private:
|
||||
std::string _message;
|
||||
public:
|
||||
IncorrectUsageException(std::string msg) : _message(msg) {}
|
||||
const char * what() const noexcept override { return _message.c_str(); }
|
||||
};
|
||||
|
||||
class ParseTypeException : public IncorrectUsageException
|
||||
{
|
||||
public:
|
||||
ParseTypeException() : IncorrectUsageException("Mismatched trick type and template call") {}
|
||||
};
|
||||
|
||||
class MalformedMessageException : public std::exception
|
||||
{
|
||||
private:
|
||||
std::string _message;
|
||||
public:
|
||||
MalformedMessageException() : _message("Attempted to parse an incorrectly formed message") {}
|
||||
MalformedMessageException(std::string msg) : _message(msg) {}
|
||||
const char * what() const noexcept override { return _message.c_str(); }
|
||||
};
|
||||
|
||||
typedef union Number {
|
||||
unsigned char raw_bytes[sizeof(long long)]; // Raw bytes
|
||||
|
||||
uint8_t unsigned_char_val;
|
||||
int8_t char_val;
|
||||
bool bool_val;
|
||||
|
||||
wchar_t wchar_val;
|
||||
uint16_t unsigned_short_val;
|
||||
int16_t short_val;
|
||||
|
||||
uint32_t unsigned_int_val;
|
||||
int32_t int_val;
|
||||
|
||||
uint64_t unsigned_long_val;
|
||||
int64_t long_val;
|
||||
|
||||
unsigned long long unsigned_long_long_val;
|
||||
long long long_long_val;
|
||||
|
||||
float float_val;
|
||||
double double_val;
|
||||
} Number;
|
||||
|
||||
|
||||
class Var {
|
||||
public:
|
||||
Var () : _has_name(false) {};
|
||||
void setValue(const std::vector<unsigned char>& bytes, size_t size, TRICK_TYPE type, bool byteswap = false);
|
||||
void setName(size_t name_size, const std::vector<unsigned char>& name_data);
|
||||
|
||||
// The closest to runtime return type polymorphism that I can think of
|
||||
// There won't be a general case
|
||||
template <typename T>
|
||||
T getValue() const;
|
||||
|
||||
std::string getName() const;
|
||||
TRICK_TYPE getType() const;
|
||||
|
||||
private:
|
||||
std::vector<unsigned char> value_bytes;
|
||||
Number getInterpreter () const;
|
||||
|
||||
bool _has_name;
|
||||
unsigned int _name_length;
|
||||
std::string _name;
|
||||
|
||||
bool _byteswap;
|
||||
|
||||
TRICK_TYPE _trick_type;
|
||||
unsigned int _var_size;
|
||||
};
|
||||
|
||||
class ParsedBinaryMessage {
|
||||
public:
|
||||
ParsedBinaryMessage() : _byteswap(false), _nonames(false) {}
|
||||
ParsedBinaryMessage (bool byteswap, bool nonames) : _byteswap(byteswap), _nonames(nonames) {}
|
||||
|
||||
void combine (const ParsedBinaryMessage& message);
|
||||
|
||||
int parse (const std::vector<unsigned char>& bytes);
|
||||
|
||||
int getMessageType() const;
|
||||
unsigned int getMessageSize() const;
|
||||
unsigned int getNumVars() const;
|
||||
Var getVariable(const std::string& name);
|
||||
Var getVariable(unsigned int index);
|
||||
|
||||
std::vector<Var> variables;
|
||||
|
||||
private:
|
||||
static bool validateMessageType(int message_type);
|
||||
|
||||
int _message_type;
|
||||
unsigned int _message_size;
|
||||
unsigned int _num_vars;
|
||||
|
||||
bool _byteswap;
|
||||
bool _nonames;
|
||||
|
||||
const static size_t header_size;
|
||||
const static size_t message_indicator_size;
|
||||
const static size_t variable_num_size;
|
||||
const static size_t message_size_size;
|
||||
const static size_t variable_name_length_size;
|
||||
const static size_t variable_type_size;
|
||||
const static size_t variable_size_size;
|
||||
};
|
@ -13,7 +13,9 @@ typedef enum {
|
||||
VS_SIE_RESOURCE = 2,
|
||||
VS_LIST_SIZE = 3 ,
|
||||
VS_STDIO = 4,
|
||||
VS_SEND_ONCE = 5
|
||||
VS_SEND_ONCE = 5,
|
||||
VS_MIN_CODE = VS_IP_ERROR,
|
||||
VS_MAX_CODE = VS_SEND_ONCE
|
||||
} VS_MESSAGE_TYPE ;
|
||||
|
||||
#endif
|
||||
|
@ -1,13 +1,14 @@
|
||||
|
||||
TRICK_CFLAGS += -I./models
|
||||
TRICK_CXXFLAGS += -I./models -I$(GTEST_HOME)/include -std=c++11
|
||||
TRICK_CXXFLAGS += -I./models
|
||||
|
||||
all: test_client
|
||||
clean: clean_test_client
|
||||
|
||||
TEST_CLIENT_LIBS += -L${GTEST_HOME}lib64 -L${GTEST_HOME}lib -lgtest -lgtest_main -lpthread
|
||||
TEST_CLIENT_LIBS += -L${GTEST_HOME}/lib64 -L${GTEST_HOME}/lib -lgtest -lgtest_main -lpthread -L${TRICK_LIB_DIR} -ltrick_var_binary_parser
|
||||
|
||||
test_client: models/test_client/test_client.cpp
|
||||
cd models/test_client; $(TRICK_CXX) test_client.cpp $(TRICK_SYSTEM_LDFLAGS) $(TRICK_CXXFLAGS) -o test_client $(TEST_CLIENT_LIBS) -std=c++11
|
||||
test_client: models/test_client/test_client.cpp
|
||||
cd models/test_client; $(TRICK_CXX) test_client.cpp -o test_client $(TRICK_CXXFLAGS) -I$(TRICK_HOME)/include $(TEST_CLIENT_LIBS) -std=c++11
|
||||
|
||||
clean_test_client:
|
||||
rm -f models/test_client/test_client
|
||||
rm -f models/test_client/test_client
|
@ -13,6 +13,8 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "trick/var_binary_parser.hh"
|
||||
|
||||
#define SOCKET_BUF_SIZE 20480
|
||||
|
||||
#define DOUBLE_TOL 1e-5
|
||||
@ -92,6 +94,21 @@ class Socket {
|
||||
ret = receive();
|
||||
}
|
||||
|
||||
std::vector<unsigned char> receive_bytes() {
|
||||
unsigned char buffer[SOCKET_BUF_SIZE];
|
||||
int numBytes = recv(_socket_fd, buffer, SOCKET_BUF_SIZE, 0);
|
||||
if (numBytes < 0) {
|
||||
std::cout << "Failed to read from socket" << std::endl;
|
||||
}
|
||||
|
||||
std::vector<unsigned char> bytes;
|
||||
for (int i = 0; i < numBytes; i++) {
|
||||
bytes.push_back(buffer[i]);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
bool check_for_message_availible(long timeout_sec = 2) {
|
||||
fd_set read_fd_set;
|
||||
struct timeval timeout = { .tv_sec = timeout_sec, .tv_usec = 0 };
|
||||
@ -581,9 +598,6 @@ TEST_F (VariableServerTest, CopyAndWriteModes) {
|
||||
// Is this what we want? Maybe we should have more strict communication on whether the data has been staged so the first message isn't incorrect
|
||||
spin();
|
||||
|
||||
// expected = "0 -1234 1234";
|
||||
// EXPECT_EQ(strcmp_IgnoringWhiteSpace(reply, expected), 0);
|
||||
// std::cout << "\tExpected: " << expected << "\n\tActual: " << reply << std::endl;
|
||||
expected = "-1234 1234";
|
||||
parse_message(socket.receive());
|
||||
EXPECT_EQ(strcmp_IgnoringWhiteSpace(vars, expected), 0) << "Received: " << vars << " Expected: " << expected;
|
||||
@ -683,6 +697,163 @@ TEST_F (VariableServerTest, CopyAndWriteModes) {
|
||||
socket.clear_buffered_data();
|
||||
}
|
||||
|
||||
bool getCompleteBinaryMessage(ParsedBinaryMessage& message, Socket& socket) {
|
||||
static const int max_retries = 5;
|
||||
|
||||
int tries = 0;
|
||||
bool parse_success = false;
|
||||
std::vector<unsigned char> reply;
|
||||
|
||||
// Try parsing until message received is correct
|
||||
// May take a cycle or 2 to update properly
|
||||
while (!parse_success && tries++ < max_retries) {
|
||||
try {
|
||||
reply = socket.receive_bytes();
|
||||
message.parse(reply);
|
||||
parse_success = true;
|
||||
} catch (const MalformedMessageException& ex) {
|
||||
std::cout << "Attempt " << tries << " failed with exception " << ex.what() << std::endl;;
|
||||
}
|
||||
}
|
||||
|
||||
return parse_success;
|
||||
}
|
||||
|
||||
TEST_F (VariableServerTest, Binary) {
|
||||
if (socket_status != 0) {
|
||||
FAIL();
|
||||
}
|
||||
std::vector<unsigned char> reply;
|
||||
socket << "trick.var_binary()\ntrick.var_add(\"vsx.vst.c\")\ntrick.var_add(\"vsx.vst.k\")\ntrick.var_add(\"vsx.vst.o\")\ntrick.var_add(\"vsx.vst.m\")\n";
|
||||
|
||||
ParsedBinaryMessage message;
|
||||
|
||||
if (!getCompleteBinaryMessage(message, socket)) {
|
||||
FAIL() << "Parser was not able to interpret the message.";
|
||||
}
|
||||
|
||||
EXPECT_EQ(message.getMessageType(), 0);
|
||||
ASSERT_EQ(message.getNumVars(), 4);
|
||||
|
||||
EXPECT_EQ(strcmp(message.variables[0].getName().c_str(), "vsx.vst.c"), 0);
|
||||
EXPECT_EQ(message.variables[0].getType(), TRICK_SHORT);
|
||||
EXPECT_EQ(message.variables[0].getValue<short>(), -1234);
|
||||
|
||||
|
||||
EXPECT_EQ(strcmp(message.variables[1].getName().c_str(), "vsx.vst.k"), 0);
|
||||
EXPECT_EQ(message.variables[1].getType(), TRICK_LONG_LONG);
|
||||
EXPECT_EQ(message.variables[1].getValue<long long>(), -12345678912345);
|
||||
|
||||
EXPECT_EQ(strcmp(message.variables[2].getName().c_str(), "vsx.vst.o"), 0);
|
||||
EXPECT_EQ(message.variables[2].getType(), TRICK_STRING);
|
||||
std::string actual = message.variables[2].getValue<std::string>();
|
||||
std::string expected = "You will rejoice to hear that no disaster has accompanied the commencement of an enterprise which you have regarded with such evil forebodings. I arrived here yesterday, and my first task is to assure my dear sister of my welfare and increasing confidence in the success of my undertaking.\0";
|
||||
EXPECT_EQ(actual.substr(0, actual.size()-1), expected);
|
||||
|
||||
Var variable = message.getVariable("vsx.vst.m");
|
||||
EXPECT_EQ(strcmp(variable.getName().c_str(), "vsx.vst.m"), 0);
|
||||
EXPECT_EQ(variable.getType(), TRICK_BOOLEAN);
|
||||
EXPECT_EQ(variable.getValue<bool>(), true);
|
||||
|
||||
// Switch to nonames
|
||||
socket << "trick.var_binary_nonames()\n";
|
||||
|
||||
ParsedBinaryMessage noname_message(false, true);
|
||||
if (!getCompleteBinaryMessage(noname_message, socket)) {
|
||||
FAIL() << "Parser was not able to interpret the message.";
|
||||
}
|
||||
|
||||
EXPECT_EQ(noname_message.getMessageType(), 0);
|
||||
ASSERT_EQ(noname_message.getNumVars(), 4);
|
||||
|
||||
EXPECT_EQ(strcmp(noname_message.variables[0].getName().c_str(), "<no name>"), 0);
|
||||
EXPECT_EQ(noname_message.variables[0].getType(), TRICK_SHORT);
|
||||
EXPECT_EQ(noname_message.variables[0].getValue<short>(), -1234);
|
||||
|
||||
EXPECT_EQ(strcmp(noname_message.variables[1].getName().c_str(), "<no name>"), 0);
|
||||
EXPECT_EQ(noname_message.variables[1].getType(), TRICK_LONG_LONG);
|
||||
EXPECT_EQ(noname_message.variables[1].getValue<long long>(), -12345678912345);
|
||||
|
||||
EXPECT_EQ(strcmp(noname_message.variables[2].getName().c_str(), "<no name>"), 0);
|
||||
EXPECT_EQ(noname_message.variables[2].getType(), TRICK_STRING);
|
||||
actual = noname_message.variables[2].getValue<std::string>();
|
||||
EXPECT_EQ(actual.substr(0, actual.size()-1), expected);
|
||||
|
||||
Var variable_noname = noname_message.getVariable(3);
|
||||
EXPECT_EQ(strcmp(variable_noname.getName().c_str(), "<no name>"), 0);
|
||||
EXPECT_EQ(variable_noname.getType(), TRICK_BOOLEAN);
|
||||
EXPECT_EQ(variable_noname.getValue<bool>(), true);
|
||||
}
|
||||
|
||||
TEST_F (VariableServerTest, DISABLED_BinaryByteswap) {
|
||||
// TODO: VAR_BYTESWAP DOES NOT APPEAR TO WORK CORRECTLY
|
||||
|
||||
std::vector<unsigned char> reply;
|
||||
socket << "trick.var_binary()\ntrick.var_byteswap(False)\ntrick.var_add(\"vsx.vst.f\")\ntrick.var_add(\"vsx.vst.j\")\n";
|
||||
|
||||
socket.receive_bytes();
|
||||
reply = socket.receive_bytes();
|
||||
|
||||
// Test byteswap
|
||||
|
||||
std::cout << "Message: ";
|
||||
for (unsigned char byte : reply) {
|
||||
std::cout << "0x" << std::setw(2) << std::setfill('0') << std::hex << (int)byte << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
ParsedBinaryMessage message;
|
||||
|
||||
try {
|
||||
message.parse(reply);
|
||||
} catch (const MalformedMessageException& ex) {
|
||||
FAIL() << "Parser threw an exception: " << ex.what();
|
||||
}
|
||||
|
||||
EXPECT_EQ(message.getMessageType(), 0);
|
||||
ASSERT_EQ(message.getNumVars(), 2);
|
||||
|
||||
EXPECT_EQ(strcmp(message.variables[0].getName().c_str(), "vsx.vst.f"), 0);
|
||||
EXPECT_EQ(message.variables[0].getType(), TRICK_UNSIGNED_INTEGER);
|
||||
EXPECT_EQ(message.variables[0].getValue<unsigned int>(), 123456);
|
||||
|
||||
EXPECT_EQ(strcmp(message.variables[1].getName().c_str(), "vsx.vst.j"), 0);
|
||||
EXPECT_EQ(message.variables[1].getType(), TRICK_DOUBLE);
|
||||
EXPECT_EQ(message.variables[1].getValue<double>(), -1234.567890);
|
||||
|
||||
socket << "trick.var_byteswap(True)\n";
|
||||
|
||||
socket.receive_bytes();
|
||||
reply = socket.receive_bytes();
|
||||
|
||||
std::cout << "Message: ";
|
||||
for (unsigned char byte : reply) {
|
||||
std::cout << "0x" << std::setw(2) << std::setfill('0') << std::hex << (int)byte << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
ParsedBinaryMessage byteswap_message(true, false);
|
||||
|
||||
try {
|
||||
byteswap_message.parse(reply);
|
||||
} catch (const MalformedMessageException& ex) {
|
||||
FAIL() << "Parser threw an exception: " << ex.what();
|
||||
}
|
||||
|
||||
EXPECT_EQ(byteswap_message.getMessageType(), 0);
|
||||
ASSERT_EQ(byteswap_message.getNumVars(), 2);
|
||||
|
||||
EXPECT_EQ(strcmp(byteswap_message.variables[0].getName().c_str(), "vsx.vst.f"), 0);
|
||||
EXPECT_EQ(byteswap_message.variables[0].getType(), TRICK_UNSIGNED_INTEGER);
|
||||
EXPECT_EQ(byteswap_message.variables[0].getValue<unsigned int>(), 123456);
|
||||
std::cout << "Byteswap value: " << byteswap_message.variables[0].getValue<unsigned int>() << std::endl;
|
||||
|
||||
EXPECT_EQ(strcmp(byteswap_message.variables[1].getName().c_str(), "vsx.vst.j"), 0);
|
||||
EXPECT_EQ(byteswap_message.variables[1].getType(), TRICK_DOUBLE);
|
||||
EXPECT_EQ(byteswap_message.variables[1].getValue<double>(), -1234.567890);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
int result = RUN_ALL_TESTS();
|
||||
|
@ -3,7 +3,7 @@ RM = rm -rf
|
||||
CC = cc
|
||||
CPP = c++
|
||||
|
||||
CFLAGS = -g -Wall -std=c++11
|
||||
CFLAGS = -g -Wall -std=c++11 ${TRICK_CXXFLAGS}
|
||||
INCLUDE_DIRS = -Iinclude
|
||||
OBJDIR = obj
|
||||
LIBDIR = lib
|
||||
|
@ -3,7 +3,7 @@ include ${TRICK_HOME}/share/trick/makefiles/Makefile.common
|
||||
|
||||
RM = rm -rf
|
||||
|
||||
TRICK_CPPFLAGS += -I$(GTEST_HOME)/include -I$(TRICK_HOME)/include -g -Wall -Wextra -std=c++11 ${TRICK_SYSTEM_CXXFLAGS}
|
||||
TRICK_CPPFLAGS += -I$(GTEST_HOME)/include -I$(TRICK_HOME)/include -g -Wall -Wextra -std=c++11 ${TRICK_CXXFLAGS}
|
||||
INCLUDE_DIRS = -I../include -I$(GTEST_HOME)/include
|
||||
|
||||
SAI_OBJDIR = obj
|
||||
|
2
trick_source/trick_utils/var_binary_parser/.gitignore
vendored
Normal file
2
trick_source/trick_utils/var_binary_parser/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.o
|
||||
TEST_var_binary_parser
|
52
trick_source/trick_utils/var_binary_parser/Makefile
Normal file
52
trick_source/trick_utils/var_binary_parser/Makefile
Normal file
@ -0,0 +1,52 @@
|
||||
RM = rm -rf
|
||||
CC = cc
|
||||
CPP = c++
|
||||
include ${TRICK_HOME}/share/trick/makefiles/Makefile.common
|
||||
|
||||
|
||||
#include ${TRICK_HOME}/share/trick/makefiles/Makefile.tricklib
|
||||
|
||||
CFLAGS = -g -Wall -Wextra -std=c++11 $(TRICK_CXXFLAGS)
|
||||
INCLUDE_DIRS = -I${TRICK_HOME}/include
|
||||
OBJDIR = obj
|
||||
LIBDIR = lib
|
||||
LIBNAME = libtrick_var_binary_parser.a
|
||||
LIBOBJS = ${OBJDIR}/var_binary_parser.o
|
||||
|
||||
TRICK_LIB := $(TRICK_LIB_DIR)/$(LIBNAME)
|
||||
# TRICK_LIB := $(TRICK_HOME)/lib/$(LIBNAME)
|
||||
|
||||
|
||||
trick: ${TRICK_LIB}
|
||||
|
||||
install: ${TRICK_LIB}
|
||||
|
||||
all: test
|
||||
|
||||
test: ${LIBDIR}/${LIBNAME}
|
||||
${MAKE} -C test
|
||||
|
||||
$(LIBOBJS): $(OBJDIR)/%.o : src/%.cc | $(OBJDIR)
|
||||
$(CPP) $(CFLAGS) ${INCLUDE_DIRS} -c $< -o $@
|
||||
|
||||
${LIBDIR}/${LIBNAME}: ${LIBOBJS} | ${LIBDIR}
|
||||
ar crs $@ ${LIBOBJS}
|
||||
|
||||
${OBJDIR}:
|
||||
mkdir -p ${OBJDIR}
|
||||
|
||||
${LIBDIR}:
|
||||
mkdir -p ${LIBDIR}
|
||||
|
||||
${TRICK_LIB}: ${LIBDIR}/${LIBNAME}
|
||||
cp ${LIBDIR}/${LIBNAME} $(TRICK_LIB)
|
||||
|
||||
clean:
|
||||
${RM} -r ${OBJDIR}
|
||||
${MAKE} -C test clean
|
||||
|
||||
spotless:
|
||||
${RM} ${OBJDIR}
|
||||
${RM} ${LIBDIR}
|
||||
${MAKE} -C unittest spotless
|
||||
${MAKE} -C examples spotless
|
@ -0,0 +1,364 @@
|
||||
#include "trick/var_binary_parser.hh"
|
||||
#include "trick/variable_server_message_types.h"
|
||||
#include <climits>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
// Utility method
|
||||
uint64_t bytesToInt(const std::vector<unsigned char>& bytes, bool byteswap) {
|
||||
uint64_t result = 0;
|
||||
|
||||
if (byteswap) {
|
||||
for (unsigned int i = 0; i < bytes.size(); i++) {
|
||||
result |= bytes[bytes.size() - 1 - i] << i*8;
|
||||
}
|
||||
} else {
|
||||
for (unsigned int i = 0; i < bytes.size(); i++) {
|
||||
result |= bytes[i] << i*8;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
class MessageIterator {
|
||||
public:
|
||||
MessageIterator(const std::vector<unsigned char>& container) : _container(container), _index(0) {}
|
||||
|
||||
std::vector<unsigned char> slice(unsigned int length) {
|
||||
unsigned int slice_end = _index + length;
|
||||
if (_index > _container.size() || slice_end > _container.size()) {
|
||||
throw MalformedMessageException("Message ends unexpectedly");
|
||||
}
|
||||
|
||||
return std::vector<unsigned char>(_container.begin() + _index, _container.begin() + slice_end);
|
||||
}
|
||||
|
||||
void operator+= (int n) {
|
||||
_index += n;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<unsigned char> _container;
|
||||
unsigned int _index;
|
||||
};
|
||||
|
||||
/**************************************************************************
|
||||
* Var implementation
|
||||
**************************************************************************/
|
||||
|
||||
const size_t ParsedBinaryMessage::header_size = 12;
|
||||
const size_t ParsedBinaryMessage::message_indicator_size = 4;
|
||||
const size_t ParsedBinaryMessage::variable_num_size = 4;
|
||||
const size_t ParsedBinaryMessage::message_size_size = 4;
|
||||
const size_t ParsedBinaryMessage::variable_name_length_size = 4;
|
||||
const size_t ParsedBinaryMessage::variable_type_size = 4;
|
||||
const size_t ParsedBinaryMessage::variable_size_size = 4;
|
||||
|
||||
int ParsedBinaryMessage::parse (const std::vector<unsigned char>& bytes){
|
||||
if (bytes.size() < header_size) {
|
||||
throw MalformedMessageException(std::string("Not enough bytes in message to contain header: expected at least 12, got " + std::to_string(bytes.size())));
|
||||
}
|
||||
|
||||
MessageIterator messageIterator (bytes);
|
||||
|
||||
// First 4 bytes is message type
|
||||
_message_type = bytesToInt(messageIterator.slice(message_indicator_size), _byteswap);
|
||||
if (!validateMessageType(_message_type)) {
|
||||
throw MalformedMessageException(std::string("Received unknown message type: " + std::to_string(_message_type)));
|
||||
}
|
||||
|
||||
messageIterator += message_indicator_size;
|
||||
|
||||
// Next 4 bytes is message size
|
||||
_message_size = bytesToInt(messageIterator.slice(message_size_size), _byteswap);
|
||||
if (bytes.size() - message_indicator_size != _message_size) {
|
||||
std::string error_message = "Message size in header (" + std::to_string(_message_size) + ") does not match size of message received (" + std::to_string(bytes.size() - message_indicator_size) + ")";
|
||||
throw MalformedMessageException(error_message);
|
||||
}
|
||||
|
||||
messageIterator += message_size_size;
|
||||
|
||||
// Next 4 bytes is number of variables
|
||||
_num_vars = bytesToInt(messageIterator.slice(variable_num_size), _byteswap);
|
||||
messageIterator += variable_num_size;
|
||||
|
||||
// Pull out all of the variables
|
||||
for (unsigned int i = 0; i < _num_vars; i++) {
|
||||
Var variable;
|
||||
|
||||
if (!_nonames) {
|
||||
// Get the name
|
||||
size_t name_length = bytesToInt(messageIterator.slice(variable_name_length_size), _byteswap);
|
||||
messageIterator += variable_name_length_size;
|
||||
|
||||
variable.setName(name_length, messageIterator.slice(name_length));
|
||||
messageIterator += name_length;
|
||||
}
|
||||
|
||||
// Parse the type first
|
||||
int var_type = bytesToInt(messageIterator.slice(variable_type_size), _byteswap);
|
||||
messageIterator += variable_type_size;
|
||||
|
||||
size_t var_size = bytesToInt(messageIterator.slice(variable_size_size), _byteswap);
|
||||
messageIterator += variable_size_size;
|
||||
|
||||
variable.setValue(messageIterator.slice(var_size), var_size, static_cast<TRICK_TYPE>(var_type), _byteswap);
|
||||
messageIterator += var_size;
|
||||
|
||||
variables.emplace_back(variable);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ParsedBinaryMessage::combine (const ParsedBinaryMessage& other) {
|
||||
if (_message_type != other._message_type) {
|
||||
std::string error_message = "Trying to combine two messages with different message indicators (" + std::to_string(_message_type) + " and " + std::to_string(other._message_type) + ")";
|
||||
throw MalformedMessageException(error_message);
|
||||
}
|
||||
|
||||
// Combined size - subtract the header size from other message size
|
||||
_message_size += other._message_size - message_size_size - variable_num_size;
|
||||
|
||||
// Combine variables
|
||||
_num_vars += other._num_vars;
|
||||
variables.insert(variables.end(), other.variables.begin(), other.variables.end());
|
||||
|
||||
// Other error checking - duplicate variables?
|
||||
}
|
||||
|
||||
Var ParsedBinaryMessage::getVariable(const std::string& name) {
|
||||
if (_nonames)
|
||||
throw IncorrectUsageException("Cannot fetch variables by name in noname message");
|
||||
|
||||
for (auto variable : variables) {
|
||||
if (variable.getName() == name)
|
||||
return variable;
|
||||
}
|
||||
|
||||
throw IncorrectUsageException("Variable " + name + " does not exist in this message.");
|
||||
}
|
||||
|
||||
Var ParsedBinaryMessage::getVariable(unsigned int index) {
|
||||
if (index >= variables.size()) {
|
||||
throw IncorrectUsageException("Variable index " + std::to_string(index) + " does not exist in this message.");
|
||||
}
|
||||
|
||||
return variables[index];
|
||||
}
|
||||
|
||||
int ParsedBinaryMessage::getMessageType() const {
|
||||
return _message_type;
|
||||
}
|
||||
|
||||
unsigned int ParsedBinaryMessage::getMessageSize() const {
|
||||
return _message_size;
|
||||
}
|
||||
|
||||
unsigned int ParsedBinaryMessage::getNumVars() const {
|
||||
return _num_vars;
|
||||
}
|
||||
|
||||
// Static methods
|
||||
|
||||
bool ParsedBinaryMessage::validateMessageType(int message_type) {
|
||||
return message_type >= VS_MIN_CODE && message_type <= VS_MAX_CODE;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* Var implementation
|
||||
**************************************************************************/
|
||||
|
||||
|
||||
void Var::setName(size_t name_size, const std::vector<unsigned char>& name_data) {
|
||||
_has_name = true;
|
||||
_name_length = name_size;
|
||||
std::stringstream ss;
|
||||
|
||||
for (unsigned char character : name_data) {
|
||||
ss << static_cast<char>(character);
|
||||
}
|
||||
|
||||
_name = ss.str();
|
||||
}
|
||||
|
||||
|
||||
void Var::setValue(const std::vector<unsigned char>& bytes, size_t size, TRICK_TYPE type, bool byteswap) {
|
||||
_trick_type = type;
|
||||
_var_size = size;
|
||||
_byteswap = byteswap;
|
||||
|
||||
value_bytes = std::vector<unsigned char> (bytes);
|
||||
}
|
||||
|
||||
Number Var::getInterpreter() const {
|
||||
Number interpreted_val;
|
||||
|
||||
if (_byteswap) {
|
||||
for (unsigned int i = 0; i < _var_size; i++) {
|
||||
interpreted_val.raw_bytes[i] = value_bytes[_var_size - 1 - i];
|
||||
}
|
||||
} else {
|
||||
for (unsigned int i = 0; i < _var_size; i++) {
|
||||
interpreted_val.raw_bytes[i] = value_bytes[i];
|
||||
}
|
||||
}
|
||||
|
||||
return interpreted_val;
|
||||
}
|
||||
|
||||
std::string Var::getName() const {
|
||||
if (_has_name) {
|
||||
return _name;
|
||||
} else {
|
||||
return std::string ("<no name>");
|
||||
}
|
||||
}
|
||||
|
||||
TRICK_TYPE Var::getType() const {
|
||||
return _trick_type;
|
||||
}
|
||||
|
||||
|
||||
// Template specialization for each supported type
|
||||
template<>
|
||||
char Var::getValue<char> () const {
|
||||
if (_trick_type != TRICK_CHARACTER) {
|
||||
throw ParseTypeException();
|
||||
}
|
||||
|
||||
return getInterpreter().char_val;
|
||||
}
|
||||
|
||||
template<>
|
||||
unsigned char Var::getValue<unsigned char> () const {
|
||||
if (_trick_type != TRICK_UNSIGNED_CHARACTER) {
|
||||
throw ParseTypeException();
|
||||
}
|
||||
|
||||
return getInterpreter().unsigned_char_val;
|
||||
}
|
||||
|
||||
template<>
|
||||
bool Var::getValue<bool> () const {
|
||||
if (_trick_type != TRICK_BOOLEAN) {
|
||||
throw ParseTypeException();
|
||||
}
|
||||
|
||||
return getInterpreter().bool_val;
|
||||
}
|
||||
|
||||
template<>
|
||||
short Var::getValue<short> () const {
|
||||
if (_trick_type != TRICK_SHORT) {
|
||||
throw ParseTypeException();
|
||||
}
|
||||
|
||||
return getInterpreter().short_val;
|
||||
}
|
||||
|
||||
template<>
|
||||
unsigned short Var::getValue<unsigned short> () const {
|
||||
if (_trick_type != TRICK_UNSIGNED_SHORT) {
|
||||
throw ParseTypeException();
|
||||
}
|
||||
|
||||
return getInterpreter().unsigned_short_val;
|
||||
}
|
||||
|
||||
template<>
|
||||
unsigned int Var::getValue<unsigned int> () const {
|
||||
if (_trick_type != TRICK_UNSIGNED_INTEGER) {
|
||||
throw ParseTypeException();
|
||||
}
|
||||
|
||||
return getInterpreter().unsigned_int_val;
|
||||
}
|
||||
|
||||
template<>
|
||||
int Var::getValue<int> () const {
|
||||
if (_trick_type != TRICK_INTEGER) {
|
||||
throw ParseTypeException();
|
||||
}
|
||||
|
||||
return getInterpreter().int_val;
|
||||
}
|
||||
|
||||
template<>
|
||||
long Var::getValue<long> () const {
|
||||
if (_trick_type != TRICK_LONG) {
|
||||
throw ParseTypeException();
|
||||
}
|
||||
|
||||
return getInterpreter().long_val;
|
||||
}
|
||||
|
||||
template<>
|
||||
unsigned long Var::getValue<unsigned long> () const {
|
||||
if (_trick_type != TRICK_UNSIGNED_LONG) {
|
||||
throw ParseTypeException();
|
||||
}
|
||||
|
||||
return getInterpreter().unsigned_long_val;
|
||||
}
|
||||
|
||||
template<>
|
||||
long long Var::getValue<long long> () const {
|
||||
if (_trick_type != TRICK_LONG_LONG) {
|
||||
throw ParseTypeException();
|
||||
}
|
||||
|
||||
return getInterpreter().long_long_val;
|
||||
}
|
||||
|
||||
template<>
|
||||
unsigned long long Var::getValue<unsigned long long> () const {
|
||||
if (_trick_type != TRICK_UNSIGNED_LONG_LONG) {
|
||||
throw ParseTypeException();
|
||||
}
|
||||
|
||||
return getInterpreter().unsigned_long_long_val;
|
||||
}
|
||||
|
||||
template<>
|
||||
float Var::getValue<float> () const {
|
||||
if (_trick_type != TRICK_FLOAT) {
|
||||
throw ParseTypeException();
|
||||
}
|
||||
|
||||
return getInterpreter().float_val;
|
||||
}
|
||||
|
||||
template<>
|
||||
double Var::getValue<double> () const {
|
||||
if (_trick_type != TRICK_DOUBLE) {
|
||||
throw ParseTypeException();
|
||||
}
|
||||
|
||||
return getInterpreter().double_val;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string Var::getValue<std::string> () const {
|
||||
if (_trick_type != TRICK_STRING) {
|
||||
throw ParseTypeException();
|
||||
}
|
||||
|
||||
std::stringstream stream;
|
||||
for (unsigned char character : value_bytes) {
|
||||
stream << static_cast<char>(character);
|
||||
}
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
template <>
|
||||
wchar_t Var::getValue<wchar_t> () const {
|
||||
if (_trick_type != TRICK_WCHAR) {
|
||||
throw ParseTypeException();
|
||||
}
|
||||
|
||||
return getInterpreter().wchar_val;
|
||||
}
|
37
trick_source/trick_utils/var_binary_parser/test/Makefile
Normal file
37
trick_source/trick_utils/var_binary_parser/test/Makefile
Normal file
@ -0,0 +1,37 @@
|
||||
#SYNOPSIS:
|
||||
#
|
||||
## make [all] - makes everything.
|
||||
# make TARGET - makes the given target.
|
||||
# # make clean - removes all files generated by make.
|
||||
|
||||
# Flags passed to the preprocessor.
|
||||
CPPFLAGS += -g -Wall -Wextra -std=c++11 ${TRICK_SYSTEM_CXXFLAGS} -I../include -I${TRICK_HOME}/include
|
||||
|
||||
# TRICK_LIBS = ${TRICK_LIB_DIR}/libtrick_units.a
|
||||
EXEC_LINK_LIBS += -L${GTEST_HOME}/lib64 -L${GTEST_HOME}/lib -lgtest -lgtest_main
|
||||
|
||||
#Added pthreads for Ubuntu
|
||||
EXEC_LINK_LIBS += -lpthread
|
||||
|
||||
OTHER_OBJECTS = ../obj/*.o
|
||||
|
||||
TESTS = TEST_var_binary_parser
|
||||
|
||||
GTEST_HEADERS = $(GTEST_DIR)/include/gtest/*.h \
|
||||
$(GTEST_DIR)/include/gtest/internal/*.h
|
||||
|
||||
all : $(TESTS) test
|
||||
|
||||
test: $(TESTS)
|
||||
./TEST_var_binary_parser --gtest_output=xml:${TRICK_HOME}/trick_test/TEST_var_binary_parser.xml
|
||||
|
||||
clean :
|
||||
rm -f $(TESTS)
|
||||
rm -f *.o
|
||||
|
||||
TEST_var_binary_parser.o : TEST_var_binary_parser.cc $(OTHER_OBJECTS)
|
||||
$(CXX) $(CPPFLAGS) -c $<
|
||||
|
||||
TEST_var_binary_parser : TEST_var_binary_parser.o $(OTHER_OBJECTS)
|
||||
@echo 'Building TEST_var_binary_parser'
|
||||
$(CXX) $(TRICK_SYSTEM_LDFLAGS) -o $@ $^ -L${TRICK_HOME}/lib_${TRICK_HOST_CPU} $(LIBS) $(EXEC_LINK_LIBS)
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user