forked from pz4kybsvg/Conception
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
163 lines
5.2 KiB
163 lines
5.2 KiB
#include "drake/common/proto/call_python.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <cstring>
|
|
#include <fstream>
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <vector>
|
|
|
|
#include "drake/common/drake_assert.h"
|
|
#include "drake/common/never_destroyed.h"
|
|
#include "drake/common/proto/rpc_pipe_temp_directory.h"
|
|
|
|
namespace drake {
|
|
namespace common {
|
|
|
|
static int py_globally_unique_id = 0;
|
|
|
|
PythonRemoteVariable::PythonRemoteVariable()
|
|
: unique_id_(py_globally_unique_id++)
|
|
{}
|
|
|
|
namespace internal {
|
|
|
|
// Copies `size` bytes at `data` into `message->data`.
|
|
void CopyBytes(const void* bytes, const int size,
|
|
lcmt_call_python_data* message) {
|
|
message->num_bytes = size;
|
|
message->data.resize(size);
|
|
std::memcpy(message->data.data(), bytes, size);
|
|
}
|
|
|
|
void ToPythonRemoteData(const PythonRemoteVariable& variable,
|
|
lcmt_call_python_data* message) {
|
|
message->data_type = lcmt_call_python_data::REMOTE_VARIABLE_REFERENCE;
|
|
message->shape_type = lcmt_call_python_data::SCALAR;
|
|
message->rows = 1;
|
|
message->cols = 1;
|
|
int64_t uid = variable.unique_id();
|
|
CopyBytes(&uid, sizeof(uid), message);
|
|
}
|
|
|
|
void ToPythonRemoteData(double scalar, lcmt_call_python_data* message) {
|
|
message->data_type = lcmt_call_python_data::DOUBLE;
|
|
message->shape_type = lcmt_call_python_data::SCALAR;
|
|
message->rows = 1;
|
|
message->cols = 1;
|
|
CopyBytes(&scalar, sizeof(scalar), message);
|
|
}
|
|
|
|
void ToPythonRemoteData(int scalar, lcmt_call_python_data* message) {
|
|
message->data_type = lcmt_call_python_data::INT;
|
|
message->shape_type = lcmt_call_python_data::SCALAR;
|
|
message->rows = 1;
|
|
message->cols = 1;
|
|
CopyBytes(&scalar, sizeof(scalar), message);
|
|
}
|
|
|
|
void ToPythonRemoteData(const std::string& str,
|
|
lcmt_call_python_data* message) {
|
|
message->data_type = lcmt_call_python_data::CHAR;
|
|
message->shape_type = lcmt_call_python_data::VECTOR;
|
|
message->rows = 1;
|
|
message->cols = str.length();
|
|
CopyBytes(str.data(), str.size(), message);
|
|
}
|
|
|
|
void ToPythonRemoteDataMatrix(const Eigen::Ref<const MatrixX<bool>>& mat,
|
|
lcmt_call_python_data* message, bool is_vector) {
|
|
message->data_type = lcmt_call_python_data::LOGICAL;
|
|
message->shape_type =
|
|
is_vector ? lcmt_call_python_data::VECTOR : lcmt_call_python_data::MATRIX;
|
|
message->rows = mat.rows();
|
|
message->cols = mat.cols();
|
|
int num_bytes = sizeof(bool) * mat.rows() * mat.cols();
|
|
CopyBytes(mat.data(), num_bytes, message);
|
|
}
|
|
|
|
void ToPythonRemoteDataMatrix(const Eigen::Ref<const Eigen::MatrixXd>& mat,
|
|
lcmt_call_python_data* message, bool is_vector) {
|
|
message->data_type = lcmt_call_python_data::DOUBLE;
|
|
message->shape_type =
|
|
is_vector ? lcmt_call_python_data::VECTOR : lcmt_call_python_data::MATRIX;
|
|
message->rows = mat.rows();
|
|
message->cols = mat.cols();
|
|
int num_bytes = sizeof(double) * mat.rows() * mat.cols();
|
|
CopyBytes(mat.data(), num_bytes, message);
|
|
}
|
|
|
|
void ToPythonRemoteDataMatrix(
|
|
const Eigen::Ref<const Eigen::MatrixXi>& mat,
|
|
lcmt_call_python_data* message, bool is_vector) {
|
|
message->data_type = lcmt_call_python_data::INT;
|
|
message->shape_type =
|
|
is_vector ? lcmt_call_python_data::VECTOR : lcmt_call_python_data::MATRIX;
|
|
message->rows = mat.rows();
|
|
message->cols = mat.cols();
|
|
int num_bytes = sizeof(int) * mat.rows() * mat.cols();
|
|
CopyBytes(mat.data(), num_bytes, message);
|
|
}
|
|
|
|
} // namespace internal
|
|
|
|
namespace {
|
|
|
|
// Latch-initialize the ofstream output that writes to the python client.
|
|
// The return value is a long-lived pointer to a singleton.
|
|
std::ofstream* InitOutput(const std::optional<std::string>& filename) {
|
|
static never_destroyed<std::unique_ptr<std::ofstream>> raw_output;
|
|
if (!raw_output.access()) {
|
|
// If we do not yet have a file, create it.
|
|
const std::string filename_default
|
|
= GetRpcPipeTempDirectory() + "/python_rpc";
|
|
const std::string filename_actual = filename ? *filename : filename_default;
|
|
raw_output.access() = std::make_unique<std::ofstream>(filename_actual);
|
|
} else {
|
|
// If we already have a file, ensure that this does not come from
|
|
// `CallPythonInit`.
|
|
if (filename) {
|
|
throw std::runtime_error(
|
|
"`CallPython` or `CallPythonInit` has already been called");
|
|
}
|
|
}
|
|
return raw_output.access().get();
|
|
}
|
|
|
|
void PublishCall(std::ofstream* stream_arg, const lcmt_call_python& message) {
|
|
DRAKE_DEMAND(stream_arg != nullptr);
|
|
std::ofstream& stream = *stream_arg;
|
|
|
|
const int num_bytes = message.getEncodedSize();
|
|
DRAKE_DEMAND(num_bytes >= 0);
|
|
const size_t size_bytes = static_cast<size_t>(num_bytes);
|
|
std::vector<uint8_t> encoded(size_bytes);
|
|
message.encode(encoded.data(), 0, num_bytes);
|
|
|
|
stream << size_bytes;
|
|
stream << '\0';
|
|
const void* const data = encoded.data();
|
|
stream.write(static_cast<const char*>(data), encoded.size());
|
|
stream << '\0';
|
|
|
|
stream.flush();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void CallPythonInit(const std::string& filename) {
|
|
InitOutput(filename);
|
|
}
|
|
|
|
void internal::PublishCallPython(const lcmt_call_python& message) {
|
|
static const never_destroyed<std::ofstream*> output{InitOutput(std::nullopt)};
|
|
PublishCall(output.access(), message);
|
|
}
|
|
|
|
} // namespace common
|
|
} // namespace drake
|