libvisiontransfer  10.8.0
datachannelservice.cpp
1 /*******************************************************************************
2  * Copyright (c) 2024 Allied Vision Technologies GmbH
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *******************************************************************************/
14 
15 #include <sys/types.h>
16 #include <cstring>
17 #include <stdexcept>
18 #include <fcntl.h>
19 #include <fstream>
20 
21 #include <visiontransfer/internal/internalinformation.h>
22 #include <visiontransfer/internal/networking.h>
23 #include <visiontransfer/internal/datachannelservicebase.h>
24 #include <visiontransfer/internal/datachannel-control.h>
25 #include <visiontransfer/datachannelservice.h>
26 
27 #include <visiontransfer/internal/datachannel-imu-bno080.h>
28 #include <visiontransfer/internal/protocol-sh2-imu-bno080.h> // for sensor constants
29 
30 #include <iostream>
31 
32 #include <memory>
33 #include <functional>
34 #include <thread>
35 #include <mutex>
36 #include <chrono>
37 
38 #ifdef _WIN32
39 #include <ws2tcpip.h>
40 #endif
41 
42 using namespace visiontransfer;
43 using namespace visiontransfer::internal;
44 
45 namespace visiontransfer {
46 namespace internal {
47 
49 private:
50  sockaddr_in serverAddr;
51  //
52  std::shared_ptr<std::thread> receiverThread;
53  unsigned long pollDelay;
54  //
55  std::shared_ptr<ClientSideDataChannelIMUBNO080> channelBNO080;
56  //
57  int handleChannel0Message(DataChannelMessage& message, sockaddr_in* sender) override;
58  void initiateHandshake();
59  void subscribeAll();
60  void unsubscribeAll();
61  void receiverRoutine();
62 public:
63  bool threadRunning;
64  std::vector<DataChannelInfo> channelsAvailable;
65  std::map<DataChannel::Type, std::set<DataChannel::ID>> channelsAvailableByType;
66 public:
68  DataChannelServiceImpl(const char* ipAddr);
69  virtual ~DataChannelServiceImpl() { }
70  void launch(unsigned long pollDelayUSec);
71 public:
72  // High-level data channels API
73  TimestampedQuaternion getLastRotationQuaternion() {
74  return channelBNO080->lastRotationQuaternion;
75  }
76  std::vector<TimestampedQuaternion> getRotationQuaternionSeries(int fromSec, int fromUSec, int untilSec, int untilUSec) {
77  return channelBNO080->ringbufRotationQuaternion.popBetweenTimes(fromSec, fromUSec, untilSec, untilUSec);
78  }
79 
80  TimestampedVector getLastSensorVector(int idx) {
81  return channelBNO080->lastXYZ[idx - 1];
82  }
83  std::vector<TimestampedVector> getSensorVectorSeries(int idx, int fromSec, int fromUSec, int untilSec, int untilUSec) {
84  return channelBNO080->ringbufXYZ[idx - 1].popBetweenTimes(fromSec, fromUSec, untilSec, untilUSec);
85  }
86 
87  TimestampedScalar getLastSensorScalar(int idx) {
88  return channelBNO080->lastScalar[idx - 0x0a];
89  }
90  std::vector<TimestampedScalar> getSensorScalarSeries(int idx, int fromSec, int fromUSec, int untilSec, int untilUSec) {
91  return channelBNO080->ringbufScalar[idx - 0x0a].popBetweenTimes(fromSec, fromUSec, untilSec, untilUSec);
92  }
93 };
94 
95 } // internal namespace
96 
97 class DataChannelService::Pimpl {
98 public:
99  std::shared_ptr<internal::DataChannelServiceImpl> impl;
100  Pimpl(DeviceInfo deviceInfo) {
101  impl = std::make_shared<internal::DataChannelServiceImpl>(deviceInfo);
102  }
103  Pimpl(const char* ipAddress) {
104  impl = std::make_shared<internal::DataChannelServiceImpl>(ipAddress);
105  }
106 };
107 
108 void internal::DataChannelServiceImpl::receiverRoutine() {
109  threadRunning = true;
110  while (threadRunning) {
111  process();
112  std::this_thread::sleep_for(std::chrono::microseconds(pollDelay));
113  }
114 }
115 
116 void internal::DataChannelServiceImpl::launch(unsigned long pollDelayUSec) {
117  // Prepare our receivers (all supported channels aside from service channel 0)
118  channelBNO080 = std::make_shared<ClientSideDataChannelIMUBNO080>();
119  registerChannel(channelBNO080);
120  // Prepare our poll thread
121  pollDelay = pollDelayUSec;
122  receiverThread = std::make_shared<std::thread>(std::bind(&internal::DataChannelServiceImpl::receiverRoutine, this));
123  receiverThread->detach();
124  // Say hello to the device to get a channel advertisement
125  initiateHandshake();
126 }
127 
128 
129 void internal::DataChannelServiceImpl::initiateHandshake() {
130  uint16_t cmd = htons((uint16_t) DataChannelControlCommands::CTLRequestAdvertisement);
131  sendDataIsolatedPacket((DataChannel::ID) 0x00, DataChannel::Types::CONTROL, (unsigned char*) &cmd, sizeof(cmd), &serverAddr);
132 }
133 
134 void internal::DataChannelServiceImpl::subscribeAll() {
135  unsigned char data[1024];
136  int len = DataChannelControlUtil::packSubscriptionMessage(data, 1024, DataChannelControlCommands::CTLRequestSubscriptions, {0});
137  sendDataIsolatedPacket((DataChannel::ID) 0x00, DataChannel::Types::CONTROL, data, len, &serverAddr);
138 }
139 
140 void internal::DataChannelServiceImpl::unsubscribeAll() {
141  unsigned char data[1024];
142  int len = DataChannelControlUtil::packSubscriptionMessage(data, 1024, DataChannelControlCommands::CTLRequestUnsubscriptions, {0});
143  sendDataIsolatedPacket((DataChannel::ID) 0x00, DataChannel::Types::CONTROL, data, len, &serverAddr);
144 }
145 
146 int internal::DataChannelServiceImpl::handleChannel0Message(DataChannelMessage& message, sockaddr_in* sender) {
147  auto cmd = DataChannelControlUtil::getCommand(message.payload, message.header.payloadSize);
148  switch (cmd) {
149  case DataChannelControlCommands::CTLProvideAdvertisement: {
150  // Update the available channels lists for run-time checks etc.
151  channelsAvailable = DataChannelControlUtil::unpackAdvertisementMessage(message.payload, message.header.payloadSize);
152  for (auto& dci: channelsAvailable) {
153  channelsAvailableByType[dci.getChannelType()].insert(dci.getChannelID());
154  }
155  // Automatic subscribeAll is suitable for now
156  subscribeAll();
157  break;
158  }
159  case DataChannelControlCommands::CTLProvideSubscriptions: {
160  break;
161  }
162  default: {
163  break;
164  }
165  }
166  return 1;
167 }
168 
169 internal::DataChannelServiceImpl::DataChannelServiceImpl(DeviceInfo deviceInfo)
170 : DataChannelServiceImpl::DataChannelServiceImpl(deviceInfo.getIpAddress().c_str())
171 {}
172 
173 internal::DataChannelServiceImpl::DataChannelServiceImpl(const char* ipAddress)
174 : DataChannelServiceBase(), threadRunning(false) {
175  serverAddr.sin_family = AF_INET;
176  serverAddr.sin_port = htons(InternalInformation::DATACHANNELSERVICE_PORT);
177  auto result = inet_addr(ipAddress);
178  if (result == INADDR_NONE) {
179  throw std::runtime_error("Failed to set address for DataChannelService");
180  }
181  serverAddr.sin_addr.s_addr = result;
182  //
183  //if (!inet_pton(AF_INET, deviceInfo.getIpAddress().c_str(), &(serverAddr.sin_addr))) {
184  // throw std::runtime_error("Failed to set address for DataChannelService");
185  //}
186 }
187 
188 
189 DataChannelService::DataChannelService(DeviceInfo deviceInfo, unsigned long pollDelayUSec) {
190  pimpl = new DataChannelService::Pimpl(deviceInfo);
191  pimpl->impl->launch(pollDelayUSec);
192 }
193 
194 DataChannelService::DataChannelService(const char* ipAddress, unsigned long pollDelayUSec) {
195  pimpl = new DataChannelService::Pimpl(ipAddress);
196  pimpl->impl->launch(pollDelayUSec);
197 }
198 
199 
200 DataChannelService::~DataChannelService() {
201  pimpl->impl->threadRunning = false;
202  delete pimpl;
203 }
204 
206  return pimpl->impl->channelsAvailableByType.count(DataChannel::Types::BNO080);
207 }
208 
209 
210 // High-level IMU accessors (C++-98 compatible signatures)
211 
212 // For devices not providing IMU data, these return placeholder defaults
214  return pimpl->impl->getLastRotationQuaternion();
215 }
216 std::vector<TimestampedQuaternion> DataChannelService::imuGetRotationQuaternionSeries(int fromSec, int fromUSec, int untilSec, int untilUSec) {
217  return pimpl->impl->getRotationQuaternionSeries(fromSec, fromUSec, untilSec, untilUSec);
218 }
219 
221  return pimpl->impl->getLastSensorVector(SH2Constants::SENSOR_ACCELEROMETER);
222 }
223 std::vector<TimestampedVector> DataChannelService::imuGetAccelerationSeries(int fromSec, int fromUSec, int untilSec, int untilUSec) {
224  return pimpl->impl->getSensorVectorSeries(SH2Constants::SENSOR_ACCELEROMETER, fromSec, fromUSec, untilSec, untilUSec);
225 }
226 
228  return pimpl->impl->getLastSensorVector(SH2Constants::SENSOR_GYROSCOPE);
229 }
230 std::vector<TimestampedVector> DataChannelService::imuGetGyroscopeSeries(int fromSec, int fromUSec, int untilSec, int untilUSec) {
231  return pimpl->impl->getSensorVectorSeries(SH2Constants::SENSOR_GYROSCOPE, fromSec, fromUSec, untilSec, untilUSec);
232 }
233 
235  return pimpl->impl->getLastSensorVector(SH2Constants::SENSOR_MAGNETOMETER);
236 }
237 std::vector<TimestampedVector> DataChannelService::imuGetMagnetometerSeries(int fromSec, int fromUSec, int untilSec, int untilUSec) {
238  return pimpl->impl->getSensorVectorSeries(SH2Constants::SENSOR_MAGNETOMETER, fromSec, fromUSec, untilSec, untilUSec);
239 }
240 
242  return pimpl->impl->getLastSensorVector(SH2Constants::SENSOR_LINEAR_ACCELERATION);
243 }
244 std::vector<TimestampedVector> DataChannelService::imuGetLinearAccelerationSeries(int fromSec, int fromUSec, int untilSec, int untilUSec) {
245  return pimpl->impl->getSensorVectorSeries(SH2Constants::SENSOR_LINEAR_ACCELERATION, fromSec, fromUSec, untilSec, untilUSec);
246 }
247 
249  return pimpl->impl->getLastSensorVector(SH2Constants::SENSOR_GRAVITY);
250 }
251 std::vector<TimestampedVector> DataChannelService::imuGetGravitySeries(int fromSec, int fromUSec, int untilSec, int untilUSec) {
252  return pimpl->impl->getSensorVectorSeries(SH2Constants::SENSOR_GRAVITY, fromSec, fromUSec, untilSec, untilUSec);
253 }
254 
255 } // visiontransfer namespace
256 
visiontransfer::internal::DataChannelMessage
Definition: datachannelservicebase.h:71
visiontransfer::DataChannelService::imuGetRotationQuaternionSeries
std::vector< TimestampedQuaternion > imuGetRotationQuaternionSeries(int fromSec=0, int fromUSec=0, int untilSec=0x7FFFffffl, int untilUSec=0x7FFFffffl)
Return the current contents of the rotation quaternion data buffer, optionally between specified time...
Definition: datachannelservice.cpp:216
visiontransfer::TimestampedQuaternion
Encapsulate a 4D (quaternion) sensor report, containing X, Y, Z, W, as well as timestamp and status f...
Definition: sensordata.h:80
visiontransfer::DataChannelService::imuAvailable
bool imuAvailable()
Return whether the device will provide data from an Inertial Measurement Unit.
Definition: datachannelservice.cpp:205
visiontransfer::DataChannelService::imuGetLinearAccelerationSeries
std::vector< TimestampedVector > imuGetLinearAccelerationSeries(int fromSec=0, int fromUSec=0, int untilSec=0x7FFFffffl, int untilUSec=0x7FFFffffl)
Return the current contents of the linear acceleration (without gravity) data buffer,...
Definition: datachannelservice.cpp:244
visiontransfer::internal::DataChannelServiceImpl
Definition: datachannelservice.cpp:48
visiontransfer::DataChannelService::imuGetRotationQuaternion
TimestampedQuaternion imuGetRotationQuaternion()
Return the most recent rotation quaternion, relative to gravity and magnetic north.
Definition: datachannelservice.cpp:213
visiontransfer::DeviceInfo
Aggregates information about a discovered device.
Definition: deviceinfo.h:59
visiontransfer::DataChannelService::imuGetMagnetometerSeries
std::vector< TimestampedVector > imuGetMagnetometerSeries(int fromSec=0, int fromUSec=0, int untilSec=0x7FFFffffl, int untilUSec=0x7FFFffffl)
Return the current contents of the magnetometer data buffer, optionally between specified timestamps.
Definition: datachannelservice.cpp:237
visiontransfer::DataChannelService::imuGetAccelerationSeries
std::vector< TimestampedVector > imuGetAccelerationSeries(int fromSec=0, int fromUSec=0, int untilSec=0x7FFFffffl, int untilUSec=0x7FFFffffl)
Return the current contents of the calibrated accelerometer data buffer, optionally between specified...
Definition: datachannelservice.cpp:223
visiontransfer::DataChannelService::imuGetAcceleration
TimestampedVector imuGetAcceleration()
Return the most recent calibrated accelerometer reading.
Definition: datachannelservice.cpp:220
visiontransfer::DataChannelService::imuGetGyroscope
TimestampedVector imuGetGyroscope()
Return the most recent calibrated angular accelerations from the gyroscope.
Definition: datachannelservice.cpp:227
visiontransfer::internal::DataChannelServiceBase
Base class for the data service (background sending and receiving, dispatching to channels)
Definition: datachannelservicebase.h:146
visiontransfer::TimestampedVector
Encapsulate a 3D sensor report, containing X, Y, Z, as well as timestamp and status fields.
Definition: sensordata.h:64
visiontransfer::DataChannelService::DataChannelService
DataChannelService(DeviceInfo deviceInfo, unsigned long pollDelayUSec=1000)
Definition: datachannelservice.cpp:189
visiontransfer::DataChannelService::imuGetLinearAcceleration
TimestampedVector imuGetLinearAcceleration()
Return the most recent linear acceleration, i.e. with gravity factored out.
Definition: datachannelservice.cpp:241
visiontransfer::DataChannelService::imuGetGyroscopeSeries
std::vector< TimestampedVector > imuGetGyroscopeSeries(int fromSec=0, int fromUSec=0, int untilSec=0x7FFFffffl, int untilUSec=0x7FFFffffl)
Return the current contents of the gyroscope data buffer, optionally between specified timestamps.
Definition: datachannelservice.cpp:230
visiontransfer::DataChannelService::imuGetGravity
TimestampedVector imuGetGravity()
Return the most recent gravity measurement.
Definition: datachannelservice.cpp:248
visiontransfer::DataChannelService::imuGetGravitySeries
std::vector< TimestampedVector > imuGetGravitySeries(int fromSec=0, int fromUSec=0, int untilSec=0x7FFFffffl, int untilUSec=0x7FFFffffl)
Return the current contents of the gravity data buffer, optionally between specified timestamps.
Definition: datachannelservice.cpp:251
visiontransfer::DataChannelService::imuGetMagnetometer
TimestampedVector imuGetMagnetometer()
Return the most recent magnetometer readings.
Definition: datachannelservice.cpp:234
visiontransfer::TimestampedScalar
Encapsulate a scalar sensor measurement, containing the value, as well as timestamp and status fields...
Definition: sensordata.h:51
Allied Vision