NDEVR
API Documentation
RealSenseServer.h
1#pragma once
2#include "RealSenseInterface/Headers/RealSenseScanner.h"
3#include "RealSenseInterface/Headers/RealSenseScannerFactory.h"
4#include "Design/Headers/DesignObjectLookup.h"
5#include <NDEVR/Connection.h>
6#include <NDEVR/OrientationSensor.h>
7#include "Widgets/Headers/QTTools.h"
8#include "Base/Headers/Translator.h"
9#include "Base/Headers/BasicThread.h"
10#include <QTimer>
11namespace NDEVR
12{
18 {
19 uint08 index_value = 0x8c15f09407357894;
20 char char_value[8];
21 };
22 static_assert(sizeof(RealsensePacketChecksum) == 8, "Bad checksum");
30#pragma pack(push,1)
33 {
34 char packet_info[255];
35 };
36#pragma pack(pop)
37#pragma pack(push,1)
47#pragma pack(pop)
48#pragma pack(push,1)
56#pragma pack(pop)
57
58#pragma pack(push,1)
66#pragma pack(pop)
67 static_assert(sizeof(RealsensePacketHeader) == 10, "Bad header");
68
69
70 static_assert(sizeof(RealsensePacketHeader) == 10, "Bad header");
77 {
78 public:
79 static constexpr uint04 rx_port_number = 31458;
80 static constexpr uint04 tx_port_number = 31459;
86 static void CreateCommandPacket(String& packet, const String& command, uint04 packet_num)
87 {
88 packet.setSize(sizeof(RealsensePacketHeader) + sizeof(CommandPacket));
89
92 header.packet_num = cast<uint01>(packet_num % 256);
93 memmove(packet.begin(), &header, sizeof(RealsensePacketHeader));
94
95 CommandPacket info;
96 memmove(info.packet_info, command.begin(), command.size());
97 info.packet_info[command.size()] = '\0';
98 memmove(packet.begin(sizeof(RealsensePacketHeader)), &info, sizeof(CommandPacket));
99 }
100
105 static void CreateProbeInfoPacket(String& packet, const RasterPacketInfo& packet_info, uint04 packet_num)
106 {
107 packet.setSize(sizeof(RealsensePacketHeader) + sizeof(InfoPacketHeader));
108
111 header.packet_num = cast<uint01>(packet_num % 256);
112 InfoPacketHeader info;
113 info.matrix = packet_info.mat;
114 //info.device_type = packet_info.device_type;
115 info.scanner_id = packet_info.scanner_id;
116 info.frame_time = packet_info.frame_time;
117 info.gyro_status = packet_info.gyro_status;
118 memmove(packet.begin(), &header, sizeof(RealsensePacketHeader));
119 memmove(packet.begin(sizeof(RealsensePacketHeader)), &info, sizeof(InfoPacketHeader));
120 }
121
128 static void CreateCloudPacket(String& packet, const RasterPacketInfo& packet_info, uint04 data_offset, uint02 data_size, uint04 packet_num)
129 {
130 packet.setSize(sizeof(RealsensePacketHeader) + sizeof(DataPacketHeader) + data_size * sizeof(Vertex<3, sint02>) + data_size * sizeof(RGBColor));
131
134 header.packet_num = cast<uint01>(packet_num % 256);
135 DataPacketHeader data;
136 data.data_size = data_size;
137 uint08 packet_offset = sizeof(RealsensePacketHeader) + sizeof(DataPacketHeader);
138
139 size_t compressed_point_data_size = cast<size_t>(data_size) * sizeof(Vertex<3, sint02>);
140 Compressor::ZLibCompress((uint01*)packet.begin(packet_offset), (uint01*)packet_info.active_point_data.begin(data_offset)
141 , compressed_point_data_size, compressed_point_data_size);
142 data.compressed_point_data_size = cast<uint02>(compressed_point_data_size);
143 packet_offset += compressed_point_data_size;
144
145
146 compressed_point_data_size = cast<size_t>(data_size) * sizeof(RGBColor);
147 Compressor::ZLibCompress((uint01*)packet.begin(packet_offset), (uint01*)packet_info.active_color_data.begin(data_offset)
148 , compressed_point_data_size, compressed_point_data_size);
149 data.compressed_color_data_size = cast<uint02>(compressed_point_data_size);
150 packet_offset += compressed_point_data_size;
151
152
153 memmove(packet.begin(), &header, sizeof(RealsensePacketHeader));
154 memmove(packet.begin(sizeof(RealsensePacketHeader)), &data, sizeof(DataPacketHeader));
156 }
157
164 static uint04 ReadNetworkPacket(const String& packet, RasterPacketInfo& packet_info, uint04 offset, String& command_response)
165 {
167 if (offset + sizeof(RealsensePacketHeader) > packet.size())
168 return offset;
169 memcpy(&header, packet.begin(offset), sizeof(RealsensePacketHeader));
170 if (header.check_sum.index_value != 0x8c15f09407357894)
171 {
172 char search[9];
173 memcpy(search, RealsensePacketChecksum().char_value, 8);
174 search[8] = 0;
175 uint04 index_value = packet.indexOf(search, false, offset);
176 if (IsInvalid(index_value))
177 return getMax(packet.size(), 7U) - 7U;//throw out 'invalid' data
178 offset = index_value;
179 if (offset + sizeof(RealsensePacketHeader) > packet.size())
180 return offset;
181 memcpy(&header, packet.begin(offset), sizeof(RealsensePacketHeader));
182 }
183 if (header.packet_num != packet_info.packet_num)
184 {
185 if (packet_info.active_color_data.size() > 0)
186 return Constant<uint04>::Invalid;//signal for new frame
187
188 }
189 packet_info.packet_num = header.packet_num;
190 switch (header.packet_type)
191 {
193 {
194 if (offset + sizeof(RealsensePacketHeader) + sizeof(CommandPacket) > packet.size())
195 return offset;//not all data recieved
196 offset += sizeof(RealsensePacketHeader);
197 CommandPacket info;
198 memcpy(&info, packet.begin(offset), sizeof(CommandPacket));
199 offset += sizeof(CommandPacket);
200 command_response = String(info.packet_info);
201 } break;
203 {
204 if (offset + sizeof(RealsensePacketHeader) + sizeof(InfoPacketHeader) > packet.size())
205 return offset;//not all data recieved
206 offset += sizeof(RealsensePacketHeader);
207 InfoPacketHeader info;
208 memcpy(&info, packet.begin(offset), sizeof(InfoPacketHeader));
209 offset += sizeof(InfoPacketHeader);
210 packet_info.scanner_id = info.scanner_id;
211 packet_info.mat = info.matrix;
212 //packet_info.device_type = info.device_type;
213 packet_info.frame_time = info.frame_time;
214 packet_info.gyro_status = info.gyro_status;
215 } break;
217 {
218 if (offset + sizeof(RealsensePacketHeader) + sizeof(DataPacketHeader) > packet.size())
219 return offset;//not all data recieved
220 DataPacketHeader data;
221 memcpy(&data, packet.begin(offset + sizeof(RealsensePacketHeader)), sizeof(DataPacketHeader));
222
223 if (offset + sizeof(RealsensePacketHeader) + sizeof(DataPacketHeader)
226 > packet.size())
227 return offset;//not all data recieved
228 offset += sizeof(RealsensePacketHeader);
229 offset += sizeof(DataPacketHeader);
230
231 uint04 point_data_offset = packet_info.active_point_data.size();
232 packet_info.active_point_data.addSpace<false>(data.data_size);
233 packet_info.active_color_data.addSpace<false>(data.data_size);
234
235 size_t decompressed_size = data.data_size * sizeof(Vector<3, sint02>);
236
237 if (decompressed_size == data.compressed_point_data_size)
238 {
239 memcpy(packet_info.active_point_data.begin(point_data_offset), packet.begin(offset), decompressed_size);
240 }
241 else
242 {
243 Compressor::ZLibDecompress((uint01*)packet.begin(offset)
244 , (uint01*)packet_info.active_point_data.begin(point_data_offset)
245 , data.compressed_point_data_size, decompressed_size);
246 }
247 //lib_assert(decompressed_size == data.data_size * sizeof(Vector<3, sint02>), "bad decompression of rs point data");
248 offset += data.compressed_point_data_size;
249
250 decompressed_size = data.data_size * sizeof(RGBColor);
251
252 if (decompressed_size == data.compressed_point_data_size)
253 {
254 memcpy(packet_info.active_color_data.begin(point_data_offset), packet.begin(offset), decompressed_size);
255 }
256 else
257 {
258 Compressor::ZLibDecompress((uint01*)packet.begin(offset)
259 , (uint01*)packet_info.active_color_data.begin(point_data_offset)
260 , data.compressed_color_data_size, decompressed_size);
261 }
262 offset += data.compressed_color_data_size;
263 //lib_assert(decompressed_size == data.data_size * sizeof(RGBColor), "bad decompression of rs color data");
264 } break;
265
266 }
267 return offset;
268 }
269 public:
271 class WorkerThread : public QThread
272 {
273 public:
278 : m_server(server)
279 {}
280
281 void run() override
282 {
283 for (;;)
284 {
286 //Thread::sleep(100);
288 if (!m_server->updateServer())
289 {
291 }
292 }
293 }
295 };
296
301 : m_scanner_factory(scanner_factory)
302 {
303 m_thread = new WorkerThread(this);
304 }
305
308 void sendPointData(const RasterPacketInfo& packet_info)
309 {
310 {
313 }
314 for (uint04 i = 0; i < packet_info.active_color_data.size(); i += 150U)
315 {
316 CreateCloudPacket(m_tx_packet, packet_info, i, cast<uint02>(getMin(150U, packet_info.active_color_data.size() - i)), m_tx_packet_count);
318 //QTTools::ServiceQT();
319 //QThread::sleep(1);
320 }
322 }
323
326 void sendCommandData(const String& data)
327 {
328 {
331 }
333 }
334
336 {
337 String s = m_rx_connection->rx();
338 if (s.hasSubString("ping"))
339 {
340 return sendCommandData(s);
341 }
342 else if (s.hasSubString("get_time"))
343 {
344 return sendCommandData("get_time|" + String(Time::SystemTime()));
345 }
346 else if (s.hasSubString("reset_server"))
347 {
348 stopServer();
349 startServer();
350 }
351 else if (s.hasSubString("request_gyro_calibration"))
352 {
354 scanner->orientationSensor()->requestGyroAlignment(false);
355 }
356 else if (s.hasSubString("request_stream"))
357 {
358 StringView stream = s.split('|')[1];
359
360 m_tx_connection = new Connection(Scene(_t("Tx Connection")));
361 ConnectionInfo tx_connection_info;
362 tx_connection_info.type = "UDP";
363 tx_connection_info.address = stream + ":" + String(tx_port_number);
365 m_tx_connection->open(tx_connection_info);
366 }
367 else if (s.hasSubString("set_system_time"))
368 {
369 StringView stream = s.split('|')[1];
370 Time::SetSystemTime(stream.getAs<Time>());
372 scanner->invalidateTime();
373 }
374 }
375
377 {
378 m_rx_connection = new Connection(Scene(_t("Rx Connection")));
379 ConnectionInfo rx_connection_info;
380 rx_connection_info.type = "UDPLH";
381 rx_connection_info.address = String(rx_port_number);
383 m_rx_connection->open(rx_connection_info);
385 {
386 checkRecieve();
387 });
388 }
389
393 {
394 if (m_rx_connection == nullptr)
396 bool has_update = false;
397 if (m_tx_connection)
398 {
400 {
401 if (scanner->getCurrentPacket(m_packet_info))
402 {
404 has_update = true;
405 }
406 }
407 }
408
409 QTTools::log().addMessage("Scanners: "+String(m_connected_scanners.size()));
410 checkRecieve();
411 return has_update;
412 }
413
416 {
418 delete scanner;
419 m_connected_scanners.clear();
420 }
421
423 {
425 Buffer<QPointer<Connection>> connections = m_scanner_factory->possibleLocalConnections(params);
426 for (QPointer<Connection> connection : connections)
427 {
428 if (connection == nullptr)
429 return;
430 RealSenseScanner* controller = new RealSenseScanner(dynamic_cast<RealSenseScannerConnection*>(connection.data()), nullptr);
431 if (controller->isValid())
432 {
433 controller->setIsNetworkServer(true);
434 QTimer::singleShot(2000, [controller]
435 {
436 controller->orientationSensor()->requestGyroAlignment(false);
437 });
438 m_connected_scanners.add(controller);
439 }
440 else
441 {
442 delete controller;
443 }
444 }
445 m_thread->start();
446 }
455 };
456
457}
The equivelent of std::vector but with a bit more control.
Definition Buffer.hpp:58
void addSpace(t_index_type space_to_add)
Adds a space to the end of the buffer.
Definition Buffer.hpp:400
static sint04 ZLibDecompress(const uint01 *compressed, uint01 *uncompressed, size_t compressed_size, size_t &decompressed_size)
A simple, raw call into zlibs uncompress function.
static sint04 ZLibCompress(uint01 *compressed, const uint01 *uncompressed, size_t &compressed_size, size_t decompressed_size)
A simple, raw call into zlibs compress function, except if the compressed size is larger than the unc...
A standard interface for all types of connections that allow transmitting and receiving of data betwe...
Definition Connection.h:316
void dataAvailableSignal()
Emitted when data is available to be read.
Templated logic for doing matrix multiplication.
Definition Matrix.hpp:182
static LogPtr log()
Returns the global log for the Widgets module.
static void ServiceQT()
Processes pending Qt events in the current event loop.
Represents a color in the RGB space with optional alpha transparency.
Definition RGBColor.h:57
Connection subclass for a locally or remotely connected RealSense device.
Factory for discovering and connecting to RealSense 3D scanners.
3D scanner implementation for Intel RealSense depth cameras.
Background thread for the server update loop.
void run() override
Runs the server update loop.
RealSenseServer * m_server
The owning server.
WorkerThread(RealSenseServer *server)
Constructs the worker thread.
static void CreateProbeInfoPacket(String &packet, const RasterPacketInfo &packet_info, uint04 packet_num)
Creates a probe info packet with scanner metadata.
void stopServer()
Stops the server and disconnects all scanners.
Connection * m_rx_connection
UDP connection for receiving commands.
static constexpr uint04 tx_port_number
UDP port for transmitting data.
String m_tx_packet
Reusable transmit packet buffer.
void startServer()
Starts the server, discovers local scanners, and begins streaming.
static void CreateCommandPacket(String &packet, const String &command, uint04 packet_num)
Creates a command packet from a command string.
Buffer< RealSenseScanner * > m_connected_scanners
List of connected local scanners.
void sendCommandData(const String &data)
Sends a command response to the connected client.
void sendPointData(const RasterPacketInfo &packet_info)
Sends point cloud data to the connected client.
void checkRecieve()
Checks for and processes incoming commands from the client.
uint04 m_tx_packet_count
Transmit packet sequence counter.
static uint04 ReadNetworkPacket(const String &packet, RasterPacketInfo &packet_info, uint04 offset, String &command_response)
Reads and parses a network packet from a raw data buffer.
bool updateServer()
Runs one update cycle: collects data from scanners and sends to client.
static void CreateCloudPacket(String &packet, const RasterPacketInfo &packet_info, uint04 data_offset, uint02 data_size, uint04 packet_num)
Creates a compressed point cloud data packet.
Connection * m_tx_connection
UDP connection for transmitting data.
RealSenseScannerFactory * m_scanner_factory
Factory for discovering local scanners.
WorkerThread * m_thread
Background server thread.
static constexpr uint04 rx_port_number
UDP port for receiving commands.
RealSenseServer(RealSenseScannerFactory *scanner_factory)
Constructs the server with the given scanner factory.
void initConnections()
Initializes the UDP listening connection for incoming commands.
RasterPacketInfo m_packet_info
Current raster packet info being transmitted.
The root Model that is responsible for storing the underlying data for all Scene Models.
Definition Scene.h:52
The core String View class for the NDEVR API.
Definition StringView.h:58
t_type getAs() const
Converts a string into an object.
Definition StringView.h:125
The core String class for the NDEVR API.
Definition String.h:95
uint04 indexOf(const StringView &sub_string, bool ignore_case=false, uint04 start_index=0) const
Given a substring specified by the input, returns the first index of that string, if it exists.
bool hasSubString(const StringView &sub_string, bool ignore_case=false, uint04 start_idx=0) const
Tests if this String contains the specified substring.
StringViewBuffer split(char delimiter, bool preserve_empty=true) const
Given a delimiter, breaks the string into subsections, returning an array of each subsection.
static void RequestSleep(const TimeSpan &interval)
Puts the current thread to sleep for a specified duration.
Stores a time span, or difference between two times, with an optional start time.
Definition TimeSpan.h:46
Represents a timestamp with utilities for manipulation and conversion.
Definition Time.h:62
static void SetSystemTime(const Time &time)
Sets the application's system clock to a specified Time.
static Time SystemTime()
Retrieves the current system time which is a combination of std::chrono::steady_clock to ensure smoot...
A universally unique identifier (UUID) is a 128-bit number used to identify information in computer s...
Definition UUID.h:61
A fixed-size array with N dimensions used as the basis for geometric and mathematical types.
Definition Vector.hpp:62
A point in N-dimensional space, used primarily for spatial location information.
Definition Vertex.hpp:44
The primary namespace for the NDEVR SDK.
PacketType
Enumeration of RealSense network packet types.
@ e_probe_info_packet
A scanner info/metadata packet.
@ e_cloud_data
A point cloud data packet.
@ e_command_packet
A text command packet.
constexpr t_type getMin(const t_type &left, const t_type &right)
Finds the minimum of the given arguments based on the < operator Author: Tyler Parke Date: 2017-11-05...
constexpr t_type getMax(const t_type &left, const t_type &right)
Finds the max of the given arguments using the > operator The only requirement is that t_type have > ...
uint16_t uint02
-Defines an alias representing a 2 byte, unsigned integer -Can represent exact integer values 0 throu...
uint64_t uint08
-Defines an alias representing an 8 byte, unsigned integer
uint32_t uint04
-Defines an alias representing a 4 byte, unsigned integer -Can represent exact integer values 0 throu...
int16_t sint02
-Defines an alias representing a 2 byte, signed integer.
uint8_t uint01
-Defines an alias representing a 1 byte, unsigned integer -Can represent exact integer values 0 throu...
static constexpr bool IsInvalid(const Angle< t_type > &value)
Checks whether the given Angle holds an invalid value.
Definition Angle.h:388
RealSenseDeviceType
Enumeration of supported RealSense device types.
@ e_unknown
Unknown device type.
@ e_write_only
Open for writing only.
Definition Connection.h:67
@ e_read_only
Open for reading only.
Definition Connection.h:66
constexpr t_to cast(const Angle< t_from > &value)
Casts an Angle from one backing type to another.
Definition Angle.h:408
Packet payload for text commands between client and server.
char packet_info[255]
Null-terminated command string.
A structure designed to store information about a specific Connection.
Definition Connection.h:90
ConnectionOpenType connect_mode
The mode in which to open the connection.
Definition Connection.h:103
String type
The connection type (e.g., "serial", "tcp", "bluetooth").
Definition Connection.h:101
String address
The address or port of the connection.
Definition Connection.h:102
Packet header for compressed point cloud data.
uint02 compressed_color_data_size
Size of compressed color data in bytes.
uint02 compressed_point_data_size
Size of compressed point data in bytes.
uint02 data_size
Number of points in this packet.
A series of paths to use for finding potential hardware connections.
Packet header for scanner metadata (ID, matrix, timing).
RealSenseDeviceType device_type
The device type.
Time frame_time
The timestamp of the frame.
sint02 gyro_status
The gyroscope calibration status.
UUID scanner_id
The scanner's unique identifier.
Matrix< fltp08 > matrix
The scanner's transformation matrix.
Packet of colored point data sent from the raster scanner for live and inactive display.
uint01 packet_num
Sequence number for ordering packets.
sint02 gyro_status
Gyro calibration status (0=uncalibrated, max=calibrated, negative=error).
UUID scanner_id
UUID of the source scanner.
Matrix< fltp08 > mat
Transform matrix for the packet.
Time frame_time
Timestamp of the packet.
Buffer< Vertex< 3, fltp04 > > active_point_data
Live display point positions.
Buffer< RGBColor > active_color_data
Live display point colors.
Top-level packet header with checksum, type, and sequence number.
uint01 packet_num
Packet sequence number (wraps at 256).
PacketType packet_type
The packet type.
RealsensePacketChecksum check_sum
Magic number checksum for packet validation.
Checksum union for identifying valid RealSense network packets.
char char_value[8]
The magic number as raw bytes.
uint08 index_value
The magic number value.