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.

ovpCBoxAlgorithmP300TactileVisualization.cpp 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  1. #include "ovpCBoxAlgorithmP300TactileVisualization.h"
  2. using namespace OpenViBE;
  3. using namespace /*OpenViBE::*/Kernel;
  4. using namespace /*OpenViBE::*/Plugins;
  5. using namespace /*OpenViBE::Plugins::*/Tactilebci;
  6. // This callback flushes all accumulated stimulations to the TCP Tagging
  7. // after the rendering has completed.
  8. static gboolean FlushCB(gpointer data)
  9. {
  10. (static_cast<CBoxAlgorithmP300TactileVisualization*>(data))->flushQueue();
  11. return false; // Only run once
  12. }
  13. bool CBoxAlgorithmP300TactileVisualization::initialize()
  14. {
  15. // ------ Init decoder/encoder
  16. m_SequenceInputDecoder.initialize(*this, 0);
  17. m_TargetInputDecoder.initialize(*this, 1);
  18. m_ResultInputDecoder.initialize(*this, 2);
  19. m_ResultOutputEncoder.initialize(*this, 0);
  20. // ------ Get box settings
  21. m_InterfaceFilename = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0);
  22. m_RowBase = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1);
  23. m_nTactilos = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2);
  24. m_FreeSpelling = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 3);
  25. //set m_nTactilos minimum number of 2, if lower
  26. if(m_nTactilos < 2)
  27. {
  28. m_nTactilos = 2;
  29. }
  30. //set m_nTactilos maximum number of MAX_TACTILOS(10), if higher
  31. if(m_nTactilos > MAX_TACTILOS)
  32. {
  33. m_nTactilos = MAX_TACTILOS;
  34. }
  35. // ------ Setup acquisition server TCP tagging
  36. m_stimuliQueue.clear();
  37. m_stimulusSender = TCPTagging::CreateStimulusSender();
  38. if(!m_stimulusSender->connect("localhost", "15361"))
  39. {
  40. this->getLogManager() << LogLevel_Warning << "Failed to connect to acquisition server TCP tagging.\n";
  41. }
  42. // ------ Gtk variables
  43. // Build main UI elements
  44. m_MainWidgetInterface = gtk_builder_new();
  45. if(!gtk_builder_add_from_file(m_MainWidgetInterface, m_InterfaceFilename.toASCIIString(), nullptr))//error
  46. {
  47. this->getLogManager() << LogLevel_ImportantWarning << "Failed to load UI file [" << m_InterfaceFilename << "]\n";
  48. this->getLogManager() << LogLevel_ImportantWarning << "Make sure, that the given filename and path are correct\n";
  49. return(false);
  50. }
  51. m_MainWindow = GTK_WIDGET(gtk_builder_get_object(m_MainWidgetInterface, "p300-tactile-main"));
  52. m_Table = GTK_TABLE(gtk_builder_get_object(m_MainWidgetInterface, "p300-tactile-table"));
  53. m_ResultLabel = GTK_LABEL(gtk_builder_get_object(m_MainWidgetInterface, "label-result"));
  54. m_TargetLabel = GTK_LABEL(gtk_builder_get_object(m_MainWidgetInterface, "label-target"));
  55. // Create font description
  56. m_FontDesc = pango_font_description_copy(pango_context_get_font_description(gtk_widget_get_pango_context(m_MainWindow)));
  57. pango_font_description_set_size(m_FontDesc, gint(FontSize*PANGO_SCALE));
  58. // test if the UI-file fits the number of tactilos
  59. guint nRow = 0;
  60. g_object_get(m_Table, "n-rows", &nRow, nullptr);
  61. if(m_nTactilos != nRow)
  62. {
  63. this->getLogManager() << LogLevel_ImportantWarning << "The number of tactilos " << m_nTactilos<<" used and the number of rows "<< nRow << " in the ui file must be the same.\n";
  64. this->getLogManager() << LogLevel_ImportantWarning << "Make sure to choose the right UI-File, e.g. p300-tactile-6.ui for 6 tactilos\n";
  65. return(false);
  66. }
  67. else
  68. {
  69. for(uint64_t i = 0; i < m_nTactilos; i++)
  70. {
  71. std::string eventbox_id = "eventbox-" + std::to_string(i+1);
  72. m_EventBox.push_back(GTK_WIDGET(gtk_builder_get_object(m_MainWidgetInterface, eventbox_id.c_str())));
  73. }
  74. }
  75. // ------ Setup tactile UI
  76. //Create UI for m_nTactilos = 6
  77. if(m_nTactilos == 6)
  78. {
  79. //create free spelling UI for 6 tactilos
  80. if(m_FreeSpelling)
  81. {
  82. //mainmenu
  83. m_Menu.push_back(TactileMenu(6));
  84. m_Menu[0].set_LabelText(0, "Ja");
  85. m_Menu[0].set_LabelText(1, "Nein");
  86. m_Menu[0].set_LabelText(2, "Geräte");
  87. m_Menu[0].set_LabelText(3, "Körper");
  88. m_Menu[0].set_LabelText(4, "Hilfe");
  89. m_Menu[0].set_LabelText(5, "Schmerzen");
  90. //submenu Geräte
  91. m_Menu.push_back(TactileMenu(6));
  92. m_Menu[1].set_LabelText(0, "Ja");
  93. m_Menu[1].set_LabelText(1, "Nein");
  94. m_Menu[1].set_LabelText(2, "Atemgerät");
  95. m_Menu[1].set_LabelText(3, "Computer");
  96. m_Menu[1].set_LabelText(4, "Rollstuhl");
  97. m_Menu[1].set_LabelText(5, "Hauptmenü");
  98. //submenu Körper
  99. m_Menu.push_back(TactileMenu(6));
  100. m_Menu[2].set_LabelText(0, "Ja");
  101. m_Menu[2].set_LabelText(1, "Nein");
  102. m_Menu[2].set_LabelText(2, "Hunger/Durst");
  103. m_Menu[2].set_LabelText(3, "warm/kalt");
  104. m_Menu[2].set_LabelText(4, "Müdigkeit");
  105. m_Menu[2].set_LabelText(5, "Hauptmenü");
  106. //submenu Hilfe
  107. m_Menu.push_back(TactileMenu(6));
  108. m_Menu[3].set_LabelText(0, "Ja");
  109. m_Menu[3].set_LabelText(1, "Nein");
  110. m_Menu[3].set_LabelText(2, "Lage");
  111. m_Menu[3].set_LabelText(3, "Atemnot");
  112. m_Menu[3].set_LabelText(4, "Anderes Problem");
  113. m_Menu[3].set_LabelText(5, "Hauptmenü");
  114. //submenu Schmerzen
  115. m_Menu.push_back(TactileMenu(6));
  116. m_Menu[3].set_LabelText(0, "Ja");
  117. m_Menu[3].set_LabelText(1, "Nein");
  118. m_Menu[3].set_LabelText(2, "Kopf");
  119. m_Menu[3].set_LabelText(3, "Torso");
  120. m_Menu[3].set_LabelText(4, "Glieder");
  121. m_Menu[3].set_LabelText(5, "Hauptmenü");
  122. //set SubMenu ptr
  123. m_Menu[0].set_SubMenu(2, &m_Menu[1]);//main --> Geräte
  124. m_Menu[0].set_SubMenu(3, &m_Menu[2]);//main --> Körper
  125. m_Menu[0].set_SubMenu(4, &m_Menu[3]);//main --> Hilfe
  126. m_Menu[0].set_SubMenu(5, &m_Menu[4]);//main --> Schmerzen
  127. m_Menu[1].set_SubMenu(5, &m_Menu[0]);//Geräte --> main
  128. m_Menu[2].set_SubMenu(5, &m_Menu[0]);//Körper --> main
  129. m_Menu[3].set_SubMenu(5, &m_Menu[0]);//Hilfe --> main
  130. m_Menu[3].set_SubMenu(5, &m_Menu[0]);//Schmerzen --> main
  131. //set curr Menu ptr to main menu
  132. m_CurrMenu = &m_Menu[0];
  133. }
  134. //create copy spelling UI for 6 tactilos
  135. else
  136. {
  137. m_Menu.push_back(TactileMenu(6));
  138. m_Menu[0].set_LabelText(0, "Bein links");
  139. m_Menu[0].set_LabelText(1, "Bein rechts");
  140. m_Menu[0].set_LabelText(2, "Arm links");
  141. m_Menu[0].set_LabelText(3, "Arm rechts");
  142. m_Menu[0].set_LabelText(4, "Brust");
  143. m_Menu[0].set_LabelText(5, "Rücken");
  144. m_CurrMenu = &m_Menu[0];
  145. }
  146. }
  147. //Create default UI for m_nTactilos != 6
  148. else
  149. {
  150. m_Menu.push_back(TactileMenu(m_nTactilos));
  151. m_CurrMenu = &m_Menu[0];
  152. }
  153. // ------ Init UI
  154. m_visualizationCtx = dynamic_cast<VisualizationToolkit::IVisualizationContext*>(this->createPluginObject(OVP_ClassId_Plugin_VisualizationCtx));
  155. m_visualizationCtx->setWidget(*this, m_MainWindow);
  156. for(uint64_t i = 0; i < m_nTactilos; ++i)
  157. {
  158. resetColor();
  159. gtk_widget_modify_font(gtk_bin_get_child(GTK_BIN(m_EventBox[i])), m_FontDesc);
  160. }
  161. toggleLabelText();
  162. return(true);
  163. }
  164. /*******************************************************************************/
  165. bool CBoxAlgorithmP300TactileVisualization::uninitialize()
  166. {
  167. // ------ uninitialize decoder/encoder
  168. m_SequenceInputDecoder.uninitialize();
  169. m_TargetInputDecoder.uninitialize();
  170. m_ResultInputDecoder.uninitialize();
  171. m_ResultOutputEncoder.uninitialize();
  172. // ------ uninitialize GTK UI
  173. if (m_MainWidgetInterface)
  174. {
  175. g_object_unref(m_MainWidgetInterface);
  176. m_MainWidgetInterface = nullptr;
  177. }
  178. if (m_visualizationCtx)
  179. {
  180. this->releasePluginObject(m_visualizationCtx);
  181. }
  182. // ------ uinitialize TCP Tagging
  183. if (m_idleFuncTag != 0)
  184. {
  185. m_stimuliQueue.clear();
  186. g_source_remove(m_idleFuncTag);
  187. m_idleFuncTag = 0;
  188. }
  189. if (m_stimulusSender)
  190. {
  191. delete m_stimulusSender;
  192. m_stimulusSender = nullptr;
  193. }
  194. return(true);
  195. }
  196. /*******************************************************************************/
  197. bool CBoxAlgorithmP300TactileVisualization::processInput(const size_t index)
  198. {
  199. // ------ ready to process
  200. getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
  201. return(true);
  202. }
  203. /*******************************************************************************/
  204. bool CBoxAlgorithmP300TactileVisualization::process()
  205. {
  206. const IBox& staticBoxContext=this->getStaticBoxContext();
  207. IBoxIO& boxContext = this->getDynamicBoxContext();
  208. //variables for processing of input chunks
  209. uint64_t SequenceID = 0;
  210. uint64_t TargetID = 0;
  211. uint64_t ResultID = 0;
  212. int Row = -1;
  213. bool IsTarget = false;
  214. bool TargetReceived = false;
  215. bool ResultReceived = false;
  216. //variables for forwarding of target stimulations
  217. uint64_t ChunkStartTime = 0;
  218. uint64_t ChunkEndTime = 0;
  219. uint64_t Size = 0;
  220. const uint8_t* Buffer = nullptr;
  221. // ------ Sequence stimulation
  222. for(uint64_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
  223. {
  224. m_SequenceInputDecoder.decode(i);
  225. m_LastTime = boxContext.getInputChunkEndTime(0, i);
  226. // if header received
  227. if(m_SequenceInputDecoder.isHeaderReceived())
  228. {}
  229. // if buffer received
  230. if(m_SequenceInputDecoder.isBufferReceived())
  231. {
  232. //check received stimulations on sequence input
  233. CStimulationSet* SequenceStimulationSet = m_SequenceInputDecoder.getOutputStimulationSet();
  234. for(uint64_t j = 0; j < SequenceStimulationSet->size(); ++j)
  235. {
  236. SequenceID = SequenceStimulationSet->getId(j);
  237. if(SequenceID >= m_RowBase && SequenceID < m_RowBase + m_nTactilos)
  238. {
  239. Row = SequenceID - m_RowBase;
  240. IsTarget = (Row == (int)m_LastTarget);
  241. }
  242. if(SequenceID == OVTK_StimulationId_VisualStimulationStart)
  243. {
  244. resetColor();
  245. toggleFlashColor(Row);
  246. if (IsTarget)
  247. {
  248. m_stimuliQueue.push_back(OVTK_StimulationId_Target);
  249. }
  250. else
  251. {
  252. m_stimuliQueue.push_back(OVTK_StimulationId_NonTarget);
  253. }
  254. }
  255. if(SequenceID == OVTK_StimulationId_VisualStimulationStop)
  256. {
  257. resetColor();
  258. }
  259. if(SequenceID == OVTK_StimulationId_SegmentStart)
  260. {
  261. toggleLabelText();
  262. }
  263. m_stimuliQueue.push_back(SequenceID);
  264. }
  265. }
  266. // if end received
  267. if(m_SequenceInputDecoder.isEndReceived())
  268. {}
  269. boxContext.markInputAsDeprecated(0, i);
  270. }
  271. // ------ Target stimulation
  272. for(uint64_t i = 0; i < boxContext.getInputChunkCount(1); ++i)
  273. {
  274. if(m_LastTime >= boxContext.getInputChunkStartTime(1, i))
  275. {
  276. m_TargetInputDecoder.decode(i);
  277. // if header received
  278. if(m_SequenceInputDecoder.isHeaderReceived())
  279. {}
  280. // if buffer received
  281. if(m_SequenceInputDecoder.isBufferReceived())
  282. {
  283. // check received dtimulations on Target input
  284. CStimulationSet* TargetStimulationSet = m_TargetInputDecoder.getOutputStimulationSet();
  285. for(uint64_t j = 0; j < TargetStimulationSet->size(); ++j)
  286. {
  287. TargetID = TargetStimulationSet->getId(j);
  288. if(TargetID >= m_RowBase && TargetID < m_RowBase + m_nTactilos)
  289. {
  290. Row = TargetID - m_RowBase;
  291. TargetReceived = true;
  292. }
  293. if(TargetReceived)
  294. {
  295. toggleTargetColor(Row);
  296. m_stimuliQueue.push_back(TargetID);
  297. std::string TargetLabel = gtk_label_get_text(m_TargetLabel);
  298. TargetLabel += gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(m_EventBox[Row]))));
  299. gtk_label_set_text(m_TargetLabel, TargetLabel.c_str());
  300. m_LastTarget = Row;
  301. }
  302. }
  303. }
  304. // if end received
  305. if(m_SequenceInputDecoder.isEndReceived())
  306. {}
  307. boxContext.markInputAsDeprecated(1, i);
  308. }
  309. }
  310. // ------ Result stimulation
  311. for(uint64_t i = 0; i < boxContext.getInputChunkCount(2); ++i)
  312. {
  313. if(m_LastTime >= boxContext.getInputChunkStartTime(2, i))
  314. {
  315. m_ResultInputDecoder.decode(i);
  316. // if header received
  317. if(m_SequenceInputDecoder.isHeaderReceived())
  318. {}
  319. // if buffer received
  320. if(m_SequenceInputDecoder.isBufferReceived())
  321. {
  322. // check received stimulations on result input
  323. CStimulationSet* ResultStimulationSet = m_ResultInputDecoder.getOutputStimulationSet();
  324. for(uint64_t j = 0; j < ResultStimulationSet->size(); ++j)
  325. {
  326. ResultID = ResultStimulationSet->getId(j);
  327. if(ResultID >= m_RowBase && ResultID < m_RowBase + m_nTactilos)
  328. {
  329. Row = ResultID - m_RowBase;
  330. ResultReceived = true;
  331. }
  332. if(ResultReceived)
  333. {
  334. toggleResultColor(Row);
  335. std::string ResultLabel = gtk_label_get_text(m_ResultLabel);
  336. ResultLabel += gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(m_EventBox[Row]))));
  337. gtk_label_set_text(m_ResultLabel, ResultLabel.c_str());
  338. //switch current menu ptr to submenu
  339. if(m_CurrMenu->get_SubMenu(Row) != nullptr)
  340. {
  341. m_CurrMenu = m_CurrMenu->get_SubMenu(Row);
  342. }
  343. }
  344. }
  345. }
  346. // if end received
  347. if(m_SequenceInputDecoder.isEndReceived())
  348. {}
  349. // forward target stimulations
  350. boxContext.getInputChunk(2, i, ChunkStartTime, ChunkEndTime, Size, Buffer);
  351. boxContext.appendOutputChunkData(0, Buffer, Size);
  352. boxContext.markOutputAsReadyToSend(0, ChunkStartTime, ChunkEndTime);
  353. boxContext.markInputAsDeprecated(2, i);
  354. }
  355. }
  356. // ------ Send accumulated stimuli to the acquisition server
  357. if(m_idleFuncTag == 0)
  358. {
  359. m_idleFuncTag = g_idle_add(FlushCB, this);
  360. }
  361. return(true);
  362. }
  363. // ------ Function Definitions
  364. // Sends all accumulated stimuli to the TCP Tagging
  365. void CBoxAlgorithmP300TactileVisualization::flushQueue()
  366. {
  367. for (const auto& stimulation : m_stimuliQueue)
  368. {
  369. m_stimulusSender->sendStimulation(stimulation);
  370. }
  371. m_stimuliQueue.clear();
  372. // This function will be automatically removed after completion, so set to 0
  373. m_idleFuncTag = 0;
  374. }
  375. // Change labels fore-/background
  376. void CBoxAlgorithmP300TactileVisualization::toggleFlashColor(uint64_t id)
  377. {
  378. gtk_widget_modify_fg(gtk_bin_get_child(GTK_BIN(m_EventBox[id])), GTK_STATE_NORMAL, &m_FlashFG);
  379. gtk_widget_modify_bg(m_EventBox[id], GTK_STATE_NORMAL, &m_FlashBG);
  380. }
  381. void CBoxAlgorithmP300TactileVisualization::toggleTargetColor(uint64_t id)
  382. {
  383. gtk_widget_modify_fg(gtk_bin_get_child(GTK_BIN(m_EventBox[id])), GTK_STATE_NORMAL, &m_TargetFG);
  384. gtk_widget_modify_bg(m_EventBox[id], GTK_STATE_NORMAL, &m_TargetBG);
  385. }
  386. void CBoxAlgorithmP300TactileVisualization::toggleResultColor(uint64_t id)
  387. {
  388. gtk_widget_modify_fg(gtk_bin_get_child(GTK_BIN(m_EventBox[id])), GTK_STATE_NORMAL, &m_ResultFG);
  389. gtk_widget_modify_bg(m_EventBox[id], GTK_STATE_NORMAL, &m_ResultBG);
  390. }
  391. void CBoxAlgorithmP300TactileVisualization::resetColor()
  392. {
  393. for(uint64_t i = 0; i < m_nTactilos; i++)
  394. {
  395. gtk_widget_modify_fg(gtk_bin_get_child(GTK_BIN(m_EventBox[i])), GTK_STATE_NORMAL, &m_NoFlashFG);
  396. gtk_widget_modify_bg(m_EventBox[i], GTK_STATE_NORMAL, &m_NoFlashBG);
  397. }
  398. }
  399. // Change label text
  400. void CBoxAlgorithmP300TactileVisualization::toggleLabelText()
  401. {
  402. for(uint64_t i = 0; i < m_nTactilos; i++)
  403. {
  404. std::string label_text = m_CurrMenu->get_LabelText(i);
  405. gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(m_EventBox[i]))), label_text.c_str());
  406. }
  407. }