#include "ovpCBoxAlgorithmP300TactileVisualization.h" using namespace OpenViBE; using namespace /*OpenViBE::*/Kernel; using namespace /*OpenViBE::*/Plugins; using namespace /*OpenViBE::Plugins::*/Tactilebci; // This callback flushes all accumulated stimulations to the TCP Tagging // after the rendering has completed. static gboolean FlushCB(gpointer data) { (static_cast(data))->flushQueue(); return false; // Only run once } bool CBoxAlgorithmP300TactileVisualization::initialize() { // ------ Init decoder/encoder m_SequenceInputDecoder.initialize(*this, 0); m_TargetInputDecoder.initialize(*this, 1); m_ResultInputDecoder.initialize(*this, 2); m_ResultOutputEncoder.initialize(*this, 0); // ------ Get box settings m_InterfaceFilename = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 0); m_RowBase = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 1); m_nTactilos = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 2); m_FreeSpelling = FSettingValueAutoCast(*this->getBoxAlgorithmContext(), 3); //set m_nTactilos minimum number of 2, if lower if(m_nTactilos < 2) { m_nTactilos = 2; } //set m_nTactilos maximum number of MAX_TACTILOS(10), if higher if(m_nTactilos > MAX_TACTILOS) { m_nTactilos = MAX_TACTILOS; } // ------ Setup acquisition server TCP tagging m_stimuliQueue.clear(); m_stimulusSender = TCPTagging::CreateStimulusSender(); if(!m_stimulusSender->connect("localhost", "15361")) { this->getLogManager() << LogLevel_Warning << "Failed to connect to acquisition server TCP tagging.\n"; } // ------ Gtk variables // Build main UI elements m_MainWidgetInterface = gtk_builder_new(); if(!gtk_builder_add_from_file(m_MainWidgetInterface, m_InterfaceFilename.toASCIIString(), nullptr))//error { this->getLogManager() << LogLevel_ImportantWarning << "Failed to load UI file [" << m_InterfaceFilename << "]\n"; this->getLogManager() << LogLevel_ImportantWarning << "Make sure, that the given filename and path are correct\n"; return(false); } m_MainWindow = GTK_WIDGET(gtk_builder_get_object(m_MainWidgetInterface, "p300-tactile-main")); m_Table = GTK_TABLE(gtk_builder_get_object(m_MainWidgetInterface, "p300-tactile-table")); m_ResultLabel = GTK_LABEL(gtk_builder_get_object(m_MainWidgetInterface, "label-result")); m_TargetLabel = GTK_LABEL(gtk_builder_get_object(m_MainWidgetInterface, "label-target")); // Create font description m_FontDesc = pango_font_description_copy(pango_context_get_font_description(gtk_widget_get_pango_context(m_MainWindow))); pango_font_description_set_size(m_FontDesc, gint(FontSize*PANGO_SCALE)); // test if the UI-file fits the number of tactilos guint nRow = 0; g_object_get(m_Table, "n-rows", &nRow, nullptr); if(m_nTactilos != nRow) { 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"; this->getLogManager() << LogLevel_ImportantWarning << "Make sure to choose the right UI-File, e.g. p300-tactile-6.ui for 6 tactilos\n"; return(false); } else { for(uint64_t i = 0; i < m_nTactilos; i++) { std::string eventbox_id = "eventbox-" + std::to_string(i+1); m_EventBox.push_back(GTK_WIDGET(gtk_builder_get_object(m_MainWidgetInterface, eventbox_id.c_str()))); } } // ------ Setup tactile UI //Create UI for m_nTactilos = 6 if(m_nTactilos == 6) { //create free spelling UI for 6 tactilos if(m_FreeSpelling) { //mainmenu m_Menu.push_back(TactileMenu(6)); m_Menu[0].set_LabelText(0, "Ja"); m_Menu[0].set_LabelText(1, "Nein"); m_Menu[0].set_LabelText(2, "Geräte"); m_Menu[0].set_LabelText(3, "Körper"); m_Menu[0].set_LabelText(4, "Hilfe"); m_Menu[0].set_LabelText(5, "Schmerzen"); //submenu Geräte m_Menu.push_back(TactileMenu(6)); m_Menu[1].set_LabelText(0, "Ja"); m_Menu[1].set_LabelText(1, "Nein"); m_Menu[1].set_LabelText(2, "Atemgerät"); m_Menu[1].set_LabelText(3, "Computer"); m_Menu[1].set_LabelText(4, "Rollstuhl"); m_Menu[1].set_LabelText(5, "Hauptmenü"); //submenu Körper m_Menu.push_back(TactileMenu(6)); m_Menu[2].set_LabelText(0, "Ja"); m_Menu[2].set_LabelText(1, "Nein"); m_Menu[2].set_LabelText(2, "Hunger/Durst"); m_Menu[2].set_LabelText(3, "warm/kalt"); m_Menu[2].set_LabelText(4, "Müdigkeit"); m_Menu[2].set_LabelText(5, "Hauptmenü"); //submenu Hilfe m_Menu.push_back(TactileMenu(6)); m_Menu[3].set_LabelText(0, "Ja"); m_Menu[3].set_LabelText(1, "Nein"); m_Menu[3].set_LabelText(2, "Lage"); m_Menu[3].set_LabelText(3, "Atemnot"); m_Menu[3].set_LabelText(4, "Anderes Problem"); m_Menu[3].set_LabelText(5, "Hauptmenü"); //submenu Schmerzen m_Menu.push_back(TactileMenu(6)); m_Menu[3].set_LabelText(0, "Ja"); m_Menu[3].set_LabelText(1, "Nein"); m_Menu[3].set_LabelText(2, "Kopf"); m_Menu[3].set_LabelText(3, "Torso"); m_Menu[3].set_LabelText(4, "Glieder"); m_Menu[3].set_LabelText(5, "Hauptmenü"); //set SubMenu ptr m_Menu[0].set_SubMenu(2, &m_Menu[1]);//main --> Geräte m_Menu[0].set_SubMenu(3, &m_Menu[2]);//main --> Körper m_Menu[0].set_SubMenu(4, &m_Menu[3]);//main --> Hilfe m_Menu[0].set_SubMenu(4, &m_Menu[4]);//main --> Schmerzen m_Menu[1].set_SubMenu(5, &m_Menu[0]);//Geräte --> main m_Menu[2].set_SubMenu(5, &m_Menu[0]);//Körper --> main m_Menu[3].set_SubMenu(5, &m_Menu[0]);//Hilfe --> main m_Menu[3].set_SubMenu(5, &m_Menu[0]);//Schmerzen --> main //set curr Menu ptr to main menu m_CurrMenu = &m_Menu[0]; } //create copy spelling UI for 6 tactilos else { m_Menu.push_back(TactileMenu(6)); m_Menu[0].set_LabelText(0, "Bein links"); m_Menu[0].set_LabelText(1, "Bein rechts"); m_Menu[0].set_LabelText(2, "Arm links"); m_Menu[0].set_LabelText(3, "Arm rechts"); m_Menu[0].set_LabelText(4, "Brust"); m_Menu[0].set_LabelText(5, "Rücken"); m_CurrMenu = &m_Menu[0]; } } //Create default UI for m_nTactilos != 6 else { m_Menu.push_back(TactileMenu(m_nTactilos)); m_CurrMenu = &m_Menu[0]; } // ------ Init UI m_visualizationCtx = dynamic_cast(this->createPluginObject(OVP_ClassId_Plugin_VisualizationCtx)); m_visualizationCtx->setWidget(*this, m_MainWindow); for(uint64_t i = 0; i < m_nTactilos; ++i) { resetColor(); gtk_widget_modify_font(gtk_bin_get_child(GTK_BIN(m_EventBox[i])), m_FontDesc); } toggleLabelText(); return(true); } /*******************************************************************************/ bool CBoxAlgorithmP300TactileVisualization::uninitialize() { // ------ uninitialize decoder/encoder m_SequenceInputDecoder.uninitialize(); m_TargetInputDecoder.uninitialize(); m_ResultInputDecoder.uninitialize(); m_ResultOutputEncoder.uninitialize(); // ------ uninitialize GTK UI if (m_MainWidgetInterface) { g_object_unref(m_MainWidgetInterface); m_MainWidgetInterface = nullptr; } if (m_visualizationCtx) { this->releasePluginObject(m_visualizationCtx); } // ------ uinitialize TCP Tagging if (m_idleFuncTag != 0) { m_stimuliQueue.clear(); g_source_remove(m_idleFuncTag); m_idleFuncTag = 0; } if (m_stimulusSender) { delete m_stimulusSender; m_stimulusSender = nullptr; } return(true); } /*******************************************************************************/ bool CBoxAlgorithmP300TactileVisualization::processInput(const size_t index) { // ------ ready to process getBoxAlgorithmContext()->markAlgorithmAsReadyToProcess(); return(true); } /*******************************************************************************/ bool CBoxAlgorithmP300TactileVisualization::process() { const IBox& staticBoxContext=this->getStaticBoxContext(); IBoxIO& boxContext = this->getDynamicBoxContext(); //variables for processing of input chunks uint64_t SequenceID = 0; uint64_t TargetID = 0; uint64_t ResultID = 0; int Row = -1; bool IsTarget = false; bool TargetReceived = false; bool ResultReceived = false; //variables for forwarding of target stimulations uint64_t ChunkStartTime = 0; uint64_t ChunkEndTime = 0; uint64_t Size = 0; const uint8_t* Buffer = nullptr; // ------ Sequence stimulation for(uint64_t i = 0; i < boxContext.getInputChunkCount(0); ++i) { m_SequenceInputDecoder.decode(i); m_LastTime = boxContext.getInputChunkEndTime(0, i); // if header received if(m_SequenceInputDecoder.isHeaderReceived()) {} // if buffer received if(m_SequenceInputDecoder.isBufferReceived()) { //check received stimulations on sequence input CStimulationSet* SequenceStimulationSet = m_SequenceInputDecoder.getOutputStimulationSet(); for(uint64_t j = 0; j < SequenceStimulationSet->size(); ++j) { SequenceID = SequenceStimulationSet->getId(j); if(SequenceID >= m_RowBase && SequenceID < m_RowBase + m_nTactilos) { Row = SequenceID - m_RowBase; IsTarget = (Row == (int)m_LastTarget); } if(SequenceID == OVTK_StimulationId_VisualStimulationStart) { resetColor(); toggleFlashColor(Row); if (IsTarget) { m_stimuliQueue.push_back(OVTK_StimulationId_Target); } else { m_stimuliQueue.push_back(OVTK_StimulationId_NonTarget); } } if(SequenceID == OVTK_StimulationId_VisualStimulationStop) { resetColor(); } if(SequenceID == OVTK_StimulationId_SegmentStart) { toggleLabelText(); } m_stimuliQueue.push_back(SequenceID); } } // if end received if(m_SequenceInputDecoder.isEndReceived()) {} boxContext.markInputAsDeprecated(0, i); } // ------ Target stimulation for(uint64_t i = 0; i < boxContext.getInputChunkCount(1); ++i) { if(m_LastTime >= boxContext.getInputChunkStartTime(1, i)) { m_TargetInputDecoder.decode(i); // if header received if(m_SequenceInputDecoder.isHeaderReceived()) {} // if buffer received if(m_SequenceInputDecoder.isBufferReceived()) { // check received dtimulations on Target input CStimulationSet* TargetStimulationSet = m_TargetInputDecoder.getOutputStimulationSet(); for(uint64_t j = 0; j < TargetStimulationSet->size(); ++j) { TargetID = TargetStimulationSet->getId(j); if(TargetID >= m_RowBase && TargetID < m_RowBase + m_nTactilos) { Row = TargetID - m_RowBase; TargetReceived = true; } if(TargetReceived) { toggleTargetColor(Row); m_stimuliQueue.push_back(TargetID); std::string TargetLabel = gtk_label_get_text(m_TargetLabel); TargetLabel += gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(m_EventBox[Row])))); gtk_label_set_text(m_TargetLabel, TargetLabel.c_str()); m_LastTarget = Row; } } } // if end received if(m_SequenceInputDecoder.isEndReceived()) {} boxContext.markInputAsDeprecated(1, i); } } // ------ Result stimulation for(uint64_t i = 0; i < boxContext.getInputChunkCount(2); ++i) { if(m_LastTime >= boxContext.getInputChunkStartTime(2, i)) { m_ResultInputDecoder.decode(i); // if header received if(m_SequenceInputDecoder.isHeaderReceived()) {} // if buffer received if(m_SequenceInputDecoder.isBufferReceived()) { // check received stimulations on result input CStimulationSet* ResultStimulationSet = m_ResultInputDecoder.getOutputStimulationSet(); for(uint64_t j = 0; j < ResultStimulationSet->size(); ++j) { ResultID = ResultStimulationSet->getId(j); if(ResultID >= m_RowBase && ResultID < m_RowBase + m_nTactilos) { Row = ResultID - m_RowBase; ResultReceived = true; } if(ResultReceived) { toggleResultColor(Row); std::string ResultLabel = gtk_label_get_text(m_ResultLabel); ResultLabel += gtk_label_get_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(m_EventBox[Row])))); gtk_label_set_text(m_ResultLabel, ResultLabel.c_str()); //switch current menu ptr to submenu if(m_CurrMenu->get_SubMenu(Row) != nullptr) { m_CurrMenu = m_CurrMenu->get_SubMenu(Row); } } } } // if end received if(m_SequenceInputDecoder.isEndReceived()) {} // forward target stimulations boxContext.getInputChunk(2, i, ChunkStartTime, ChunkEndTime, Size, Buffer); boxContext.appendOutputChunkData(0, Buffer, Size); boxContext.markOutputAsReadyToSend(0, ChunkStartTime, ChunkEndTime); boxContext.markInputAsDeprecated(2, i); } } // ------ Send accumulated stimuli to the acquisition server if(m_idleFuncTag == 0) { m_idleFuncTag = g_idle_add(FlushCB, this); } return(true); } // ------ Function Definitions // Sends all accumulated stimuli to the TCP Tagging void CBoxAlgorithmP300TactileVisualization::flushQueue() { for (const auto& stimulation : m_stimuliQueue) { m_stimulusSender->sendStimulation(stimulation); } m_stimuliQueue.clear(); // This function will be automatically removed after completion, so set to 0 m_idleFuncTag = 0; } // Change labels fore-/background void CBoxAlgorithmP300TactileVisualization::toggleFlashColor(uint64_t id) { gtk_widget_modify_fg(gtk_bin_get_child(GTK_BIN(m_EventBox[id])), GTK_STATE_NORMAL, &m_FlashFG); gtk_widget_modify_bg(m_EventBox[id], GTK_STATE_NORMAL, &m_FlashBG); } void CBoxAlgorithmP300TactileVisualization::toggleTargetColor(uint64_t id) { gtk_widget_modify_fg(gtk_bin_get_child(GTK_BIN(m_EventBox[id])), GTK_STATE_NORMAL, &m_TargetFG); gtk_widget_modify_bg(m_EventBox[id], GTK_STATE_NORMAL, &m_TargetBG); } void CBoxAlgorithmP300TactileVisualization::toggleResultColor(uint64_t id) { gtk_widget_modify_fg(gtk_bin_get_child(GTK_BIN(m_EventBox[id])), GTK_STATE_NORMAL, &m_ResultFG); gtk_widget_modify_bg(m_EventBox[id], GTK_STATE_NORMAL, &m_ResultBG); } void CBoxAlgorithmP300TactileVisualization::resetColor() { for(uint64_t i = 0; i < m_nTactilos; i++) { gtk_widget_modify_fg(gtk_bin_get_child(GTK_BIN(m_EventBox[i])), GTK_STATE_NORMAL, &m_NoFlashFG); gtk_widget_modify_bg(m_EventBox[i], GTK_STATE_NORMAL, &m_NoFlashBG); } } // Change label text void CBoxAlgorithmP300TactileVisualization::toggleLabelText() { for(uint64_t i = 0; i < m_nTactilos; i++) { std::string label_text = m_CurrMenu->get_LabelText(i); gtk_label_set_text(GTK_LABEL(gtk_bin_get_child(GTK_BIN(m_EventBox[i]))), label_text.c_str()); } }