TTK
Loading...
Searching...
No Matches
ttkWebSocketIO.cpp
Go to the documentation of this file.
1#include <ttkWebSocketIO.h>
2
3#include <vtkInformation.h>
4
5#include <vtkImageData.h>
6#include <vtkMultiBlockDataSet.h>
7#include <vtkPolyData.h>
8#include <vtkSmartPointer.h>
9#include <vtkStringArray.h>
10#include <vtkUnstructuredGrid.h>
11
12#include <vtkCellData.h>
13#include <vtkDoubleArray.h>
14#include <vtkPointData.h>
15#include <vtkUnsignedCharArray.h>
16
17#include <vtkCellArray.h>
18#include <vtkPoints.h>
19
20#include <boost/algorithm/string/replace.hpp>
21#include <boost/lexical_cast.hpp>
22#include <boost/optional/optional.hpp>
23#include <boost/property_tree/json_parser.hpp>
24#include <boost/property_tree/ptree.hpp>
25
26#include <ttkUtils.h>
27
29
33
34 this->SetNumberOfInputPorts(1);
35 this->SetNumberOfOutputPorts(1);
36}
37
39
40int ttkWebSocketIO::FillInputPortInformation(int port, vtkInformation *info) {
41 if(port == 0) {
42 info->Remove(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE());
43 info->Append(
44 vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkMultiBlockDataSet");
45 info->Append(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkDataSet");
46 return 1;
47 }
48 return 0;
49}
50
51int ttkWebSocketIO::FillOutputPortInformation(int port, vtkInformation *info) {
52 if(port == 0) {
53 info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkMultiBlockDataSet");
54 return 1;
55 }
56 return 0;
57}
58
59int ttkWebSocketIO::RequestData(vtkInformation *ttkNotUsed(request),
60 vtkInformationVector **inputVector,
61 vtkInformationVector *outputVector) {
62 const bool neededUpdate = this->GetNeedsUpdate();
63 this->SetNeedsUpdate(false);
64
65 // close existing server if it has a different port
66 if(this->isListening() && (this->getPortNumber() != this->PortNumber))
67 this->stopServer();
68
69 try {
70 if(!this->isListening())
71 this->startServer(this->PortNumber);
72 } catch(const std::exception &e) {
73 this->printErr("Unable to start server:\n" + std::string(e.what()));
74 return 0;
75 }
76
77 if(neededUpdate) {
78 // if needed update then the client send data to the server which was parsed
79 // and is now passed on as the output of the filter
80 auto output = vtkMultiBlockDataSet::GetData(outputVector);
81 output->ShallowCopy(this->LastOutput);
82 } else {
83 // in any other case (i.e., if the input or parameters changed) then send
84 // the last input to the client
85 auto input = vtkDataObject::GetData(inputVector[0]);
86 this->LastInput
87 = vtkSmartPointer<vtkDataObject>::Take(input->NewInstance());
88 this->LastInput->ShallowCopy(input);
89 if(!this->processEvent("on_message", "RequestInputVtkDataSet"))
90 return 0;
91 }
92 return 1;
93}
94
95int ttkWebSocketIO::processEvent(const std::string &eventName,
96 const std::string &eventData) {
97 if(eventData.compare("RequestInputVtkDataSet") == 0) {
98 if(!this->SendVtkDataObject(this->LastInput))
99 return 0;
100 } else if(eventData.rfind("{\"vtkDataSet", 12) == 0) {
101 if(!this->ParseVtkDataObjectFromJSON(eventData))
102 return 0;
103 }
104
105 return WebSocketIO::processEvent(eventName, eventData);
106}
107
108template <typename T>
109T jsonGetValue(const boost::property_tree::ptree &pt,
110 const boost::property_tree::ptree::key_type &key) {
111 return pt.get_child(key).get_value<T>();
112}
113
114template <typename T>
115void jsonArrayToArray(const boost::property_tree::ptree &pt,
116 const boost::property_tree::ptree::key_type &key,
117 T *result) {
118 std::vector<double> values;
120 jsonGetValue<std::string>(pt, key), values);
121
122 for(size_t i = 0, j = values.size(); i < j; i++) {
123 result[i] = (T)values[i];
124 }
125}
126
127static bool jsonHasChild(const boost::property_tree::ptree &pt,
128 const boost::property_tree::ptree::key_type &key) {
129 return pt.find(key) != pt.not_found();
130}
131
132int ttkWebSocketIO::ParseVtkDataObjectFromJSON(const std::string &json) {
133 ttk::Timer timer;
134 this->printMsg("Parsing vtkDataObject from JSON string", 0, 0,
136
138
139 // parse lastClientInput into json
140 boost::property_tree::ptree pt;
141 try {
142 std::stringstream ss;
143 ss << json;
144 boost::property_tree::read_json(ss, pt);
145 } catch(const std::exception &e) {
146 this->printErr("Unable to parse JSON message: " + json);
147 return 0;
148 }
149
150 // check if a path exists in property_tree or a key exists in json object
151
152 size_t b = 0;
153 for(auto it = pt.begin(); it != pt.end(); ++it) {
155
156 auto jsonVtkDataSet = it->second;
157 if(!jsonHasChild(jsonVtkDataSet, "className")
158 || !jsonHasChild(jsonVtkDataSet, "points")
159 || !jsonHasChild(jsonVtkDataSet, "cells")
160 || !jsonHasChild(jsonVtkDataSet, "pointData")
161 || !jsonHasChild(jsonVtkDataSet, "cellData")
162 || !jsonHasChild(jsonVtkDataSet, "fieldData")) {
163 this->printErr("Invalid vtkDataSet serialization.");
164 return 0;
165 }
166
167 if(jsonGetValue<std::string>(jsonVtkDataSet, "className")
168 .compare("vtkUnstructuredGrid")
169 != 0) {
170 this->printErr(
171 "Currently WebSocketIO only supports parsing 'vtkUnstructuredGrids'.");
172 return 0;
173 }
174
175 // parse points
176 {
177 auto nPoints
178 = jsonGetValue<int>(jsonVtkDataSet, "points.coordinates.nTuples");
179
181 points->SetNumberOfPoints(nPoints);
182
183 jsonArrayToArray<float>(jsonVtkDataSet, "points.coordinates.data",
184 (float *)ttkUtils::GetVoidPointer(points));
185
186 block->SetPoints(points);
187 }
188
189 // parse cells
190 {
191 // VTK CELL TYPES MAP
192 constexpr int vtkCellsTypeHash[20] = {VTK_EMPTY_CELL,
193 VTK_VERTEX,
194 VTK_LINE,
195 VTK_TRIANGLE,
196 VTK_TETRA,
197 VTK_CONVEX_POINT_SET,
198 VTK_CONVEX_POINT_SET,
199 VTK_CONVEX_POINT_SET,
200 VTK_VOXEL,
201 VTK_CONVEX_POINT_SET,
202 VTK_CONVEX_POINT_SET,
203 VTK_CONVEX_POINT_SET,
204 VTK_CONVEX_POINT_SET,
205 VTK_CONVEX_POINT_SET,
206 VTK_CONVEX_POINT_SET,
207 VTK_CONVEX_POINT_SET,
208 VTK_CONVEX_POINT_SET,
209 VTK_CONVEX_POINT_SET,
210 VTK_CONVEX_POINT_SET,
211 VTK_CONVEX_POINT_SET};
212
213 auto nOffsets
214 = jsonGetValue<int>(jsonVtkDataSet, "cells.offsetsArray.nTuples");
216 offsets->SetNumberOfTuples(nOffsets);
217 auto offsetsData = (vtkIdType *)ttkUtils::GetVoidPointer(offsets);
218 jsonArrayToArray<vtkIdType>(
219 jsonVtkDataSet, "cells.offsetsArray.data", offsetsData);
220
221 auto nConnectivity
222 = jsonGetValue<int>(jsonVtkDataSet, "cells.connectivityArray.nTuples");
223 auto connectivity = vtkSmartPointer<vtkIdTypeArray>::New();
224 connectivity->SetNumberOfTuples(nConnectivity);
225 jsonArrayToArray<vtkIdType>(
226 jsonVtkDataSet, "cells.connectivityArray.data",
227 (vtkIdType *)ttkUtils::GetVoidPointer(connectivity));
228
230 cells->SetData(offsets, connectivity);
231
233 cellTypes->SetNumberOfTuples(nOffsets - 1);
234 auto cellTypesData = (unsigned char *)ttkUtils::GetVoidPointer(cellTypes);
235 for(int i = 1; i < nOffsets; i++) {
236 cellTypesData[i - 1]
237 = vtkCellsTypeHash[offsetsData[i] - offsetsData[i - 1]];
238 }
239
240 block->SetCells(cellTypes, cells);
241 }
242
243 auto addArraysToAssociation = [](vtkFieldData *fd,
244 const boost::property_tree::ptree &fdJSON,
245 const std::string &fdKey) {
246 // parse point data
247 for(const auto &pda : fdJSON.get_child(fdKey)) {
248 const auto &jsonArray = pda.second;
249 size_t nComponents = jsonGetValue<size_t>(jsonArray, "nComponents");
250 size_t nTuples = jsonGetValue<size_t>(jsonArray, "nTuples");
251
253 vtkDataArray::CreateArray(jsonGetValue<int>(jsonArray, "dataType")));
254
255 array->SetName(jsonGetValue<std::string>(jsonArray, "name").data());
256 array->SetNumberOfComponents(nComponents);
257 array->SetNumberOfTuples(nTuples);
258
259 if(auto dataArray = vtkDataArray::SafeDownCast(array)) {
260 switch(dataArray->GetDataType()) {
261 vtkTemplateMacro(jsonArrayToArray<VTK_TT>(
262 jsonArray, "data",
263 (VTK_TT *)ttkUtils::GetVoidPointer(dataArray)));
264 }
265 } else if(auto stringArray = vtkStringArray::SafeDownCast(array)) {
266 std::vector<std::string> values;
268 jsonGetValue<std::string>(jsonArray, "data"), values);
269
270 for(size_t i = 0; i < values.size(); i++)
271 stringArray->SetValue(i, values[i]);
272 } else {
273 return 0;
274 }
275
276 fd->AddArray(array);
277 }
278 return 1;
279 };
280
281 if(!addArraysToAssociation(
282 block->GetPointData(), jsonVtkDataSet, "pointData")) {
283 this->printErr("Unsupported data type.");
284 return 0;
285 }
286 if(!addArraysToAssociation(
287 block->GetCellData(), jsonVtkDataSet, "cellData")) {
288 this->printErr("Unsupported data type.");
289 return 0;
290 }
291 if(!addArraysToAssociation(
292 block->GetFieldData(), jsonVtkDataSet, "fieldData")) {
293 this->printErr("Unsupported data type.");
294 return 0;
295 }
296
297 this->LastOutput->SetBlock(b++, block);
298 }
299
300 this->printMsg(
301 "Parsing vtkDataObject from JSON string", 1, timer.getElapsedTime());
302
303 this->SetNeedsUpdate(true);
304
305 return 1;
306}
307
308int ttkWebSocketIO::SendVtkDataObject(vtkDataObject *object) {
309 ttk::Timer timer;
310 this->printMsg(
311 "Serializing vtkDataObject", 0, 0, ttk::debug::LineMode::REPLACE);
312
313 // clear queue
314 this->clearMessageQueue();
315
317 if(object->IsA("vtkMultiBlockDataSet"))
318 objectAsMB->ShallowCopy(object);
319 else
320 objectAsMB->SetBlock(0, object);
321
322 const size_t nBlocks = objectAsMB->GetNumberOfBlocks();
323 for(size_t b = 0; b < nBlocks; b++) {
324 auto block = vtkDataSet::SafeDownCast(objectAsMB->GetBlock(b));
325 if(!block) {
326 this->printErr("Input `vtkDataObject` is not a `vtkDataSet` or a "
327 "`vtkMultiBlockDataSet` containing only `vtkDataSets`.");
328 return 0;
329 }
330
331 // send class name
332 this->queueMessage("{ \"className\": \""
333 + std::string(block->GetClassName()) + "\" }");
334
335 this->printMsg(
336 "Serializing vtkDataObject", 0.2, 0, ttk::debug::LineMode::REPLACE);
337
338 // send image meta data if last input is a vtkImageData
339 if(block->IsA("vtkImageData")) {
340 auto lastInputAsID = vtkImageData::SafeDownCast(block);
341 int dimension[3];
342 double origin[3];
343 double spacing[3];
344 lastInputAsID->GetDimensions(dimension);
345 lastInputAsID->GetOrigin(origin);
346 lastInputAsID->GetSpacing(spacing);
347
348 this->queueMessage(
349 "{"
350 "\"dimension\": ["
351 + std::to_string(dimension[0]) + "," + std::to_string(dimension[1])
352 + "," + std::to_string(dimension[2])
353 + "],"
354 "\"origin\": ["
355 + std::to_string(origin[0]) + "," + std::to_string(origin[1]) + ","
356 + std::to_string(origin[2])
357 + "],"
358 "\"spacing\": ["
359 + std::to_string(spacing[0]) + "," + std::to_string(spacing[1]) + ","
360 + std::to_string(spacing[2])
361 + "]"
362 "}");
363 }
364
365 this->printMsg(
366 "Serializing vtkDataObject", 0.4, 0, ttk::debug::LineMode::REPLACE);
367
368 // send points if last input is a vtkPointSet
369 auto blockAsPS = vtkPointSet::SafeDownCast(block);
370 if(blockAsPS != nullptr) {
371 auto points = blockAsPS->GetPoints();
372 this->queueMessage(
373 "{"
374 "\"target\": \"points\","
375 "\"name\": \"coordinates\","
376 "\"dataType\":"
377 + std::to_string(points ? points->GetDataType() : VTK_FLOAT)
378 + ","
379 "\"nTuples\":"
380 + std::to_string(blockAsPS->GetNumberOfPoints())
381 + ","
382 "\"nComponents\": 3"
383 "}");
384 if(blockAsPS->GetNumberOfPoints() > 0 && points != nullptr)
385 switch(points->GetDataType()) {
386 vtkTemplateMacro(this->queueMessage(
387 blockAsPS->GetNumberOfPoints() * sizeof(VTK_TT) * 3,
388 ttkUtils::GetVoidPointer(points)));
389 }
390 }
391
392 this->printMsg(
393 "Serializing vtkDataObject", 0.6, 0, ttk::debug::LineMode::REPLACE);
394
395 // send cells if last input is vtkUnstructuredGrid or vtkPolyData
396 {
397 vtkCellArray *cells = nullptr;
398
399 if(block->IsA("vtkUnstructuredGrid")) {
400 auto blockAsUG = vtkUnstructuredGrid::SafeDownCast(block);
401 if(blockAsUG != nullptr) {
402 cells = blockAsUG->GetCells();
403 }
404 } else if(block->IsA("vtkPolyData")) {
405 auto blockAsPD = vtkPolyData::SafeDownCast(block);
406 if(blockAsPD != nullptr) {
407 cells = blockAsPD->GetPolys() ? blockAsPD->GetPolys()
408 : blockAsPD->GetLines();
409 }
410 }
411
412 if(cells) {
413 {
414 auto connectivityArray = cells->GetConnectivityArray();
415 this->queueMessage(
416 "{"
417 "\"target\": \"cells\","
418 "\"name\": \"connectivityArray\","
419 "\"dataType\":"
420 + std::to_string(VTK_ID_TYPE)
421 + ","
422 "\"nTuples\":"
423 + std::to_string(connectivityArray->GetNumberOfTuples())
424 + ","
425 "\"nComponents\": 1"
426 "}");
427 if(connectivityArray->GetNumberOfTuples() > 0)
428 this->queueMessage(
429 connectivityArray->GetNumberOfTuples() * sizeof(vtkIdType),
430 ttkUtils::GetVoidPointer(connectivityArray));
431 }
432
433 {
434 auto offsetsArray = cells->GetOffsetsArray();
435 this->queueMessage("{"
436 "\"target\": \"cells\","
437 "\"name\": \"offsetsArray\","
438 "\"dataType\":"
439 + std::to_string(VTK_ID_TYPE)
440 + ","
441 "\"nTuples\":"
442 + std::to_string(offsetsArray->GetNumberOfTuples())
443 + ","
444 "\"nComponents\": 1"
445 "}");
446 if(offsetsArray->GetNumberOfTuples() > 0)
447 this->queueMessage(
448 offsetsArray->GetNumberOfTuples() * sizeof(vtkIdType),
449 ttkUtils::GetVoidPointer(offsetsArray));
450 }
451 }
452 }
453
454 this->printMsg(
455 "Serializing vtkDataObject", 0.8, 0, ttk::debug::LineMode::REPLACE);
456
457 // send point, cell, and field data
458 {
459 std::vector<std::pair<std::string, vtkFieldData *>> attributes
460 = {{"pointData", block->GetPointData()},
461 {"cellData", block->GetCellData()},
462 {"fieldData", block->GetFieldData()}};
463
464 for(auto &attribute : attributes) {
465 size_t nArrays = attribute.second->GetNumberOfArrays();
466
467 for(size_t i = 0; i < nArrays; i++) {
468 auto array = attribute.second->GetAbstractArray(i);
469
470 if(array->IsA("vtkDataArray")) {
471 auto dataArray = vtkDataArray::SafeDownCast(array);
472 if(dataArray == nullptr) {
473 continue;
474 }
475 this->queueMessage(
476 "{"
477 "\"target\":\""
478 + attribute.first
479 + "\","
480 "\"name\":\""
481 + std::string(dataArray->GetName())
482 + "\","
483 "\"dataType\":"
484 + std::to_string(dataArray->GetDataType())
485 + ","
486 "\"nTuples\":"
487 + std::to_string(dataArray->GetNumberOfTuples())
488 + ","
489 "\"nComponents\":"
490 + std::to_string(dataArray->GetNumberOfComponents()) + "}");
491 if(dataArray->GetNumberOfValues() > 0)
492 switch(dataArray->GetDataType()) {
493 vtkTemplateMacro(this->queueMessage(
494 dataArray->GetNumberOfValues() * sizeof(VTK_TT),
495 ttkUtils::GetVoidPointer(dataArray)));
496 }
497 } else if(array->IsA("vtkStringArray")) {
498 auto stringArray = vtkStringArray::SafeDownCast(array);
499 std::string values;
500 values += stringArray->GetValue(0);
501 for(int j = 1; j < stringArray->GetNumberOfValues(); j++)
502 values += "," + stringArray->GetValue(j);
503
504 this->queueMessage(
505 "{"
506 "\"target\":\""
507 + attribute.first
508 + "\","
509 "\"name\":\""
510 + std::string(stringArray->GetName())
511 + "\","
512 "\"dataType\":"
513 + std::to_string(stringArray->GetDataType())
514 + ","
515 "\"nTuples\":"
516 + std::to_string(stringArray->GetNumberOfTuples())
517 + ","
518 "\"nComponents\":"
519 + std::to_string(stringArray->GetNumberOfComponents())
520 + ","
521 "\"data\":\""
522 + boost::replace_all_copy(values, "\"", "\\\"")
523 + "\""
524 "}");
525 }
526 }
527 }
528 }
529 }
530
531 this->printMsg("Serializing vtkDataObject", 1, timer.getElapsedTime());
532
533 this->processMessageQueue();
534
535 return 1;
536}
#define ttkNotUsed(x)
Mark function/method parameters that are not used in the function body at all.
Definition: BaseClass.h:47
static void * GetVoidPointer(vtkDataArray *array, vtkIdType start=0)
Definition: ttkUtils.cpp:225
static int stringListToDoubleVector(const std::string &iString, std::vector< double > &v)
Definition: ttkUtils.cpp:133
static int stringListToVector(const std::string &iString, std::vector< std::string > &v)
Definition: ttkUtils.cpp:115
int FillOutputPortInformation(int port, vtkInformation *info) override
virtual void SetNeedsUpdate(bool)
int RequestData(vtkInformation *request, vtkInformationVector **inputVector, vtkInformationVector *outputVector) override
int processEvent(const std::string &eventName, const std::string &eventData="") override
virtual bool GetNeedsUpdate()
int SendVtkDataObject(vtkDataObject *object)
~ttkWebSocketIO() override
int FillInputPortInformation(int port, vtkInformation *info) override
int ParseVtkDataObjectFromJSON(const std::string &json)
int printMsg(const std::string &msg, const debug::Priority &priority=debug::Priority::INFO, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cout) const
Definition: Debug.h:118
int printErr(const std::string &msg, const debug::LineMode &lineMode=debug::LineMode::NEW, std::ostream &stream=std::cerr) const
Definition: Debug.h:149
double getElapsedTime()
Definition: Timer.h:15
int queueMessage(const std::string &msg)
int startServer(int PortNumber)
T jsonGetValue(const boost::property_tree::ptree &pt, const boost::property_tree::ptree::key_type &key)
vtkStandardNewMacro(ttkWebSocketIO)
void jsonArrayToArray(const boost::property_tree::ptree &pt, const boost::property_tree::ptree::key_type &key, T *result)