optoforce_daq.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. #include "optoforce_daq.h"
  2. #include "core/basic_obj_types.hpp"
  3. #include "utility/config_utility.hpp"
  4. #include "utility/coro_worker.hpp"
  5. #include "utility/coro_worker_helper_func.hpp"
  6. #include "utility/versatile_buffer2.hpp"
  7. #include <boost/asio/serial_port.hpp>
  8. #include <boost/asio/use_awaitable.hpp>
  9. #include <boost/crc.hpp>
  10. #include <boost/smart_ptr/scoped_ptr.hpp>
  11. #include <spdlog/spdlog.h>
  12. #include <numeric>
  13. namespace sophiar {
  14. using boost::asio::async_read;
  15. using boost::asio::async_write;
  16. using boost::asio::awaitable;
  17. using boost::asio::serial_port;
  18. using boost::asio::use_awaitable;
  19. using boost::scoped_ptr;
  20. struct optoforce_daq::impl {
  21. static constexpr auto daq_endian = boost::endian::order::big;
  22. static constexpr uint8_t header_magic_1[] = {170, 7, 8, 10};
  23. static constexpr uint8_t header_magic_2[] = {170, 7, 8, 28};
  24. static constexpr uint8_t header_magic_3[] = {170, 7, 8, 16};
  25. static constexpr uint8_t header_magic_config[] = {170, 0, 50, 3};
  26. static constexpr auto message_length_1 = 16;
  27. static constexpr auto message_length_2 = 34;
  28. static constexpr auto message_length_3 = 22;
  29. static constexpr auto message_length_config = 9;
  30. optoforce_daq *q_this = nullptr;
  31. scoped_ptr<serial_port> conn;
  32. uint8_t device_type = 3;
  33. coro_worker::pointer worker;
  34. double force_resolution, torque_resolution;
  35. variable_index_type force_var_index;
  36. variable_index_type torque_var_index;
  37. template<size_t MessageLength>
  38. uint16_t calc_checksum(const char *data) {
  39. return std::accumulate(data, data + MessageLength - 2, static_cast<uint16_t>(0));
  40. }
  41. template<size_t MessageLength>
  42. uint16_t get_checksum_val(const char *data) {
  43. uint16_t checksum_val = *reinterpret_cast<const uint16_t *>(data + MessageLength - 2);
  44. swap_net_loc_endian<daq_endian>(checksum_val);
  45. return checksum_val;
  46. }
  47. template<size_t MessageLength>
  48. bool check_checksum(const char *data) {
  49. auto checksum_val = calc_checksum<MessageLength>(data);
  50. auto checksum = get_checksum_val<MessageLength>(data);
  51. return checksum_val == checksum;
  52. }
  53. awaitable<bool> handle_type_3_message() {
  54. auto buf = static_memory<message_length_3>{};
  55. co_await async_fill_memory_from(*conn, buf);
  56. auto reader = versatile_reader<daq_endian>(buf);
  57. // check header and checksum
  58. assert(memcmp(header_magic_3, buf.data(), 4) == 0);
  59. assert(check_checksum<message_length_3>(buf.data()));
  60. reader.manual_offset(4); // ignore header
  61. uint16_t sample_count, status;
  62. reader >> sample_count >> status;
  63. if (status != 0) {
  64. // TODO show log for abnormal status
  65. }
  66. // force component
  67. auto ts = current_timestamp();
  68. uint16_t fx, fy, fz;
  69. reader >> fx >> fy >> fz;
  70. auto force_value = Eigen::Vector3d(
  71. fx * force_resolution,
  72. fy * force_resolution,
  73. fz * force_resolution);
  74. UPDATE_VARIABLE_VAL_WITH_TS(scalarxyz_obj, force_var_index, std::move(force_value), ts);
  75. // torque component
  76. uint16_t tx, ty, tz;
  77. reader >> tx >> ty >> tz;
  78. auto torque_value = Eigen::Vector3d(
  79. tx * torque_resolution,
  80. ty * torque_resolution,
  81. tz * torque_resolution);
  82. UPDATE_VARIABLE_VAL_WITH_TS(scalarxyz_obj, torque_var_index, std::move(torque_value), ts);
  83. co_return true;
  84. }
  85. awaitable<void> send_config_message(uint8_t speed, uint8_t filter, uint8_t zero = 0) {
  86. assert(zero == 0); // zero will be done manually
  87. auto buf = static_memory<message_length_config>{};
  88. auto writer = versatile_writer<daq_endian>(buf);
  89. memcpy(buf.data(), header_magic_config, 4);
  90. writer.manual_offset(4);
  91. writer << speed << filter << zero;
  92. auto checksum = calc_checksum<message_length_config>(buf.data());
  93. writer << checksum;
  94. co_await async_write_memory_to(*conn, buf);
  95. co_return;
  96. }
  97. awaitable<void> load_device_config(const nlohmann::json &config) {
  98. // device type
  99. device_type = LOAD_UINT_ITEM("device_type");
  100. assert(device_type == 3); // TODO only type 3 is supported
  101. // global objects
  102. switch (device_type) {
  103. case 3: {
  104. force_var_index = LOAD_VARIABLE_INDEX(scalarxyz_obj, "force_output_name");
  105. torque_var_index = LOAD_VARIABLE_INDEX(scalarxyz_obj, "torque_output_name");
  106. break;
  107. }
  108. default: {
  109. assert(false);
  110. break;
  111. }
  112. }
  113. // resolution
  114. force_resolution = LOAD_FLOAT_ITEM("force_resolution");
  115. torque_resolution = LOAD_FLOAT_ITEM("torque_resolution");
  116. // report config
  117. auto freq = LOAD_UINT_ITEM("report_frequency");
  118. assert(freq >= 10);
  119. auto filter = LOAD_UINT_ITEM("filter_type");
  120. co_await send_config_message(1000 / freq, filter);
  121. }
  122. void setup_connection(const nlohmann::json &config) {
  123. assert(conn == nullptr);
  124. conn.reset(new serial_port(*global_context));
  125. // open serial port
  126. assert(config.contains("com_port"));
  127. assert(config["com_port"].is_string());
  128. auto com_port_name = config["com_port"].get<std::string>();
  129. conn->open(com_port_name);
  130. assert(conn->is_open());
  131. // config
  132. conn->set_option(serial_port::baud_rate(115200));
  133. conn->set_option(serial_port::stop_bits(serial_port::stop_bits::one));
  134. conn->set_option(serial_port::parity(serial_port::parity::none));
  135. conn->set_option(serial_port::character_size(8));
  136. conn->set_option(serial_port::flow_control(serial_port::flow_control::none));
  137. }
  138. void create_worker() {
  139. auto exit_func = stop_on_exit_func(q_this);
  140. switch (device_type) {
  141. case 3: {
  142. auto func_wrapper = make_noexcept_func(
  143. std::bind(&impl::handle_type_3_message, this));
  144. worker = make_infinite_coro_worker(std::move(func_wrapper), std::move(exit_func));
  145. break;
  146. }
  147. default: {
  148. assert(false);
  149. break;
  150. }
  151. }
  152. worker->run();
  153. }
  154. };
  155. optoforce_daq::optoforce_daq()
  156. : pimpl(std::make_unique<impl>()) {
  157. pimpl->q_this = this;
  158. }
  159. optoforce_daq::~optoforce_daq() = default;
  160. awaitable<bool> optoforce_daq::on_init(const nlohmann::json &config) noexcept {
  161. co_return true;
  162. }
  163. awaitable<bool> optoforce_daq::on_start(const nlohmann::json &config) noexcept {
  164. try {
  165. pimpl->setup_connection(config);
  166. co_await pimpl->load_device_config(config);
  167. pimpl->create_worker();
  168. } catch (std::exception &e) {
  169. // TODO show log
  170. co_return false;
  171. }
  172. co_return true;
  173. }
  174. awaitable<void> optoforce_daq::on_stop() noexcept {
  175. pimpl->worker->cancel();
  176. co_await pimpl->worker->coro_wait_stop();
  177. pimpl->worker = nullptr;
  178. co_return;
  179. }
  180. awaitable<void> optoforce_daq::on_reset() noexcept {
  181. co_return;
  182. }
  183. }