diff --git a/Modules/IGT/IGTFilters/mitkNavigationDataPlayer.cpp b/Modules/IGT/IGTFilters/mitkNavigationDataPlayer.cpp index efc2b808e5..d154a499f2 100644 --- a/Modules/IGT/IGTFilters/mitkNavigationDataPlayer.cpp +++ b/Modules/IGT/IGTFilters/mitkNavigationDataPlayer.cpp @@ -1,534 +1,539 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: 2009-02-10 18:08:54 +0100 (Di, 10 Feb 2009) $ Version: $Revision: 16228 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkNavigationDataPlayer.h" //for the pause #include #include #include mitk::NavigationDataPlayer::NavigationDataPlayer() { m_NumberOfOutputs = 0; m_Pause = false; m_Playing = false; m_Stream = NULL; m_PlayerMode = NormalFile; m_FileName = ""; m_FileVersion = 1; m_Playing = false; m_Pause = false; m_NumberOfOutputs = 0; m_StartPlayingTimeStamp = 0.0; m_PauseTimeStamp = 0.0; m_parentElement = NULL; m_currentNode = NULL; m_StreamEnd = false; m_StreamValid = true; m_ErrorMessage = ""; + m_StreamSetOutsideFromClass = false; //To get a start time mitk::TimeStamp::GetInstance()->Start(this); } mitk::NavigationDataPlayer::~NavigationDataPlayer() { StopPlaying(); delete m_parentElement; } void mitk::NavigationDataPlayer::GenerateData() { //Only produce new output if the player is started if (!m_Playing) { //The output is not valid anymore for (unsigned int index = 0; index < m_NumberOfOutputs; index++) { mitk::NavigationData* output = this->GetOutput(index); assert(output); mitk::NavigationData::Pointer nd = mitk::NavigationData::New(); mitk::NavigationData::PositionType position; mitk::NavigationData::OrientationType orientation(0.0,0.0,0.0,0.0); position.Fill(0.0); nd->SetPosition(position); nd->SetOrientation(orientation); nd->SetDataValid(false); output->Graft(nd); } return; } //first of all get current time TimeStampType now = mitk::TimeStamp::GetInstance()->GetElapsed(); //now we make a little time arithmetic //to get the elapsed time since the start of the player TimeStampType timeSinceStart = now - m_StartPlayingTimeStamp; //init the vectors std::vector< NavigationData::Pointer > nextCandidates; std::vector< NavigationData::Pointer > lastCandidates; std::vector< NavigationData::TimeStampType > currentTimeOfData; for (unsigned int index=0; index < m_NumberOfOutputs; index++) { nextCandidates.push_back(m_NextToPlayNavigationData.at(index)); lastCandidates.push_back(m_NextToPlayNavigationData.at(index)); currentTimeOfData.push_back(timeSinceStart + m_StartTimeOfData.at(index)); } if (m_NextToPlayNavigationData.size() != m_NumberOfOutputs) { std::cout << "Mismatch in data" << std::endl; return; } // Now we try to find next NavigationData in the stream: // This means we step through the stream of NavigationDatas until we find // a NavigationData which has a current timestamp (currentTimeOfData) greater // then the current playing time. Then we store the data in // m_NextToPlayNavigationData and take the last data (lastCandidates) for the // output of this filter. // // The loop will stop when a suitable NavigationData is found or we reach EOF. // The timestamps of each recorded NavigationData should be equal // therefore we take always the time from the first. while( nextCandidates[0]->GetTimeStamp() < currentTimeOfData[0]) { for (unsigned int index=0; index < m_NumberOfOutputs; index++) { lastCandidates[index] = nextCandidates.at(index); switch(m_FileVersion) { case 1: nextCandidates[index] = ReadVersion1(); break; default: //this case should not happen! therefore the return at this point return; break; } //check if the input stream delivered a correct NavigationData object for (unsigned int i = 0; i < m_NumberOfOutputs; i++) { if (nextCandidates.at(index).IsNull()) { m_StreamEnd = true; StopPlaying(); return; //the case if no NavigationData is found, e.g. EOF, bad stream } } } } //Now lastCandidates stores the new output and nextCandidates is stored to the m_NextToPlay vector for (unsigned int index = 0; index < m_NumberOfOutputs; index++) { mitk::NavigationData* output = this->GetOutput(index); assert(output); output->Graft(lastCandidates.at(index)); m_NextToPlayNavigationData[index] = nextCandidates.at(index); } } void mitk::NavigationDataPlayer::UpdateOutputInformation() { this->Modified(); // make sure that we need to be updated Superclass::UpdateOutputInformation(); } void mitk::NavigationDataPlayer::InitPlayer() { if (m_Stream == NULL) { StreamInvalid("Playing not possible. Wrong file name or path?"); return; } if (!m_Stream->good()) { StreamInvalid("Playing not possible. Stream is not good!"); return; } m_FileVersion = GetFileVersion(m_Stream); //first get the file version //check if we have a valid version if (m_FileVersion < 1) { StreamInvalid("Playing not possible. Stream is not good!"); return; } //now read the number of Tracked Tools if(m_NumberOfOutputs == 0){m_NumberOfOutputs = GetNumberOfNavigationDatas(m_Stream);} //with the information about the tracked tool number we can generate the output if (m_NumberOfOutputs > 0) { //Generate the output only if there are changes to the amount of outputs //This happens when the player is stopped and start again with different file if (this->GetNumberOfOutputs() != m_NumberOfOutputs) {SetNumberOfOutputs(m_NumberOfOutputs);} //initialize the player with first data GetFirstData(); //set stream valid m_ErrorMessage = ""; m_StreamValid = true; } else { StreamInvalid("The input stream seems to have NavigationData incompatible format"); return; } } unsigned int mitk::NavigationDataPlayer::GetFileVersion(std::istream* stream) { if (stream==NULL) { std::cout << "No input stream set!" << std::endl; return 0; } if (!stream->good()) { std::cout << "Stream not good!" << std::endl; return 0; } int version = 1; TiXmlDeclaration* dec = new TiXmlDeclaration(); *stream >> *dec; if(strcmp(dec->Version(),"") == 0){ std::cout << "The input stream seems to have XML incompatible format" << std::endl; return 0; } m_parentElement = new TiXmlElement(""); *stream >> *m_parentElement; //2nd line this is the file version std::string tempValue = m_parentElement->Value(); if(tempValue != "Version") { if(tempValue == "Data"){ m_parentElement->QueryIntAttribute("version",&version); } } else { m_parentElement->QueryIntAttribute("Ver",&version); } if (version > 0) return version; else return 0; } unsigned int mitk::NavigationDataPlayer::GetNumberOfNavigationDatas(std::istream* stream) { if (stream == NULL) { std::cout << "No input stream set!" << std::endl; return 0; } if (!stream->good()) { std::cout << "Stream not good!" << std::endl; return 0; } //If something has changed in a future version of the XML definition e.g. navigationcount or addional parameters //catch this here with a select case block (see GenerateData() method) int numberOfTools = 0; std::string tempValue = m_parentElement->Value(); if(tempValue == "Version"){ *stream >> *m_parentElement; } m_parentElement->QueryIntAttribute("ToolCount",&numberOfTools); if (numberOfTools > 0) return numberOfTools; return 0; } mitk::NavigationData::Pointer mitk::NavigationDataPlayer::ReadVersion1() { if (m_Stream == NULL) { m_Playing = false; std::cout << "Playing not possible. Wrong file name or path? " << std::endl; return NULL; } if (!m_Stream->good()) { m_Playing = false; std::cout << "Playing not possible. Stream is not good!" << std::endl; return NULL; } /*TiXmlElement* elem = new TiXmlElement(""); m_currentNode = m_parentElement->IterateChildren(m_currentNode); if(m_currentNode) { elem = m_currentNode->ToElement(); }*/ TiXmlElement* elem; m_currentNode = m_parentElement->IterateChildren(m_currentNode); bool delElem; if(m_currentNode) { elem = m_currentNode->ToElement(); delElem = false; } else { elem = new TiXmlElement(""); delElem = true; } mitk::NavigationData::Pointer nd = this->ReadNavigationData(elem); if(delElem) delete elem; return nd; } void mitk::NavigationDataPlayer::StartPlaying() { if (m_Stream == NULL) { m_Playing = false; //Perhaps the SetStream method was not called so we do this when a FileName is set with SetStream(PlayerMode) if (m_FileName != "") { //The PlayerMode is initialized with LastSetStream //SetStream also calls InitPlayer() SetStream(m_PlayerMode); } //now check again if (m_Stream == NULL) { StopPlaying(); std::cout << "Playing not possible. Wrong file name or path? " << std::endl; return; } } if (!m_Playing && m_Stream->good()) { m_Playing = true; //starts the player m_StartPlayingTimeStamp = mitk::TimeStamp::GetInstance()->GetElapsed(); } else { std::cout << "Player already started or stream is not good" << std::endl; StopPlaying(); } } void mitk::NavigationDataPlayer::StopPlaying() { //re init all data!! for playing again with different data //only PlayerMode and FileName are not changed m_Pause = false; m_Playing = false; + if (!m_StreamSetOutsideFromClass) + {delete m_Stream;} m_Stream = NULL; m_FileVersion = 1; m_Playing = false; m_Pause = false; m_StartPlayingTimeStamp = 0.0; m_PauseTimeStamp = 0.0; m_NextToPlayNavigationData.clear(); m_StartTimeOfData.clear(); } void mitk::NavigationDataPlayer::GetFirstData() { //Here we read the first lines of input (dependend on the number of inputs) for (unsigned int index=0; index < m_NumberOfOutputs; index++) { //Here we init the vector for later use m_NextToPlayNavigationData.push_back(NULL); m_StartTimeOfData.push_back(0.0); mitk::NavigationData::Pointer nd = this->GetOutput(index); switch(m_FileVersion) { case 1: m_NextToPlayNavigationData[index] = ReadVersion1(); //check if there is valid data in it if (m_NextToPlayNavigationData[index].IsNull()) { m_StreamEnd = true; StopPlaying(); std::cout << "XML File is corrupt or has no NavigationData" << std::endl; return; } //Have a look it the output was set already without this check the pipline will disconnect after a start/stop cycle if (nd.IsNull()) this->SetNthOutput(index, m_NextToPlayNavigationData[index]); m_StartTimeOfData[index] = m_NextToPlayNavigationData[index]->GetTimeStamp(); break; default: //this case should not happen! therefore the return at this point return; break; } } } void mitk::NavigationDataPlayer::Pause() { //player runs and pause was called -> pause the player if(m_Playing && !m_Pause) { m_Playing = false; m_Pause = true; m_PauseTimeStamp = mitk::TimeStamp::GetInstance()->GetElapsed(); } else { std::cout << "Player is either not started or already is paused" << std::endl; } } void mitk::NavigationDataPlayer::Resume() { //player is in pause mode -> play at the last position if(!m_Playing && m_Pause) { m_Playing = true; m_Pause = false; mitk::NavigationData::TimeStampType now = mitk::TimeStamp::GetInstance()->GetElapsed(); // in this case m_StartPlayingTimeStamp is set to the total elapsed time with NO playback m_StartPlayingTimeStamp = now - (m_PauseTimeStamp - m_StartPlayingTimeStamp); } else { std::cout << "Player is not paused!" << std::endl; } } void mitk::NavigationDataPlayer::SetStream( PlayerMode /*mode*/ ) { m_Stream = NULL; if (!itksys::SystemTools::FileExists(m_FileName.c_str())) { std::cout << "File dont exist!" << std::endl; return; } switch(m_PlayerMode) { case NormalFile: m_Stream = new std::ifstream(m_FileName.c_str()); + m_StreamSetOutsideFromClass = false; break; case ZipFile: m_Stream = NULL; std::cout << "Sorry no ZipFile support yet"; break; default: m_Stream = NULL; break; } this->Modified(); InitPlayer(); } void mitk::NavigationDataPlayer::SetStream( std::istream* stream ) { if (!stream->good()) { m_StreamEnd = true; std::cout << "The stream is not good" << std::endl; return; } m_Stream = stream; + m_StreamSetOutsideFromClass = true; this->Modified(); InitPlayer(); } const bool mitk::NavigationDataPlayer::IsAtEnd() { return this->m_StreamEnd; } void mitk::NavigationDataPlayer::StreamInvalid(std::string message) { m_StreamEnd = true; StopPlaying(); m_ErrorMessage = message; m_StreamValid = false; MITK_ERROR << m_ErrorMessage; return; } \ No newline at end of file diff --git a/Modules/IGT/IGTFilters/mitkNavigationDataPlayer.h b/Modules/IGT/IGTFilters/mitkNavigationDataPlayer.h index ff108d7810..b8b4f846f9 100644 --- a/Modules/IGT/IGTFilters/mitkNavigationDataPlayer.h +++ b/Modules/IGT/IGTFilters/mitkNavigationDataPlayer.h @@ -1,213 +1,215 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: 2009-02-10 18:08:54 +0100 (Di, 10 Feb 2009) $ Version: $Revision: 16228 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef MITKNavigationDataPlayer_H_HEADER_INCLUDED_ #define MITKNavigationDataPlayer_H_HEADER_INCLUDED_ #include #include #include //for the Recording Mode enum #include "mitkTrackingDevice.h" #include #include "tinyxml.h" #include namespace mitk { /**Documentation * \brief This class is used to play recorded (see mitkNavigationDataRecorder class) files. * * If you want to play a file you have to set an input stream. This can be an own one (use StartPlaying(std::istream*)) * or a preset (use StartPlaying()). The presets are NormalFile and ZipFile and can be set with the method * SetPlayerMode(PlayerMode). The presets need a FileName. Therefore the FileName must be set before the preset. * For pausing the player call Pause(). A call of Resume() will continue the playing. * * * \ingroup IGT */ class MitkIGT_EXPORT NavigationDataPlayer : public NavigationDataPlayerBase { public: mitkClassMacro(NavigationDataPlayer, NavigationDataPlayerBase); itkNewMacro(Self); /** * \brief sets the file name and path for the PlayerMode NormalFile and ZipFile */ itkSetStringMacro(FileName); /** * \brief returns the file name and path for the PlayerMode NormalFile and ZipFile */ itkGetStringMacro(FileName); /** @return Returns an error message if there was one (e.g. if the stream is invalid). * Returns an empty string if there was no error in the current stream. */ itkGetStringMacro(ErrorMessage); /** @return Retruns if the current stream is valid or not. */ itkGetMacro(StreamValid,bool); /** * \brief Used for pipeline update just to tell the pipeline that we always have to update */ virtual void UpdateOutputInformation(); /** * \brief This method starts the player. * * Before the stream has to be set. Either with a PlayingMode (SetStream(PlayerMode)) and FileName. Or * with an own inputstream (SetStream(istream*)). */ void StartPlaying(); /** * \brief Stops the player and closes the stream. After a call of StopPlaying() * StartPlaying() must be called to get new output data * * \warning the output is generated in this method because we know first about the number of output after * reading the first lines of the XML file. Therefore you should assign your output after the call of this method */ void StopPlaying(); /** * \brief This method pauses the player. If you want to play again call Resume() * *\warning This method is not tested yet. It is not save to use! */ void Pause(); /** * \brief This method resumes the player when it was paused. * *\warning This method is not tested yet. It is not save to use! */ void Resume(); /** * \brief This method checks if player arrived at end of file. * *\warning This method is not tested yet. It is not save to use! */ const bool IsAtEnd(); /** * \brief The PlayerMode is used for generating a presetted output stream. You do not need to * set it if you want to use your own stream. * * There are: * NormalFile: ifstream * ZipFile: not implemented yet * *\warning The ZipFile Mode is not implemented yet */ enum PlayerMode { NormalFile, ZipFile }; /** * \brief sets the recording mode which causes different types of output streams * This method is overloaded with SetStream( ostream* ) */ void SetStream(PlayerMode mode); /** * \brief sets the recording mode which causes different types of output streams * This method is overloaded with SetStream( PlayerMode ) */ void SetStream(std::istream* stream); protected: NavigationDataPlayer(); virtual ~NavigationDataPlayer(); typedef mitk::NavigationData::TimeStampType TimeStampType; /** * \brief filter execute method */ virtual void GenerateData(); /** * \brief Returns the file version out of the XML document. */ unsigned int GetFileVersion(std::istream* stream); /** * \brief Returns the number of tracked tools out of the XML document. */ unsigned int GetNumberOfNavigationDatas(std::istream* stream); /** * \brief Gets the first data for initializing the player */ void GetFirstData(); /** * \brief This method reads one line of the XML document and returns the data as a NavigationData object * If there is a new file version another method must be added which reads this data. */ mitk::NavigationData::Pointer ReadVersion1(); /** * \brief This method initializes the player with first data */ void InitPlayer(); std::istream* m_Stream; ///< stores a pointer to the input stream + bool m_StreamSetOutsideFromClass; ///< stores if the stream was created in this class and must be deleted in the end + PlayerMode m_PlayerMode; ///< stores the mode for the presetted PlayerMode sieh enum PlayerMode std::string m_FileName; ///< stores the filename unsigned int m_FileVersion; ///< indicates which XML encoding is used bool m_Playing; ///< indicates whether the generateoutput method generates new output or not bool m_Pause; ///< indicates if the player is paused unsigned int m_NumberOfOutputs; ///< stores the number of outputs known from the XML document TimeStampType m_StartPlayingTimeStamp; ///< the starttime of the playing set in the method StartPlaying() TimeStampType m_PauseTimeStamp; ///< stores the beginning of a pause std::vector m_NextToPlayNavigationData; ///< stores the next possible candidate for playing std::vector m_StartTimeOfData; ///< stores the start time of the different tools TiXmlElement * m_parentElement; TiXmlNode * m_currentNode; bool m_StreamEnd; ///< stores if the input stream arrived at end bool m_StreamValid; ///< stores if the input stream is valid or not std::string m_ErrorMessage; ///< stores the error message if the stream is invalid void StreamInvalid(std::string message); ///< help method which sets the stream invalid and displays an error }; } // namespace mitk #endif /* MITKNavigationDataPlayer_H_HEADER_INCLUDED_ */ diff --git a/Modules/IGT/IGTFilters/mitkNavigationDataRecorder.cpp b/Modules/IGT/IGTFilters/mitkNavigationDataRecorder.cpp index 9b18685651..8f25285a10 100644 --- a/Modules/IGT/IGTFilters/mitkNavigationDataRecorder.cpp +++ b/Modules/IGT/IGTFilters/mitkNavigationDataRecorder.cpp @@ -1,357 +1,365 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: 2007-12-11 14:46:19 +0100 (Di, 11 Dez 2007) $ Version: $Revision: 13129 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "mitkNavigationDataRecorder.h" #include #include #include #include mitk::NavigationDataRecorder::NavigationDataRecorder() { //set default values m_NumberOfInputs = 0; m_RecordingMode = NormalFile; m_Recording = false; m_NumberOfRecordedFiles = 0; m_Stream = NULL; m_FileName = ""; m_SystemTimeClock = RealTimeClock::New(); m_OutputFormat = mitk::NavigationDataRecorder::xml; m_RecordCounter = 0; m_RecordCountLimit = -1; m_DoNotOverwriteFiles = false; + m_StreamMustBeDeleted = false; //To get a start time mitk::TimeStamp::GetInstance()->Start(this); } mitk::NavigationDataRecorder::~NavigationDataRecorder() { } void mitk::NavigationDataRecorder::GenerateData() { } void mitk::NavigationDataRecorder::AddNavigationData( const NavigationData* nd ) { // Process object is not const-correct so the const_cast is required here this->SetNthInput(m_NumberOfInputs, const_cast< mitk::NavigationData * >( nd ) ); m_NumberOfInputs++; this->Modified(); } void mitk::NavigationDataRecorder::SetRecordingMode( RecordingMode mode ) { m_RecordingMode = mode; this->Modified(); } void mitk::NavigationDataRecorder::Update() { if (m_Recording) { DataObjectPointerArray inputs = this->GetInputs(); //get all inputs mitk::NavigationData::TimeStampType timestamp=0.0; // timestamp for mitk time timestamp = mitk::TimeStamp::GetInstance()->GetElapsed(); mitk::NavigationData::TimeStampType sysTimestamp = 0.0; // timestamp for system time sysTimestamp = m_SystemTimeClock->GetCurrentStamp(); // cast system time double value to stringstream to avoid low precision rounding std::ostringstream strs; strs.precision(15); // rounding precision for system time double value strs << sysTimestamp; std::string sysTimeStr = strs.str(); //if csv-mode: write csv header and timpstamp at beginning if (m_OutputFormat == mitk::NavigationDataRecorder::csv) { //write header only when it's the first line if (m_firstLine) { m_firstLine = false; *m_Stream << "TimeStamp"; for (unsigned int index = 0; index < inputs.size(); index++){ *m_Stream << ";Valid_Tool" << index << ";X_Tool" << index << ";Y_Tool" << index << ";Z_Tool" << index << ";QX_Tool" << index << ";QY_Tool" << index << ";QZ_Tool" << index << ";QR_Tool" << index;} *m_Stream << "\n"; } //write timestamp (always) *m_Stream << timestamp; } //write tool data for every tool for (unsigned int index = 0; index < inputs.size(); index++) { mitk::NavigationData* nd = dynamic_cast(inputs[index].GetPointer()); nd->Update(); // call update to propagate update to previous filters mitk::NavigationData::PositionType position; mitk::NavigationData::OrientationType orientation(0.0, 0.0, 0.0, 0.0); mitk::NavigationData::CovarianceMatrixType matrix; bool hasPosition = true; bool hasOrientation = true; bool dataValid = false; position.Fill(0.0); matrix.SetIdentity(); position = nd->GetPosition(); orientation = nd->GetOrientation(); matrix = nd->GetCovErrorMatrix(); hasPosition = nd->GetHasPosition(); hasOrientation = nd->GetHasOrientation(); dataValid = nd->IsDataValid(); //use this one if you want the timestamps of the source //timestamp = nd->GetTimeStamp(); //a timestamp is never < 0! this case happens only if you are using the timestamp of the nd object instead of getting a new one if (timestamp >= 0) { if (this->m_OutputFormat == mitk::NavigationDataRecorder::xml) { TiXmlElement* elem = new TiXmlElement("NavigationData"); elem->SetDoubleAttribute("Time", timestamp); elem->SetAttribute("SystemTime", sysTimeStr); // tag for system time elem->SetDoubleAttribute("Tool", index); elem->SetDoubleAttribute("X", position[0]); elem->SetDoubleAttribute("Y", position[1]); elem->SetDoubleAttribute("Z", position[2]); elem->SetDoubleAttribute("QX", orientation[0]); elem->SetDoubleAttribute("QY", orientation[1]); elem->SetDoubleAttribute("QZ", orientation[2]); elem->SetDoubleAttribute("QR", orientation[3]); elem->SetDoubleAttribute("C00", matrix[0][0]); elem->SetDoubleAttribute("C01", matrix[0][1]); elem->SetDoubleAttribute("C02", matrix[0][2]); elem->SetDoubleAttribute("C03", matrix[0][3]); elem->SetDoubleAttribute("C04", matrix[0][4]); elem->SetDoubleAttribute("C05", matrix[0][5]); elem->SetDoubleAttribute("C10", matrix[1][0]); elem->SetDoubleAttribute("C11", matrix[1][1]); elem->SetDoubleAttribute("C12", matrix[1][2]); elem->SetDoubleAttribute("C13", matrix[1][3]); elem->SetDoubleAttribute("C14", matrix[1][4]); elem->SetDoubleAttribute("C15", matrix[1][5]); if (dataValid) elem->SetAttribute("Valid",1); else elem->SetAttribute("Valid",0); if (hasOrientation) elem->SetAttribute("hO",1); else elem->SetAttribute("hO",0); if (hasPosition) elem->SetAttribute("hP",1); else elem->SetAttribute("hP",0); // set additional attribute? std::map >::iterator it = m_AdditionalAttributes.find( nd ); if( it != m_AdditionalAttributes.end() ) { elem->SetAttribute(it->second.first, it->second.second); } *m_Stream << " " << *elem << std::endl; delete elem; } else if (this->m_OutputFormat == mitk::NavigationDataRecorder::csv) { *m_Stream << ";" << dataValid << ";" << position[0] << ";" << position[1] << ";" << position[2] << ";" << orientation[0] << ";" << orientation[1] << ";" << orientation[2] << ";" << orientation[3]; } } } if (this->m_OutputFormat == mitk::NavigationDataRecorder::csv) { *m_Stream << "\n"; } } m_RecordCounter++; if ((m_RecordCountLimit<=m_RecordCounter)&&(m_RecordCountLimit != -1)) {StopRecording();} } void mitk::NavigationDataRecorder::SetAdditionalAttribute(const NavigationData* nd, const std::string& attributeName , const std::string& attributeValue ) { std::map >::iterator it = m_AdditionalAttributes.find( nd ); if( it == m_AdditionalAttributes.end() ) m_AdditionalAttributes[nd] = std::pair(attributeName, attributeValue); else { it->second.first = attributeName; it->second.second = attributeValue; } } void mitk::NavigationDataRecorder::RemoveAdditionalAttribute( const NavigationData* nd ) { std::map >::iterator it = m_AdditionalAttributes.find( nd ); if( it != m_AdditionalAttributes.end() ) m_AdditionalAttributes.erase(it); } void mitk::NavigationDataRecorder::StartRecording() { if (m_Recording) { std::cout << "Already recording please stop before start new recording session" << std::endl; return; } if (m_Stream == NULL) { std::stringstream ss; std::ostream* stream; //An existing extension will be cut and replaced with .xml std::string tmpPath = itksys::SystemTools::GetFilenamePath(m_FileName); m_FileName = itksys::SystemTools::GetFilenameWithoutExtension(m_FileName); std::string extension = ".xml"; if (m_OutputFormat == mitk::NavigationDataRecorder::csv) extension = ".csv"; ss << tmpPath << "/" << m_FileName << "-" << m_NumberOfRecordedFiles << extension; if( m_DoNotOverwriteFiles ) { unsigned int index = m_NumberOfRecordedFiles+1; while( itksys::SystemTools::FileExists( ss.str().c_str() ) ) { ss.str(""); ss << tmpPath << "/" << m_FileName << "-" << index << extension; index++; } } switch(m_RecordingMode) { case Console: stream = &std::cout; break; case NormalFile: //Check if there is a file name and path if (m_FileName == "") { stream = &std::cout; std::cout << "No file name or file path set the output is redirected to the console"; } else { stream = new std::ofstream(ss.str().c_str()); } break; case ZipFile: stream = &std::cout; std::cout << "Sorry no ZipFile support yet"; break; default: stream = &std::cout; break; } + m_Stream = stream; + m_StreamMustBeDeleted = true; m_firstLine = true; m_RecordCounter = 0; StartRecording(stream); } } void mitk::NavigationDataRecorder::StartRecording(std::ostream* stream) { if (m_Recording) { std::cout << "Already recording please stop before start new recording session" << std::endl; return; } m_Stream = stream; m_Stream->precision(10); //TODO store date and GMT time if (m_Stream) { if (m_OutputFormat == mitk::NavigationDataRecorder::xml) { *m_Stream << "" << std::endl; /**m_Stream << "" << std::endl;*/ // should be a generic version, meaning a member variable, which has the actual version *m_Stream << " " << "" << std::endl; } m_Recording = true; } } void mitk::NavigationDataRecorder::StopRecording() { if (!m_Recording) { std::cout << "You have to start a recording first" << std::endl; return; } if ((m_Stream) && (m_OutputFormat == mitk::NavigationDataRecorder::xml)) { *m_Stream << "" << std::endl; } m_NumberOfRecordedFiles++; m_Recording = false; m_Stream->flush(); + if (m_StreamMustBeDeleted) //stream must only be deleted if it was created inside this class + { + m_StreamMustBeDeleted = false; + delete m_Stream; + } m_Stream = NULL; } diff --git a/Modules/IGT/IGTFilters/mitkNavigationDataRecorder.h b/Modules/IGT/IGTFilters/mitkNavigationDataRecorder.h index 8c45e09864..1985af7755 100644 --- a/Modules/IGT/IGTFilters/mitkNavigationDataRecorder.h +++ b/Modules/IGT/IGTFilters/mitkNavigationDataRecorder.h @@ -1,204 +1,212 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: 2008-02-08 13:23:19 +0100 (Fr, 08 Feb 2008) $ Version: $Revision: 13561 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef _MITK_NavigationDataRecorder_H #define _MITK_NavigationDataRecorder_H #include #include "mitkNavigationData.h" #include #include namespace mitk { /**Documentation * \brief This class records NavigationData objects. * * The output of this class is formated as a XML document. * * Internal this class uses streams for recording NavigationData objects. Therefore different types of output are possible * and can be set with the SetOutputMode() method. The default output is directed to the console. If you want to save into a * file you have to set a file name and the path. The recording is started with the call of the method StartRecording(). Now * every Update() stores the current state of the added NavigationDatas. With StopRecording() the stream is stopped. With * another call of StartRecording() the output is written to a new file with incremented filename counter. * * \warning At the moment there is no check if the file is already existing and this class will override existing files. * \ingroup IGT */ class MitkIGT_EXPORT NavigationDataRecorder : public itk::ProcessObject { public: mitkClassMacro( NavigationDataRecorder, itk::ProcessObject ); itkNewMacro( Self ); + /**Documentation + * \brief Determines where the output is directed to + * + * Console: std::cout + * NormalFile: std::ofstream + * ZipFile: Not supported yet -> std::cout + */ + enum RecordingMode + { + Console, + NormalFile, + ZipFile + }; + + /**Documentation + * \brief Determines the output format + * + * xml: XML format, also default, can be read by NavigationDataPlayer + * csv: use to export in excel, matlab, etc. + */ + enum OutputFormatEnum + { + xml, + csv + }; + /** * \brief sets the file name for the OutputMode NormalFile and ZipFile * * Any extensions will be cut * \warning existing files will be overridden * \warning do not use "." in file names at the end */ itkSetStringMacro(FileName); /** * \brief Returns the file name of the recording file (in OutputMode NormalFile and ZipFile) */ itkGetStringMacro(FileName); /** * \brief If true the recorder will never overwrite a file */ itkSetMacro(DoNotOverwriteFiles,bool); /** * \brief Returns whether the NavigationDataRecorder is recording or not */ itkGetMacro(Recording,bool); + /** + * \brief Returns the recording mode + */ + itkGetMacro(RecordingMode,RecordingMode); + /** * \brief Returns the number of data sets / frames which were recorded by the NavigationDataRecorder since start */ itkGetMacro(RecordCounter,int); /** * \brief Sets a limit of recorded data sets / frames. Recording will be stopped if the number is reached. -1 disables the limit, -1 is default value as well. */ itkSetMacro(RecordCountLimit,int); /** * \brief Adds the input NavigationDatas */ virtual void AddNavigationData(const NavigationData* nd); /// /// set an additional attribute for a specified navigation data /// this will be written for each navigation data and may be /// updated before calling Update() /// void SetAdditionalAttribute( const NavigationData* nd, const std::string& attributeName , const std::string& attributeValue ); void RemoveAdditionalAttribute( const NavigationData* nd ); /**Documentation * \brief Starts the recording with the presetted OutputMode * this method calls StartRecording(std::ostream*) */ void StartRecording(); /**Documentation * \brief Starts the recording with an own preinitialized stream */ void StartRecording(std::ostream* stream); /**Documentation * \brief Stops the recording and closes the stream */ void StopRecording(); /**Documentation * \brief Every call of update causes one line for each added NavigationData in the output if the recording was started */ virtual void Update(); - /**Documentation - * \brief Determines where the output is directed to - * - * Console: std::cout - * NormalFile: std::ofstream - * ZipFile: Not supported yet -> std::cout - */ - enum RecordingMode - { - Console, - NormalFile, - ZipFile - }; - - /**Documentation - * \brief Determines the output format - * - * xml: XML format, also default, can be read by NavigationDataPlayer - * csv: use to export in excel, matlab, etc. - */ - enum OutputFormatEnum - { - xml, - csv - }; /**Documentation * \brief Sets the recording mode which causes different types of output streams * see enum RecordingMode */ void SetRecordingMode(RecordingMode mode); /**Documentation * \brief Sets the output format which causes different formats of output streams. The XML format is default. * Also see enum OutputFormat for more information. */ itkSetMacro(OutputFormat,mitk::NavigationDataRecorder::OutputFormatEnum); protected: /**Documentation * \brief filter execute method here it is not used * */ virtual void GenerateData(); NavigationDataRecorder(); virtual ~NavigationDataRecorder(); std::string m_FileName; ///< stores the file name and path unsigned int m_NumberOfInputs; ///< counts the numbers of added input NavigationDatas std::ostream* m_Stream; ///< the output stream + bool m_StreamMustBeDeleted; + RecordingMode m_RecordingMode; ///< stores the mode see enum RecordingMode OutputFormatEnum m_OutputFormat; ///< stores the output format; see enum OutputFormat bool m_Recording; ///< indicates whether the recording is started or not int m_RecordCounter; ///< counts the number of frames which are recorded since StartRecording int m_RecordCountLimit; ///< limits the number of frames, recording will be stopped if the limit is reached. -1 disables the limit bool m_firstLine; //for the csv writer to detect wether the header must be written unsigned int m_NumberOfRecordedFiles; ///< necessary for the naming of the file if there is more than one start-stop cycle mitk::RealTimeClock::Pointer m_SystemTimeClock; ///< system time clock for system time tag in output xml file bool m_DoNotOverwriteFiles; ///< do not overwrite any files if true std::map > m_AdditionalAttributes; }; } #endif // #define _MITK_POINT_SET_SOURCE_H diff --git a/Modules/IGT/Testing/mitkNavigationDataRecorderTest.cpp b/Modules/IGT/Testing/mitkNavigationDataRecorderTest.cpp index be1904a812..098f77ef47 100644 --- a/Modules/IGT/Testing/mitkNavigationDataRecorderTest.cpp +++ b/Modules/IGT/Testing/mitkNavigationDataRecorderTest.cpp @@ -1,96 +1,312 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date: 2009-05-13 14:52:01 +0200 (Mi, 13. Mai 2009) $ Version: $Revision: 17230 $ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ -#include "mitkNavigationDataRecorder.h" -#include "mitkNavigationData.h" +#include +#include +#include +#include +#include + +#include +#include -#include "mitkTestingMacros.h" #include #include -/**Documentation - * test for the class "NavigationDataRecorder". - */ -int mitkNavigationDataRecorderTest(int /* argc */, char* /*argv*/[]) -{ - MITK_TEST_BEGIN("NavigationDataRecorder"); - std::string tmp = ""; +class mitkNavigationDataRecorderTestClass + { + public: + + static void TestInstantiation() + { + // let's create an object of our class + mitk::NavigationDataRecorder::Pointer recorder = mitk::NavigationDataRecorder::New(); + MITK_TEST_CONDITION( recorder.IsNotNull(), "Testing instatiation of NavigationDataRecorder"); + } + + static void TestRecordingWithGivenStream() + { + std::string tmp = ""; std::ostringstream* stream = new std::ostringstream( std::ostringstream::trunc ); stream->setf( std::ios::fixed, std::ios::floatfield ); // let's create an object of our class mitk::NavigationDataRecorder::Pointer recorder = mitk::NavigationDataRecorder::New(); - // first test: did this work? - // using MITK_TEST_CONDITION_REQUIRED makes the test stop after failure, since - // it makes no sense to continue without an object. - MITK_TEST_CONDITION_REQUIRED(recorder.IsNotNull(), "Testing instantiation"); MITK_TEST_CONDITION(recorder->GetInputs().size() == 0, "testing initial number of inputs"); MITK_TEST_CONDITION(recorder->GetOutputs().size() == 0, "testing initial number of outputs"); mitk::NavigationData::Pointer naviData = mitk::NavigationData::New(); recorder->AddNavigationData( naviData ); //recorder->SetFileName("e:/test"); //recorder->SetRecordingMode( mitk::NavigationDataRecorder::NormalFile ); recorder->StartRecording( stream ); for ( unsigned int i=0; i<5; i++ ) { mitk::Point3D pnt; pnt[0] = i + 1; pnt[1] = i + 1/2; pnt[2] = i +1*3; naviData->SetPosition(pnt); //naviData->Modified(); recorder->Update(); //Sleep(80 + i*10); } recorder->StopRecording(); std::string str = stream->str(); int pos = str.find( "ToolCount=" ); std::string sub = stream->str().substr(pos+11, 1); MITK_TEST_CONDITION( sub.compare("1") == 0, "check if number of inputs is correct by stringstream"); pos = str.find( "X=" ); sub = stream->str().substr(pos+3, 1); MITK_TEST_CONDITION( sub.compare("1") == 0, "check if the X coordinate is correct"); pos = str.find( "Y=" ); sub = stream->str().substr(pos+3, 1); MITK_TEST_CONDITION( sub.compare("0") == 0, "check if the Y coordinate is correct"); pos = str.find( "Z=" ); sub = stream->str().substr(pos+3, 1); MITK_TEST_CONDITION( sub.compare("3") == 0, "check if the Z coordinate is correct"); recorder->SetFileName("blablabla"); const char* string = recorder->GetFileName(); MITK_TEST_CONDITION( strcmp(string, "blablabla") == 0, "check if set- and getName-methods work"); + } + + static void TestRecordingOnHarddiscXML() + { + mitk::NavigationDataRecorder::Pointer recorder = mitk::NavigationDataRecorder::New(); + + //create filename + std::string filename = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory()+Poco::Path::separator()+"Recordertest.xml"; + + recorder->SetFileName(filename.c_str()); + + mitk::NavigationData::Pointer naviData = mitk::NavigationData::New(); + recorder->AddNavigationData( naviData ); + recorder->StartRecording(); + + for ( unsigned int i=0; i<5; i++ ) + { + mitk::Point3D pnt; + pnt[0] = i + 1; + pnt[1] = i + 1/2; + pnt[2] = i +1*3; + naviData->SetPosition(pnt); + recorder->Update(); + } + + recorder->StopRecording(); + + std::string prooffilename = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory()+Poco::Path::separator()+"Recordertest-0.xml"; + Poco::File myFile(prooffilename); + MITK_TEST_CONDITION(myFile.exists(),"Testing XML recording on harddisc (does file exist?)."); + } + + static void TestRecordingOnHarddiscXMLZIP() + { + mitk::NavigationDataRecorder::Pointer recorder = mitk::NavigationDataRecorder::New(); + + //create filename + std::string filename = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory()+Poco::Path::separator()+"Recordertestzip.xml"; + + recorder->SetFileName(filename.c_str()); + recorder->SetRecordingMode(mitk::NavigationDataRecorder::ZipFile); + + MITK_TEST_CONDITION(recorder->GetRecordingMode()==mitk::NavigationDataRecorder::ZipFile,"Testing setter of recording mode."); + + /* Zip file not supported yet, activate later + mitk::NavigationData::Pointer naviData = mitk::NavigationData::New(); + recorder->AddNavigationData( naviData ); + recorder->StartRecording(); + + for ( unsigned int i=0; i<5; i++ ) + { + mitk::Point3D pnt; + pnt[0] = i + 1; + pnt[1] = i + 1/2; + pnt[2] = i +1*3; + naviData->SetPosition(pnt); + recorder->Update(); + } + + recorder->StopRecording(); + + std::string prooffilename = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory()+Poco::Path::separator()+"Recordertest-0.xml"; + Poco::File myFile(prooffilename); + MITK_TEST_CONDITION(myFile.exists(),"Testing XML Zip recording on harddisc (does file exist?)."); + */ + } + + static void TestRecordingOnHarddiscCSV() + { + mitk::NavigationDataRecorder::Pointer recorder = mitk::NavigationDataRecorder::New(); + + //create filename + std::string filename = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory()+Poco::Path::separator()+"Recordertest.xml"; + + recorder->SetFileName(filename.c_str()); + recorder->SetOutputFormat(mitk::NavigationDataRecorder::csv); + + mitk::NavigationData::Pointer naviData = mitk::NavigationData::New(); + recorder->AddNavigationData( naviData ); + recorder->StartRecording(); + + for ( unsigned int i=0; i<5; i++ ) + { + mitk::Point3D pnt; + pnt[0] = i + 1; + pnt[1] = i + 1/2; + pnt[2] = i +1*3; + naviData->SetPosition(pnt); + recorder->Update(); + } + + recorder->StopRecording(); + + std::string prooffilename = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory()+Poco::Path::separator()+"Recordertest-0.csv"; + Poco::File myFile(prooffilename); + MITK_TEST_CONDITION(myFile.exists(),"Testing CSV recording on harddisc (does file exist?)."); + } + + static void TestLoadingRecordedXMLFile() + { + mitk::NavigationDataPlayer::Pointer myPlayer = mitk::NavigationDataPlayer::New(); + std::string filenameXML = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory()+Poco::Path::separator()+"Recordertest-0.xml"; + myPlayer->SetFileName(filenameXML.c_str()); + myPlayer->StartPlaying(); + + //only testing first position at the moment + myPlayer->Update(); + mitk::NavigationData::Pointer thisData = myPlayer->GetOutput(); + mitk::Point3D reference_pnt; + reference_pnt[0] = 1; + reference_pnt[1] = 1/2; + reference_pnt[2] = 1*3; + + myPlayer->StopPlaying(); + + MITK_TEST_CONDITION((thisData->GetPosition() == reference_pnt),"Testing load data from xml file."); + } + + static void TestRecordingInvalidData() + { + std::string tmp = ""; + std::ostringstream* stream = new std::ostringstream( std::ostringstream::trunc ); + stream->setf( std::ios::fixed, std::ios::floatfield ); + + // let's create an object of our class + mitk::NavigationDataRecorder::Pointer recorder = mitk::NavigationDataRecorder::New(); + + + mitk::NavigationData::Pointer naviData = mitk::NavigationData::New(); + recorder->AddNavigationData( naviData ); + naviData->SetDataValid(false); + //recorder->SetFileName("e:/test"); + //recorder->SetRecordingMode( mitk::NavigationDataRecorder::NormalFile ); + recorder->StartRecording( stream ); + for ( unsigned int i=0; i<5; i++ ) + { + mitk::Point3D pnt; + pnt[0] = i + 1; + pnt[1] = i + 1/2; + pnt[2] = i +1*3; + + + naviData->SetPosition(pnt); + //naviData->Modified(); + recorder->Update(); + + //Sleep(80 + i*10); + + } + + recorder->StopRecording(); + + bool record_success = true; + + std::string str = stream->str(); + int pos = str.find( "ToolCount=" ); + std::string sub = stream->str().substr(pos+11, 1); + if (sub.compare("1") != 0) {record_success = false;} + + pos = str.find( "X=" ); + sub = stream->str().substr(pos+3, 1); + if (sub.compare("1") != 0) {record_success = false;} + + pos = str.find( "Y=" ); + sub = stream->str().substr(pos+3, 1); + if (sub.compare("0") != 0) {record_success = false;} + + pos = str.find( "Z=" ); + sub = stream->str().substr(pos+3, 1); + if (sub.compare("3") != 0) {record_success = false;} + + MITK_TEST_CONDITION(record_success,"Testing recording of invalid data."); + + } + + static void CleanUp() + { + std::string filenameXML = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory()+Poco::Path::separator()+"Recordertest-0.xml"; + std::string filenameCSV = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory()+Poco::Path::separator()+"Recordertest-0.csv"; + Poco::File myFileXML(filenameXML); + if (myFileXML.exists()) + {myFileXML.remove();} + Poco::File myFileCSV(filenameCSV); + if (myFileCSV.exists()) + {myFileCSV.remove();} + + } + + }; + +/**Documentation + * test for the class "NavigationDataRecorder". + */ +int mitkNavigationDataRecorderTest(int /* argc */, char* /*argv*/[]) +{ + MITK_TEST_BEGIN("NavigationDataRecorder"); + + mitkNavigationDataRecorderTestClass::TestInstantiation(); + mitkNavigationDataRecorderTestClass::TestRecordingWithGivenStream(); + mitkNavigationDataRecorderTestClass::TestRecordingOnHarddiscXML(); + mitkNavigationDataRecorderTestClass::TestRecordingOnHarddiscXMLZIP(); + mitkNavigationDataRecorderTestClass::TestRecordingOnHarddiscCSV(); + mitkNavigationDataRecorderTestClass::TestRecordingInvalidData(); + mitkNavigationDataRecorderTestClass::TestLoadingRecordedXMLFile(); + + mitkNavigationDataRecorderTestClass::CleanUp(); + + // always end with this! MITK_TEST_END(); }