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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  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. m_Menu.push_back(TactileMenu(6));
  83. m_Menu[0].set_LabelText(0, "Ja");
  84. m_Menu[0].set_LabelText(1, "Nein");
  85. m_Menu[0].set_LabelText(2, "Pflege");
  86. m_Menu[0].set_LabelText(3, "Hilfe");
  87. m_Menu[0].set_LabelText(4, "Geräte");
  88. m_Menu[0].set_LabelText(5, "Funktionen");
  89. m_Menu.push_back(TactileMenu(6));
  90. m_Menu[1].set_LabelText(0, "Ja");
  91. m_Menu[1].set_LabelText(1, "Nein");
  92. m_Menu[1].set_LabelText(2, "Hunger");
  93. m_Menu[1].set_LabelText(3, "Lage");
  94. m_Menu[1].set_LabelText(4, "Müdigkeit");
  95. m_Menu[1].set_LabelText(5, "Hauptmenü");
  96. m_Menu.push_back(TactileMenu(6));
  97. m_Menu[2].set_LabelText(0, "Ja");
  98. m_Menu[2].set_LabelText(1, "Nein");
  99. m_Menu[2].set_LabelText(2, "Schmerzen");
  100. m_Menu[2].set_LabelText(3, "Atemnot");
  101. m_Menu[2].set_LabelText(4, "Anderes");
  102. m_Menu[2].set_LabelText(5, "Hauptmenü");
  103. m_Menu.push_back(TactileMenu(6));
  104. m_Menu[3].set_LabelText(0, "Ja");
  105. m_Menu[3].set_LabelText(1, "Nein");
  106. m_Menu[3].set_LabelText(2, "Atemgerät");
  107. m_Menu[3].set_LabelText(3, "Rollstuhl");
  108. m_Menu[3].set_LabelText(4, "Computer");
  109. m_Menu[3].set_LabelText(5, "Hauptmenü");
  110. //set SubMenu ptr
  111. m_Menu[0].set_SubMenu(2, &m_Menu[1]);
  112. m_Menu[0].set_SubMenu(3, &m_Menu[2]);
  113. m_Menu[0].set_SubMenu(4, &m_Menu[3]);
  114. m_Menu[1].set_SubMenu(5, &m_Menu[0]);
  115. m_Menu[2].set_SubMenu(5, &m_Menu[0]);
  116. m_Menu[3].set_SubMenu(5, &m_Menu[0]);
  117. //set curr Menu ptr to main menu
  118. m_CurrMenu = &m_Menu[0];
  119. }
  120. //create copy spelling UI for 6 tactilos
  121. else
  122. {
  123. m_Menu.push_back(TactileMenu(6));
  124. m_Menu[0].set_LabelText(0, "Hand links");
  125. m_Menu[0].set_LabelText(1, "Arm links");
  126. m_Menu[0].set_LabelText(2, "Brust links");
  127. m_Menu[0].set_LabelText(3, "Brust rechts");
  128. m_Menu[0].set_LabelText(4, "Arm rechts");
  129. m_Menu[0].set_LabelText(5, "Hand rechts");
  130. m_CurrMenu = &m_Menu[0];
  131. }
  132. }
  133. //Create default UI for m_nTactilos != 6
  134. else
  135. {
  136. m_Menu.push_back(TactileMenu(m_nTactilos));
  137. m_CurrMenu = &m_Menu[0];
  138. }
  139. // ------ Init UI
  140. m_visualizationCtx = dynamic_cast<VisualizationToolkit::IVisualizationContext*>(this->createPluginObject(OVP_ClassId_Plugin_VisualizationCtx));
  141. m_visualizationCtx->setWidget(*this, m_MainWindow);
  142. for(uint64_t i = 0; i < m_nTactilos; ++i)
  143. {
  144. resetColor();
  145. gtk_widget_modify_font(gtk_bin_get_child(GTK_BIN(m_EventBox[i])), m_FontDesc);
  146. }
  147. toggleLabelText();
  148. return(true);
  149. }
  150. /*******************************************************************************/
  151. bool CBoxAlgorithmP300TactileVisualization::uninitialize()
  152. {
  153. // ------ uninitialize decoder/encoder
  154. m_SequenceInputDecoder.uninitialize();
  155. m_TargetInputDecoder.uninitialize();
  156. m_ResultInputDecoder.uninitialize();
  157. m_ResultOutputEncoder.uninitialize();
  158. // ------ uninitialize GTK UI
  159. if (m_MainWidgetInterface)
  160. {
  161. g_object_unref(m_MainWidgetInterface);
  162. m_MainWidgetInterface = nullptr;
  163. }
  164. if (m_visualizationCtx)
  165. {
  166. this->releasePluginObject(m_visualizationCtx);
  167. }
  168. // ------ uinitialize TCP Tagging
  169. if (m_idleFuncTag != 0)
  170. {
  171. m_stimuliQueue.clear();
  172. g_source_remove(m_idleFuncTag);
  173. m_idleFuncTag = 0;
  174. }
  175. if (m_stimulusSender)
  176. {
  177. delete m_stimulusSender;
  178. m_stimulusSender = nullptr;
  179. }
  180. return(true);
  181. }
  182. /*******************************************************************************/
  183. bool CBoxAlgorithmP300TactileVisualization::processInput(const size_t index)
  184. {
  185. // ------ ready to process
  186. getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess();
  187. return(true);
  188. }
  189. /*******************************************************************************/
  190. bool CBoxAlgorithmP300TactileVisualization::process()
  191. {
  192. const IBox& staticBoxContext=this->getStaticBoxContext();
  193. IBoxIO& boxContext = this->getDynamicBoxContext();
  194. //variables for processing of input chunks
  195. uint64_t SequenceID = 0;
  196. uint64_t TargetID = 0;
  197. uint64_t ResultID = 0;
  198. int Row = -1;
  199. bool IsTarget = false;
  200. bool TargetReceived = false;
  201. bool ResultReceived = false;
  202. //variables for forwarding of target stimulations
  203. uint64_t ChunkStartTime = 0;
  204. uint64_t ChunkEndTime = 0;
  205. uint64_t Size = 0;
  206. const uint8_t* Buffer = nullptr;
  207. // ------ Sequence stimulation
  208. for(uint64_t i = 0; i < boxContext.getInputChunkCount(0); ++i)
  209. {
  210. m_SequenceInputDecoder.decode(i);
  211. m_LastTime = boxContext.getInputChunkEndTime(0, i);
  212. // if header received
  213. if(m_SequenceInputDecoder.isHeaderReceived())
  214. {}
  215. // if buffer received
  216. if(m_SequenceInputDecoder.isBufferReceived())
  217. {
  218. //check received stimulations on sequence input
  219. CStimulationSet* SequenceStimulationSet = m_SequenceInputDecoder.getOutputStimulationSet();
  220. for(uint64_t j = 0; j < SequenceStimulationSet->size(); ++j)
  221. {
  222. SequenceID = SequenceStimulationSet->getId(j);
  223. if(SequenceID >= m_RowBase && SequenceID < m_RowBase + m_nTactilos)
  224. {
  225. Row = SequenceID - m_RowBase;
  226. IsTarget = (Row == (int)m_LastTarget);
  227. }
  228. if(SequenceID == OVTK_StimulationId_VisualStimulationStart)
  229. {
  230. resetColor();
  231. toggleFlashColor(Row);
  232. if (IsTarget)
  233. {
  234. m_stimuliQueue.push_back(OVTK_StimulationId_Target);
  235. }
  236. else
  237. {
  238. m_stimuliQueue.push_back(OVTK_StimulationId_NonTarget);
  239. }
  240. }
  241. if(SequenceID == OVTK_StimulationId_VisualStimulationStop)
  242. {
  243. resetColor();
  244. }
  245. if(SequenceID == OVTK_StimulationId_SegmentStart)
  246. {
  247. toggleLabelText();
  248. }
  249. m_stimuliQueue.push_back(SequenceID);
  250. }
  251. }
  252. // if end received
  253. if(m_SequenceInputDecoder.isEndReceived())
  254. {}
  255. boxContext.markInputAsDeprecated(0, i);
  256. }
  257. // ------ Target stimulation
  258. for(uint64_t i = 0; i < boxContext.getInputChunkCount(1); ++i)
  259. {
  260. if(m_LastTime >= boxContext.getInputChunkStartTime(1, i))
  261. {
  262. m_TargetInputDecoder.decode(i);
  263. // if header received
  264. if(m_SequenceInputDecoder.isHeaderReceived())
  265. {}
  266. // if buffer received
  267. if(m_SequenceInputDecoder.isBufferReceived())
  268. {
  269. // check received dtimulations on Target input
  270. CStimulationSet* TargetStimulationSet = m_TargetInputDecoder.getOutputStimulationSet();
  271. for(uint64_t j = 0; j < TargetStimulationSet->size(); ++j)
  272. {
  273. TargetID = TargetStimulationSet->getId(j);
  274. if(TargetID >= m_RowBase && TargetID < m_RowBase + m_nTactilos)
  275. {
  276. Row = TargetID - m_RowBase;
  277. TargetReceived = true;
  278. }
  279. if(TargetReceived)
  280. {
  281. toggleTargetColor(Row);
  282. m_stimuliQueue.push_back(TargetID);
  283. std::string TargetLabel = gtk_label_get_text(m_TargetLabel);
  284. TargetLabel += gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(m_EventBox[Row]))));
  285. gtk_label_set_text(m_TargetLabel, TargetLabel.c_str());
  286. m_LastTarget = Row;
  287. }
  288. }
  289. }
  290. // if end received
  291. if(m_SequenceInputDecoder.isEndReceived())
  292. {}
  293. boxContext.markInputAsDeprecated(1, i);
  294. }
  295. }
  296. // ------ Result stimulation
  297. for(uint64_t i = 0; i < boxContext.getInputChunkCount(2); ++i)
  298. {
  299. if(m_LastTime >= boxContext.getInputChunkStartTime(2, i))
  300. {
  301. m_ResultInputDecoder.decode(i);
  302. // if header received
  303. if(m_SequenceInputDecoder.isHeaderReceived())
  304. {}
  305. // if buffer received
  306. if(m_SequenceInputDecoder.isBufferReceived())
  307. {
  308. // check received stimulations on result input
  309. CStimulationSet* ResultStimulationSet = m_ResultInputDecoder.getOutputStimulationSet();
  310. for(uint64_t j = 0; j < ResultStimulationSet->size(); ++j)
  311. {
  312. ResultID = ResultStimulationSet->getId(j);
  313. if(ResultID >= m_RowBase && ResultID < m_RowBase + m_nTactilos)
  314. {
  315. Row = ResultID - m_RowBase;
  316. ResultReceived = true;
  317. }
  318. if(ResultReceived)
  319. {
  320. toggleResultColor(Row);
  321. std::string ResultLabel = gtk_label_get_text(m_ResultLabel);
  322. ResultLabel += gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(m_EventBox[Row]))));
  323. gtk_label_set_text(m_ResultLabel, ResultLabel.c_str());
  324. //switch current menu ptr to submenu
  325. if(m_CurrMenu->get_SubMenu(Row) != nullptr)
  326. {
  327. m_CurrMenu = m_CurrMenu->get_SubMenu(Row);
  328. }
  329. }
  330. }
  331. }
  332. // if end received
  333. if(m_SequenceInputDecoder.isEndReceived())
  334. {}
  335. // forward target stimulations
  336. boxContext.getInputChunk(2, i, ChunkStartTime, ChunkEndTime, Size, Buffer);
  337. boxContext.appendOutputChunkData(0, Buffer, Size);
  338. boxContext.markOutputAsReadyToSend(0, ChunkStartTime, ChunkEndTime);
  339. boxContext.markInputAsDeprecated(2, i);
  340. }
  341. }
  342. // ------ Send accumulated stimuli to the acquisition server
  343. if(m_idleFuncTag == 0)
  344. {
  345. m_idleFuncTag = g_idle_add(FlushCB, this);
  346. }
  347. return(true);
  348. }
  349. // ------ Function Definitions
  350. // Sends all accumulated stimuli to the TCP Tagging
  351. void CBoxAlgorithmP300TactileVisualization::flushQueue()
  352. {
  353. for (const auto& stimulation : m_stimuliQueue)
  354. {
  355. m_stimulusSender->sendStimulation(stimulation);
  356. }
  357. m_stimuliQueue.clear();
  358. // This function will be automatically removed after completion, so set to 0
  359. m_idleFuncTag = 0;
  360. }
  361. // Change labels fore-/background
  362. void CBoxAlgorithmP300TactileVisualization::toggleFlashColor(uint64_t id)
  363. {
  364. gtk_widget_modify_fg(gtk_bin_get_child(GTK_BIN(m_EventBox[id])), GTK_STATE_NORMAL, &m_FlashFG);
  365. gtk_widget_modify_bg(m_EventBox[id], GTK_STATE_NORMAL, &m_FlashBG);
  366. }
  367. void CBoxAlgorithmP300TactileVisualization::toggleTargetColor(uint64_t id)
  368. {
  369. gtk_widget_modify_fg(gtk_bin_get_child(GTK_BIN(m_EventBox[id])), GTK_STATE_NORMAL, &m_TargetFG);
  370. gtk_widget_modify_bg(m_EventBox[id], GTK_STATE_NORMAL, &m_TargetBG);
  371. }
  372. void CBoxAlgorithmP300TactileVisualization::toggleResultColor(uint64_t id)
  373. {
  374. gtk_widget_modify_fg(gtk_bin_get_child(GTK_BIN(m_EventBox[id])), GTK_STATE_NORMAL, &m_ResultFG);
  375. gtk_widget_modify_bg(m_EventBox[id], GTK_STATE_NORMAL, &m_ResultBG);
  376. }
  377. void CBoxAlgorithmP300TactileVisualization::resetColor()
  378. {
  379. for(uint64_t i = 0; i < m_nTactilos; i++)
  380. {
  381. gtk_widget_modify_fg(gtk_bin_get_child(GTK_BIN(m_EventBox[i])), GTK_STATE_NORMAL, &m_NoFlashFG);
  382. gtk_widget_modify_bg(m_EventBox[i], GTK_STATE_NORMAL, &m_NoFlashBG);
  383. }
  384. }
  385. // Change label text
  386. void CBoxAlgorithmP300TactileVisualization::toggleLabelText()
  387. {
  388. for(uint64_t i = 0; i < m_nTactilos; i++)
  389. {
  390. std::string label_text = m_CurrMenu->get_LabelText(i);
  391. gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(m_EventBox[i]))), label_text.c_str());
  392. }
  393. }