#include "mainwindow.h" #include "ui_mainwindow.h" #include "formworklist.h" #include "formhistory.h" #include "formsetting.h" #include "formcapture.h" #include "formviewer.h" #include "qrtspthread.h" #include "qgstreamerplayer.h" #include #include "charconvert.h" #include "dialogpowerbutton.h" #include #include "dialoglogin.h" #include "dialogchecksumerror.h" #include "dialogexportsamba.h" #include "dialogsettingnetworkshare.h" #include #include MainWindow* MainWindow::m_pMainWindow = NULL; CommonData* MainWindow::m_pCommonData = NULL; extern QRTSPThread t; extern SThreadWatchVideoLink v; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { m_bMountDiskUSB = false; m_pProcessNode = NULL; m_pCommonData = NULL; QRTSPThread* pThreadRTSP = &t; SThreadWatchVideoLink* pThreadWatchVideoLink = &v; m_pMainWindow = this; m_pCommonData = new CommonData; m_pCommonData->SetUseWebDisplay(true); setWindowFlags(Qt::Window | Qt::FramelessWindowHint); m_pThreadWatch = NULL; //#ifndef _PC m_pThreadWatch = new SThreadWatchPort(); connect(m_pThreadWatch, SIGNAL(CaptureHandSwitch()), this, SLOT(CaptureHandSwitch())); m_pThreadWatch->start(); //#endif WRITE_FUNCTION_LOG(); ui->setupUi(this); setGeometry(0, 0, 1920, 1080); FormWorklist* pWorklist = new FormWorklist(this); pWorklist->setGeometry(58, 166, 1808, 857); pWorklist->hide(); FormHistory* pHistory = new FormHistory(this); pHistory->setGeometry(58, 166, 1808, 857); pHistory->hide(); FormSetting* pSetting = new FormSetting(this); pSetting->setGeometry(58, 166, 1808, 857); pSetting->hide(); FormCapture* pCapture = new FormCapture(this); pCapture->setGeometry(0, 0, 1920, 1080); pCapture->hide(); FormViewer* pViewer = new FormViewer(this); pViewer->setGeometry(0, 0, 1920, 1080); pViewer->hide(); #ifndef _PC //connect(m_pThreadWatch, SIGNAL(HDMICalbeConnect(bool)), pCapture, SLOT(HDMICalbeConnect(bool))); #endif pThreadWatchVideoLink->SetThreadRTSP(pThreadRTSP); //connect(pThreadWatchVideoLink, SIGNAL(HDMICalbeConnect(bool)), pCapture, SLOT(HDMICalbeConnect(bool))); pThreadWatchVideoLink->WatchVideoConnect(); connect(this, SIGNAL(SendCaptureHandSwitch()), pCapture, SLOT(CaptureHandSwitch())); connect(this, SIGNAL(SendCaptureFootSwitch()), pCapture, SLOT(CaptureFootSwitch())); connect(pThreadRTSP, SIGNAL(VideoButtonChange(bool)), pCapture, SLOT(VideoButtonChange(bool))); //Test Code connect(pWorklist, SIGNAL(Exit()), this, SLOT(Exit())); connect(&m_TimerCheckDiskUSB, SIGNAL(timeout()), this, SLOT(CheckDiskUSB())); m_ListWidget.push_back(pWorklist); m_ListWidget.push_back(pHistory); m_ListWidget.push_back(pSetting); m_ListWidget.push_back(pCapture); m_ListWidget.push_back(pViewer); m_pDialogMissCapture = new DialogMissCapture(this); m_nCurrentMenuID = -1; m_nCurrentWidgetID = -1; connect(&m_Timer, SIGNAL(timeout()), this, SLOT(DeferredInit())); connect(&m_TimerWatchPowerButton, SIGNAL(timeout()), this, SLOT(WatchPowerButton())); connect(&m_TimerMissCapture, SIGNAL(timeout()), this, SLOT(MissCapture())); connect(pCapture, SIGNAL(BackupNetworkShareFolder(QString)), this, SLOT(BackupNetworkShareFolder(QString))); //m_pCommonData->SetUseNetworkBackupVideo(true); connect(&m_TimerScreenSaver, SIGNAL(timeout()), this, SLOT(CheckShowScreenSaver())); connect(&m_TimerLog, SIGNAL(timeout()), this, SLOT(WriteLogTimer())); m_TimerLog.start(1000*5); // 5 seconds m_Timer.start(500); m_TimerWatchPowerButton.start(1000); m_pScreenSaver = new FormScreenSaver; m_pScreenSaver->hide(); pThreadWatchVideoLink->ChangeState(0); } MainWindow::~MainWindow() { FormCapture* pCapture = (FormCapture*)m_ListWidget[3]; disconnect(&m_Timer, SIGNAL(timeout()), this, SLOT(DeferredInit())); disconnect(&m_TimerWatchPowerButton, SIGNAL(timeout()), this, SLOT(WatchPowerButton())); disconnect(&m_TimerMissCapture, SIGNAL(timeout()), this, SLOT(MissCapture())); disconnect(&m_TimerScreenSaver, SIGNAL(timeout()), this, SLOT(CheckShowScreenSaver())); disconnect(this, SIGNAL(SendCaptureHandSwitch()), pCapture, SLOT(CaptureHandSwitch())); disconnect(this, SIGNAL(SendCaptureFootSwitch()), pCapture, SLOT(CaptureFootSwitch())); SThreadImageSave* pThreadImageSave = m_pCommonData->GetThreadCaptureImage(); if(pThreadImageSave!=NULL) { disconnect(pThreadImageSave, SIGNAL(ViewDialogMissCapture()), this, SLOT(ViewDialogMissCapture()) ); } WRITE_FUNCTION_LOG(); SAFE_DELETE(m_pCommonData); SAFE_DELETE(m_pScreenSaver); if(m_pThreadWatch!=NULL) { m_pThreadWatch->ExitThread(); SAFE_DELETE(m_pThreadWatch); } delete ui; } void MainWindow::CheckShowScreenSaver() { qint64 nTime = m_TimeCheckScreenSaver.elapsed()/(1000); int nScreenSaverTime = m_pCommonData->GetScreenSaverTime(); if(nScreenSaverTime==0) { return; } if(nTime>=60*nScreenSaverTime) //60 sec * 10 --> 10 minutes { if(m_pScreenSaver->isVisible()==false) { m_pScreenSaver->Init(); StopScreenSaverTimer(); } } } MainWindow* MainWindow::GetMainWindow() { return m_pMainWindow; } CommonData* MainWindow::GetCommonData() { return m_pCommonData; } void MainWindow::SetDisplayType(int nType) { WRITE_FUNCTION_LOG_PARAM(QString::number(nType)); m_nDisplayType = nType; m_pCommonData->SetDisplayType(nType); if(m_nDisplayType==SDISPLAY_1920X1080) { resize(1920, 1080); } else if(m_nDisplayType==SDISPLAY_1280X1024) { resize(1280, 1024); } } void MainWindow::ChangeMenu(int nID) { WRITE_FUNCTION_LOG_PARAM(QString::number(nID)); int nPrevCurrentMenuID = m_nCurrentMenuID; int nPrevCurrentWidgetID = m_nCurrentWidgetID; SThreadImageSave* pThreadImageSave = m_pCommonData->GetThreadCaptureImage(); if(m_nCurrentWidgetID>=0) { m_ListWidget[m_nCurrentWidgetID]->hide(); } switch(nID) { case SMENU_WORKLIST: m_nCurrentWidgetID = SWIDGET_WORKLIST; break; case SMENU_HISTORY: m_nCurrentWidgetID = SWIDGET_HISTORY; break; case SMENU_SETTING: m_nCurrentWidgetID = SWIDGET_SETTING; break; case SMENU_CAPTURE: m_nCurrentWidgetID = SWIDGET_CAPTURE; break; case SMENU_VIEWER: m_nCurrentWidgetID = SWIDGET_VIEWER; break; } if(nID!=SMENU_CAPTURE) { if(m_pCommonData->GetScreenSaverTime()>0) { if(m_TimerScreenSaver.isActive()==false) { ResetScreenSaverTimer(); StartScreenSaverTimer(); } } } else { if(m_pCommonData->GetScreenSaverTime()>0) { if(m_TimerScreenSaver.isActive()==true) { StopScreenSaverTimer(); } } } if(nID<=SMENU_SETTING) { m_nCurrentMenuID = nID; if(m_nCurrentMenuID==SMENU_WORKLIST) { ((FormWorklist*)m_ListWidget[m_nCurrentWidgetID])->Init(); } else if(m_nCurrentMenuID==SMENU_HISTORY) { ((FormHistory*)m_ListWidget[m_nCurrentWidgetID])->Init(); } else if(m_nCurrentMenuID==SMENU_SETTING) { ((FormSetting*)m_ListWidget[m_nCurrentWidgetID])->Init(); } m_ListWidget[m_nCurrentWidgetID]->show(); return; } else { if(nID==SMENU_CAPTURE) { { ACQUISITION_INFO* pAcquisitionInfo = m_pCommonData->GetAcquisitionInfo(); QDateTime dt = QDateTime::currentDateTime(); int nYear = dt.date().year(); int nMonth = dt.date().month(); int nDay = dt.date().day(); int nHour = dt.time().hour(); int nMinute = dt.time().minute(); int nSecond = dt.time().second(); SDATA_STUDY* pStudy = pAcquisitionInfo->GetStudyInfo(); //if(pStudy->strStudyDate.length()==0) { //pStudy->strStudyDate = (QString("%1%2%3").arg(QString::number(nYear).rightJustified(4, '0')).arg(QString::number(nMonth).rightJustified(2, '0')).arg(QString::number(nDay).rightJustified(2, '0'))).toStdString(); //pStudy->strStudyTime = (QString("%1%2%3").arg(QString::number(nHour).rightJustified(2, '0')).arg(QString::number(nMinute).rightJustified(2, '0')).arg(QString::number(nSecond).rightJustified(2, '0'))).toStdString(); } if(pAcquisitionInfo->GetPatientInfo()->strPatientID.size()==0) { pAcquisitionInfo->Clear(); pAcquisitionInfo->GetPatientInfo()->strPatientID = (QString("%1%2%3%4%5%6").arg(QString::number(nYear)).arg(QString::number(nMonth)).arg(QString::number(nDay)).arg(QString::number(nHour)).arg(QString::number(nMinute)).arg(QString::number(nSecond))).toStdString().c_str(); pAcquisitionInfo->GetPatientInfo()->strPatientName = (QString("테스트%1").arg(pAcquisitionInfo->GetPatientInfo()->strPatientID)).toStdString().c_str(); pAcquisitionInfo->GetStudyInfo()->strStudyInstanceUID = (QString("1.1.22.33.%1.1%2%3.1%4").arg(pAcquisitionInfo->GetPatientInfo()->strPatientID.toStdString().c_str()).arg(QString::number(nHour)).arg(QString::number(nMinute)).arg(QString::number(nSecond))).toStdString().c_str(); pAcquisitionInfo->GetStudyInfo()->strStudyDescription = "Emergency Study Desc."; pAcquisitionInfo->GetSeriesInfo()->strSeriesDescription = "Emergency Series Desc."; pAcquisitionInfo->GetSeriesInfo()->strOperatorsName = ""; pAcquisitionInfo->GetPatientInfo()->strPatientSex = "F"; pAcquisitionInfo->GetPatientInfo()->strPatientWeight = "70"; pAcquisitionInfo->GetPatientInfo()->strPatientBirthDate = "19990909"; } SDatabase* pDatabase = NULL; int nStudyIndex = 0; int nPatientIndex = 0; int nSeriesImageIndex = 0; int nSeriesMovieIndex = 0; bool bCreateStudy = false; int nMaxTestCount = 10; int nTestCount = 0; for(nTestCount=0 ; nTestCount(this))); if (db.OpenDatabase("/home/birdhead/test.db") != 0) { qWarning() << "DB open failed, retrying..."; usleep(100 * 1000); // DB 연결 실패 시 잠시 대기 후 재시도 continue; } // 2. 트랜잭션을 시작합니다. 이제부터 모든 작업은 "All or Nothing" 입니다. if (!db.BeginTransaction()) { qWarning() << "Failed to begin transaction, retrying..."; usleep(10 * 1000); continue; } // 3. 트랜잭션 내에서 모든 INSERT 작업을 수행합니다. nPatientIndex = db.InsertPatient(pAcquisitionInfo); nStudyIndex = db.InsertStudy(pAcquisitionInfo); nSeriesImageIndex = db.InsertSeriesImage(pAcquisitionInfo); nSeriesMovieIndex = db.InsertSeriesMovie(pAcquisitionInfo); // 4. 모든 작업의 성공 여부를 확인합니다. if (nPatientIndex > 0 && nStudyIndex > 0 && nSeriesImageIndex > 0 && nSeriesMovieIndex > 0) { // 5. 모두 성공했다면, 트랜잭션을 최종적으로 데이터베이스에 반영 (Commit) if (db.Commit()) { bCreateStudy = true; // 최종 성공! } else { qWarning() << "Transaction commit failed! Rolling back..."; db.Rollback(); // Commit 실패 시 롤백 } } else { // 하나라도 실패했다면, 지금까지의 모든 작업을 취소 (Rollback) qWarning() << "One or more insert failed. Rolling back..."; db.Rollback(); } // 무언가 실패하여 재시도해야 하는 경우, 잠시 대기 if (!bCreateStudy) { usleep(10 * 1000); } } nStudyIndex = pAcquisitionInfo->m_nIndexStudy; m_pCommonData->ReleaseAcquisitionInfo(); //bCreateStudy = false; if(bCreateStudy==false) { m_nCurrentMenuID = nPrevCurrentMenuID; m_nCurrentWidgetID = nPrevCurrentWidgetID; m_ListWidget[m_nCurrentWidgetID]->show(); if(m_nCurrentMenuID==SMENU_WORKLIST) { FormWorklist* pWorklist = (FormWorklist*)m_ListWidget[m_nCurrentWidgetID]; pWorklist->ErrorPatientInfo(); } else if(m_nCurrentMenuID==SMENU_HISTORY) { FormHistory* pHistory = (FormHistory*)m_ListWidget[m_nCurrentWidgetID]; } SetDisplayError(0x08); return; } if (nStudyIndex > 0) { // 1. 이 로직 블록 전체에서 사용할 단 하나의 지역 DB 인스턴스를 생성합니다. SUTIL::SDatabase db(QString("ProcessStudy_%1").arg(reinterpret_cast(this))); if (db.OpenDatabase("/home/birdhead/test.db") != 0) { qWarning() << "DB open failed for study processing, StudyIndex:" << nStudyIndex; return; // 또는 다른 오류 처리 } // 2. 새로운 API를 사용합니다. 함수가 직접 결과 "값"을 반환합니다. QList listResponse = db.GetImageWithStudyIndexASC(nStudyIndex); int nTotalCount = listResponse.size(); if (nTotalCount > 0) { // 3. 동일한 지역 DB 인스턴스를 사용하여 다음 DB 작업을 수행합니다. db.AddImageToAcquisitionTemp(nStudyIndex); usleep(10 * 1000); // 4. 다른 스레드로 데이터를 넘겨줄 때도 값으로 넘겨줍니다. // (참고: pThreadImageSave의 SetListImageWithStudyASC 함수 시그니처도 // const QList& 타입으로 변경하는 것이 가장 이상적입니다.) pThreadImageSave->SetListImageWithStudyASC(listResponse); pThreadImageSave->ChangeState(0x200); while (pThreadImageSave->GetCurrentState() != 0x2000) { QThread::usleep(1000 * 100); } pThreadImageSave->ChangeState(0); } // 5. 더 이상 수동으로 메모리를 해제할 필요가 없습니다. // SDatabase::DeleteListReponse(listResponse); // 이 줄은 완전히 제거됩니다. } } m_nCurrentMenuID = (m_nCurrentMenuID | SMENU_CAPTURE); FormCapture* pCapture = (FormCapture*)m_ListWidget[m_nCurrentWidgetID]; pCapture->Init(); pCapture->show(); pCapture->setFocus(); /* if(m_pCommonData->IsAutoVideoCapture()==true) { //m_pCommonData->GetThreadRTSP()->ChangeState(0); QString strVideoFilename = m_pCommonData->GetNewFilename(DISPLAY_VIDEO); m_pCommonData->GetThreadRTSP()->SaveFile(strVideoFilename); m_pCommonData->GetThreadRTSP()->ChangeState(6); } else { m_pCommonData->GetThreadRTSP()->ChangeState(0); } */ m_pCommonData->GetThreadRTSP()->ChangeState(0); usleep(1*1000); } else if(nID==SMENU_VIEWER) { HISTORY_STUDY* pHistoryStudy = m_pCommonData->GetCurrentHistoryStudy(); if(pHistoryStudy==NULL) { return; } //QString strStudyInstanceUID = *pHistoryStudy->pStrStudyInstanceUID; //int nStudyIndex = pDatabase->GetStudyIndexWithStudyInstanceUID(strStudyInstanceUID); QString strStudyIndex = *pHistoryStudy->pStrStudyIndex; m_pCommonData->ReleaseCurrentHistoryStudy(); int nStudyIndex = strStudyIndex.toInt(); FormViewer* pViewer = (FormViewer*)m_ListWidget[m_nCurrentWidgetID]; m_nCurrentMenuID = (m_nCurrentMenuID | SMENU_VIEWER); QDate dateToday = QDate::currentDate(); int nYear = dateToday.year(); int nMonth = dateToday.month(); int nDay = dateToday.day(); QString strSearchDate = QString("%1/%2/%3-%1/%2/%3").arg(QString::number(nYear).rightJustified(4, '0')).arg(QString::number(nMonth).rightJustified(2, '0')).arg(QString::number(nDay).rightJustified(2, '0')); CommonData* pCommonData = MainWindow::GetCommonData(); pCommonData->ClearHistoryImage(); pCommonData->ClearHistorySearchItem(); pCommonData->ClearHistorySearchStudyIndex(); SEARCH_ITEM si; //QList listResponse; si.m_strStudyDate = strSearchDate; //si.m_strStudyDate = "2021/08/06-2021/08/12"; //si.m_strPatientName = "202188"; int historyCount = 0; QList listResponse; // 포인터가 아닌 값 타입의 리스트 // 1. 이 작업을 위한 지역 DB 인스턴스를 생성하고 연결합니다. SUTIL::SDatabase db(QString("HistoryAndImageFetch_%1").arg(reinterpret_cast(this))); if (db.OpenDatabase("/home/birdhead/test.db") == 0) // 0이 성공 { // 2. 새로운 GetHistory API를 호출하고, 반환 값을 변수에 저장합니다. // (기존 코드에서는 이 값을 사용하지 않았지만, 필요할 수 있으므로 받아둡니다.) historyCount = db.GetHistory(&si); // 3. 새로운 GetImageWithStudyIndex API를 호출하여 결과를 직접 받습니다. // 더 이상 출력 파라미터나 포인터를 사용하지 않습니다. listResponse = db.GetImageWithStudyIndex(nStudyIndex); } else { qWarning() << "DB open failed for history/image fetching."; // 필요에 따라 오류 처리 } // 이제 'historyCount'와 'listResponse'를 안전하게 사용할 수 있습니다. // 예를 들어: qDebug() << "Found history items:" << historyCount; qDebug() << "Found images in study:" << listResponse.size(); // 수동 메모리 해제는 더 이상 필요 없습니다. // SDatabase::DeleteListReponse(listResponse); // 이 줄은 완전히 제거됩니다. pCommonData->SetSearchItemHistory(si); pCommonData->SetHistorySearchStudyIndex(nStudyIndex); int nImageCount = listResponse.size(); pViewer->Init(); pViewer->SetTotalImageCount(nImageCount); //pViewer->UpdateViewer(); pViewer->ChangeViewMode(2); SThreadImageSave* pThreadImageSave = m_pCommonData->GetThreadCaptureImage(); pThreadImageSave->SetListImageWithStudy(listResponse); pThreadImageSave->ChangeState(0x08); pViewer->show(); //SDatabase::DeleteListReponse(listResponse); } return; } } void MainWindow::ExitCapture() { WRITE_FUNCTION_LOG(); m_nCurrentMenuID = (m_nCurrentMenuID & ~SMENU_CAPTURE); SThreadImageSave* pThreadCaptureImage = m_pCommonData->GetThreadCaptureImage(); pThreadCaptureImage->ExitCapture(); pThreadCaptureImage->ClearCaptureSaveToFile(); FormCapture* pCapture = (FormCapture*)m_ListWidget[m_nCurrentWidgetID]; if(pCapture->isVisible()==true) { pCapture->ExitCapture(); pCapture->hide(); } m_pCommonData->ClearCaptureImage(); ACQUISITION_INFO* pAcquisitionInfo = m_pCommonData->GetAcquisitionInfo(); pAcquisitionInfo->Clear(); m_pCommonData->ReleaseAcquisitionInfo(); switch(m_nCurrentMenuID) { case SMENU_WORKLIST: m_nCurrentWidgetID = SWIDGET_WORKLIST; break; case SMENU_HISTORY: m_nCurrentWidgetID = SWIDGET_HISTORY; break; case SMENU_SETTING: m_nCurrentWidgetID = SWIDGET_SETTING; break; case SMENU_CAPTURE: m_nCurrentWidgetID = SWIDGET_CAPTURE; break; case SMENU_VIEWER: m_nCurrentWidgetID = SWIDGET_VIEWER; break; } if(m_nCurrentMenuID==0x22) { ui->widgetMainMenu->SelectWorklist(); //m_nCurrentMenuID = SMENU_WORKLIST; //m_nCurrentWidgetID = SWIDGET_WORKLIST; //ChangeMenu(m_nCurrentMenuID); } m_ListWidget[m_nCurrentWidgetID]->show(); if(m_nCurrentWidgetID!=SMENU_CAPTURE) { if(m_pCommonData->GetScreenSaverTime()>0) { if(m_TimerScreenSaver.isActive()==false) { ResetScreenSaverTimer(); StartScreenSaverTimer(); } } } QWidget* pFocusWidget = focusWidget(); if(m_nCurrentWidgetID==SWIDGET_WORKLIST) { FormWorklist* pFormWorklist = (FormWorklist*)m_ListWidget[m_nCurrentWidgetID]; pFormWorklist->Init(); repaint(); } if(pFocusWidget!=m_ListWidget[m_nCurrentWidgetID]) { m_ListWidget[m_nCurrentWidgetID]->setFocus(Qt::ActiveWindowFocusReason); connect(&m_Timer, SIGNAL(timeout()), this, SLOT(ViewerChangeFocusCheck())); m_Timer.start(); } if(m_pCommonData->IsUseWebDisplay()==true) { if(m_pProcessNode!=NULL) { if(m_pProcessNode->state()==QProcess::Running) { m_pProcessNode->close(); int nCount = 0; while(m_pProcessNode->state()==QProcess::Running && nCount<100) { usleep(100); nCount++; } if(m_pProcessNode->state()!=QProcess::Running) { m_pProcessNode->start("/usr/bin/node", QStringList("index.js")); } } } } } void MainWindow::ErrorCapture() { WRITE_FUNCTION_LOG(); ExitCapture(); } void MainWindow::ExitViewer() { WRITE_FUNCTION_LOG(); SThreadImageSave* pThreadImageSave = m_pCommonData->GetThreadCaptureImage(); if(pThreadImageSave->GetCurrentState()==4) { pThreadImageSave->StopLoadImage(); while(pThreadImageSave->GetCurrentState()==4) { usleep(100*1000); } } m_nCurrentMenuID = (m_nCurrentMenuID & ~SMENU_VIEWER); m_ListWidget[m_nCurrentWidgetID]->hide(); switch(m_nCurrentMenuID) { case SMENU_WORKLIST: m_nCurrentWidgetID = SWIDGET_WORKLIST; break; case SMENU_HISTORY: m_nCurrentWidgetID = SWIDGET_HISTORY; ((FormHistory*)m_ListWidget[m_nCurrentWidgetID])->Init(); break; case SMENU_SETTING: m_nCurrentWidgetID = SWIDGET_SETTING; break; case SMENU_CAPTURE: m_nCurrentWidgetID = SWIDGET_CAPTURE; break; case SMENU_VIEWER: m_nCurrentWidgetID = SWIDGET_VIEWER; break; } m_ListWidget[m_nCurrentWidgetID]->show(); m_pCommonData->ClearHistoryImage(); } void MainWindow::BackupNetworkShareFolder(QString strFilename) { //test samba backup DialogExportSamba dlgExport(this); QThread::usleep(1000*1000); dlgExport.exec(); } void MainWindow::SetRTSPThread(QRTSPThread* pThread) { m_pCommonData->SetRTSPThread(pThread); FormCapture* pCapture = (FormCapture*)m_ListWidget[3]; pThread->SetVideoWidget(pCapture->GetVideoWidget()); } void MainWindow::SetThreadCaptureImage(SThreadImageSave* pThread) { m_pCommonData->SetThreadCaptureImage(pThread); pThread->OpenDatabase(); FormCapture* pCapture = (FormCapture*)m_ListWidget[3]; connect(pThread, SIGNAL(captureImage(CAPTURE_IMAGE*)), pCapture, SLOT(captureImage(CAPTURE_IMAGE*))); connect(pThread, SIGNAL(captureVideo(CAPTURE_IMAGE*)), pCapture, SLOT(captureVideo(CAPTURE_IMAGE*))); FormViewer* pViewer = (FormViewer*)m_ListWidget[4]; connect(pThread, SIGNAL(SetImageLoadInfo(int, int, ImageSelect*)), pViewer, SLOT(SetImageLoadInfo(int, int, ImageSelect*))); connect(pThread, SIGNAL(ImageLoadComplete()), pViewer, SLOT(ImageLoadComplete())); //FormWorklist* pWorklist = (FormWorklist*)m_ListWidget[0]; //connect(pThread, SIGNAL(worklistUpdateComplete()), pWorklist, SLOT(worklistUpdateComplete())); //connect(pThread, SIGNAL(worklistError()), pWorklist, SLOT(worklistError())); //connect(pThread, SIGNAL(SetWorklistProgress(int)), pWorklist, SLOT(SetWorklistProgress(int))); connect(pThread, SIGNAL(CompleteCleanStorage()), this, SLOT(CompleteCleanStorage())); connect(pThread, SIGNAL(CompleteAcquisitionFromHistory()), this, SLOT(CompleteAcquisitionFromHistory())); connect(pThread, SIGNAL(LoadInfoAcquisitionFromHistory(int, int)), this, SLOT(LoadInfoAcquisitionFromHistory(int, int))); connect(pThread, SIGNAL(ViewDialogMissCapture()), this, SLOT(ViewDialogMissCapture())); } void MainWindow::SetThreadSendDICOM(SThreadSendDICOM* pThread) { m_pCommonData->SetThreadSendDICOM(pThread); //FormWorklist* pWorklist = (FormWorklist*)m_ListWidget[0]; //connect(pThread, SIGNAL(worklistUpdateComplete()), pWorklist, SLOT(worklistUpdateComplete())); //connect(pThread, SIGNAL(worklistError()), pWorklist, SLOT(worklistError())); //connect(pThread, SIGNAL(SetWorklistProgress(int)), pWorklist, SLOT(SetWorklistProgress(int))); connect(pThread, SIGNAL(UpdateSendFailed()), this, SLOT(UpdateSendFailed())); } void MainWindow::CompleteCleanStorage() { WRITE_FUNCTION_LOG(); if (m_pDialogStorageClean != NULL) { m_pDialogStorageClean->accept(); } // --- 첫 번째 블록: 스케줄에 따른 자동 삭제 --- { // 1. 이 블록만을 위한 지역 DB 인스턴스 생성 SUTIL::SDatabase db("AutoRemoveTask"); if (db.OpenDatabase("/home/birdhead/test.db") == 0) { // 2. 두 개의 수정 작업을 하나의 트랜잭션으로 묶어 원자성 보장 db.BeginTransaction(); bool success1 = db.AutoremoveImageTable(); // UpdateScheduled... 함수도 이전에 bool을 반환하도록 수정했어야 하나, // void라면 일단 호출만 합니다. db.UpdateScheduledDeleteCompleteStudyDate(); // AutoremoveImageTable의 성공 여부에 따라 Commit 결정 if (success1) { db.Commit(); } else { db.Rollback(); } } } // --- 두 번째 블록: 필요 시 추가 공간 확보 --- m_pCommonData->ComputeStorageSize(); int nStorageFreeSize = m_pCommonData->GetStorageFreeSize(); const int nRemainSize = 100000; // 100MB if (nStorageFreeSize < nRemainSize) { // 1. 이 블록만을 위한 별도의 지역 DB 인스턴스 생성 SUTIL::SDatabase db("FreeUpSpaceTask"); if (db.OpenDatabase("/home/birdhead/test.db") == 0) { // 2. 새로운 API를 사용하여 파일 목록을 값(QStringList)으로 직접 받음 QStringList videoFiles = db.GetVideoFileList(); // 3. 파일 삭제 및 DB 업데이트를 하나의 트랜잭션으로 처리 db.BeginTransaction(); bool bSuccess = true; for (const QString& strFileLocation : videoFiles) { bool bFileActuallyDeleted = false; if(strFileLocation.contains(MOVIE_FILE_EXTENSION)==true) { QString strTest = strFileLocation; QFileInfo fi(strTest); QString strPath = fi.path(); QString strFilename = fi.fileName(); strFilename = strFilename.replace(MOVIE_FILE_EXTENSION, ""); strFilename += "*"; QDirIterator itDir(strPath, {strFilename}, QDir::Files); while(itDir.hasNext()==true) { QString strFileDelete = itDir.next(); QFile fileRemove(strFileDelete); if(fileRemove.exists()==true) { fileRemove.remove(); bFileActuallyDeleted = true; } } } // ... (기존과 동일한 파일 시스템에서 파일 삭제 로직) ... // 이 로직이 성공적으로 파일을 삭제했다면, DB에 업데이트 if (bFileActuallyDeleted) { // SetDeleteStorageFile 함수도 bool을 반환하도록 수정하는 것이 좋음 if (!db.SetDeleteStorageFile(strFileLocation)) { bSuccess = false; break; // DB 업데이트 실패 시 중단 } } // 성능 참고: 이 부분은 여전히 비효율적입니다. // 루프 밖에서 한 번만 계산하거나, 10개 파일 처리 후 한 번씩만 계산하는 것이 좋습니다. m_pCommonData->ComputeStorageSize(); if (m_pCommonData->GetStorageFreeSize() >= nRemainSize) { break; // 목표 공간 확보 시 루프 탈출 } } if (bSuccess) { db.Commit(); } else { db.Rollback(); } } } } #include "sexception.h" void MainWindow::DeferredInit() { WRITE_FUNCTION_LOG(); m_Timer.stop(); disconnect(&m_Timer, SIGNAL(timeout()), this, SLOT(DeferredInit())); ChangeMenu(SMENU_WORKLIST); if (m_pCommonData->IsAutoLogin() == false) { DialogLogin dlgLogin(this); dlgLogin.exec(); } SThreadImageSave* pThreadImageSave = m_pCommonData->GetThreadCaptureImage(); // --- 첫 번째 블록 리팩토링 --- { // 1. 이 작업을 위한 지역 DB 인스턴스를 생성합니다. SUTIL::SDatabase db("DeferredInit_DeleteList_Connection"); if (db.OpenDatabase("/home/birdhead/test.db") == 0) // 0이 성공 { // 2. 새로운 API를 호출하여 QStringList 값을 직접 받습니다. QStringList dateList = db.GetScheduledDeleteStudyDateList(); // 3. 다른 스레드로 값을 전달합니다. // 참고: SetSchduledDeleteStudyDateList의 파라미터도 const QStringList&로 변경해야 합니다. pThreadImageSave->SetSchduledDeleteStudyDateList(dateList); } else { qWarning() << "DB open failed for GetScheduledDeleteStudyDateList in DeferredInit."; } // 'db'와 'dateList'는 스코프가 끝나면 자동으로 메모리가 정리됩니다. // DeleteListReponse 호출이 필요 없습니다. } usleep(10 * 1000); pThreadImageSave->ChangeState(0x100); m_pDialogStorageClean = new DialogProgress(this); m_pDialogStorageClean->Init(2); m_pDialogStorageClean->exec(); delete m_pDialogStorageClean; m_pDialogStorageClean = NULL; if (m_pCommonData->IsUseNetworkBackupVideo() == true) { // Samba 관련 로직 } // --- 두 번째 블록 리팩토링 --- { // 1. 별도의 작업을 위한 또 다른 지역 DB 인스턴스를 생성합니다. SUTIL::SDatabase db("DeferredInit_MergeTemp_Connection"); if (db.OpenDatabase("/home/birdhead/test.db") == 0) { // 2. 이 함수 하나가 데이터 병합과 임시 테이블 정리를 모두 수행합니다. db.AddAcquisitionTempToImage(); } else { qWarning() << "DB open failed for AddAcquisitionTempToImage in DeferredInit."; } // 'db'는 스코프가 끝나면 자동으로 소멸됩니다. } m_TimeCheckScreenSaver.start(); m_TimerScreenSaver.setInterval(1000 * 10); if (m_pCommonData->IsUseWebDisplay() == true) { m_pProcessNode = new QProcess(); m_pProcessNode->setWorkingDirectory("/home/birdhead/web_display"); m_pProcessNode->start("/usr/bin/node", QStringList("index.js")); } UmountDiskUSB("/home/birdhead/backup"); m_TimerCheckDiskUSB.start(2000); } void MainWindow::Exit() { WRITE_FUNCTION_LOG(); FormWorklist* pWorklist = (FormWorklist*)m_ListWidget[0]; FormHistory* pHistory = (FormHistory*)m_ListWidget[1]; FormCapture* pCapture = (FormCapture*)m_ListWidget[3]; m_pCommonData->GetThreadRTSP()->exitRTSP(); usleep(1000*1000); //pWorklist->Exit(); //pHistory->Exit(); m_pCommonData->Exit(); QApplication::quit(); } void MainWindow::ViewerChangeFocusCheck() { WRITE_FUNCTION_LOG(); QWidget* pFocusWidget = focusWidget(); if(pFocusWidget!=m_ListWidget[m_nCurrentWidgetID]) { usleep(10*1000); m_ListWidget[m_nCurrentWidgetID]->setFocus(Qt::ActiveWindowFocusReason); } else { m_ListWidget[m_nCurrentWidgetID]->update(); update(); disconnect(&m_Timer, SIGNAL(timeout()), this, SLOT(ViewerChangeFocusCheck())); m_Timer.stop(); } } void MainWindow::resizeEvent(QResizeEvent *event) { QSize nSize = size(); if(m_nDisplayType==SDISPLAY_1280X1024) { //ui->widgetMainMenu->setGeometry(45, 40, 462, 58); ui->widgetMainMenu->setGeometry(45, 40, 1190, 58); FormWorklist* pWorklist = (FormWorklist*)m_ListWidget[0]; pWorklist->setGeometry(45, 120, 1190, 864); FormHistory* pHistory = (FormHistory*)m_ListWidget[1]; pHistory->setGeometry(45, 120, 1190, 864); FormSetting* pSetting = (FormSetting*)m_ListWidget[2]; pSetting->setGeometry(45, 120, 1190, 864); FormCapture* pCapture = (FormCapture*)m_ListWidget[3]; pCapture->setGeometry(0, 0, 1280, 1024); FormViewer* pViewer = (FormViewer*)m_ListWidget[4]; pViewer->setGeometry(0, 0, 1280, 1024); m_pDialogMissCapture->setGeometry(0, 0, 1280, 1024); } else if(m_nDisplayType==SDISPLAY_1920X1080) { ui->widgetMainMenu->setGeometry(58, 58, 573, 70); FormWorklist* pWorklist = (FormWorklist*)m_ListWidget[0]; pWorklist->setGeometry(58, 166, 1808, 857); FormCapture* pCapture = (FormCapture*)m_ListWidget[3]; pCapture->setGeometry(0, 0, 1920, 1080); } } QWidget* MainWindow::GetFormCapture() { FormCapture* pCapture = (FormCapture*)m_ListWidget[3]; return pCapture; } void MainWindow::UpdateMakeDICOMInfo(int nCurrent, int nTotal) { WRITE_FUNCTION_LOG_PARAM(QString::number(nCurrent) + ", " +QString::number(nTotal)); if(m_nCurrentWidgetID==SWIDGET_VIEWER) { FormViewer* pFormViewer = (FormViewer*)m_ListWidget[m_nCurrentWidgetID]; pFormViewer->UpdateMakeDICOMInfo(nCurrent, nTotal); } else if(m_nCurrentWidgetID==SWIDGET_CAPTURE) { FormCapture* pFormCapture = (FormCapture*)m_ListWidget[m_nCurrentWidgetID]; pFormCapture->UpdateMakeDICOMInfo(nCurrent, nTotal); } } void MainWindow::UpdateSendInfo(int nCount, int nTotalCount) { WRITE_FUNCTION_LOG_PARAM(QString::number(nCount) + ", " + QString::number(nTotalCount)); if(m_nCurrentWidgetID==SWIDGET_VIEWER) { FormViewer* pFormViewer = (FormViewer*)m_ListWidget[m_nCurrentWidgetID]; pFormViewer->UpdateSendInfo(nCount, nTotalCount); } else if(m_nCurrentWidgetID==SWIDGET_CAPTURE) { FormCapture* pFormCapture = (FormCapture*)m_ListWidget[m_nCurrentWidgetID]; pFormCapture->UpdateSendInfo(nCount, nTotalCount); } /* switch(nID) { case SMENU_WORKLIST: m_nCurrentWidgetID = SWIDGET_WORKLIST; break; case SMENU_HISTORY: m_nCurrentWidgetID = SWIDGET_HISTORY; break; case SMENU_SETTING: m_nCurrentWidgetID = SWIDGET_SETTING; break; case SMENU_CAPTURE: m_nCurrentWidgetID = SWIDGET_CAPTURE; break; case SMENU_VIEWER: m_nCurrentWidgetID = SWIDGET_VIEWER; break; } */ } void MainWindow::UpdateSendComplete() { WRITE_FUNCTION_LOG(); if(m_nCurrentWidgetID==SWIDGET_VIEWER) { FormViewer* pFormViewer = (FormViewer*)m_ListWidget[m_nCurrentWidgetID]; pFormViewer->UpdateSendComplete(); } else if(m_nCurrentWidgetID==SWIDGET_CAPTURE) { FormCapture* pFormCapture = (FormCapture*)m_ListWidget[m_nCurrentWidgetID]; pFormCapture->UpdateSendComplete(); } } void MainWindow::UpdateSendFailed() { WRITE_FUNCTION_LOG(); if(m_nCurrentWidgetID==SWIDGET_VIEWER) { FormViewer* pFormViewer = (FormViewer*)m_ListWidget[m_nCurrentWidgetID]; pFormViewer->UpdateSendFailed(); } else if(m_nCurrentWidgetID==SWIDGET_CAPTURE) { FormCapture* pFormCapture = (FormCapture*)m_ListWidget[m_nCurrentWidgetID]; pFormCapture->UpdateSendFailed(); } } void MainWindow::UpdateAutoSendDICOMFailed() { } void MainWindow::ClearHistoryView() { WRITE_FUNCTION_LOG(); FormHistory* pForm = (FormHistory*)m_ListWidget[SWIDGET_HISTORY]; pForm->Init(); } int MainWindow::GetCurrentWidgetID() { return m_nCurrentWidgetID; } void MainWindow::DeleteUI() { WRITE_FUNCTION_LOG(); int i=0; for(i=0 ; iGetThreadRTSP()->ExitThread(); } void MainWindow::WatchPowerButton() { QSharedMemory shared("SmartQuadra"); if(shared.create(512, QSharedMemory::ReadWrite)==false) { //exit(0); bool bAttach = shared.attach(); if(bAttach==true) { int nSize = shared.size(); shared.lock(); { const char* pData1 = (const char*)shared.constData(); int nLength = strlen(pData1); if(nLength>0) { //Dialog Power Button DialogPowerButton dlg(this); //dlg.setGeometry(0, 0, 1280, 1280); dlg.exec(); char* pPipe = (char*)shared.data(); memset(pPipe, 0, 512); } } shared.unlock(); shared.detach(); } } } void MainWindow::keyReleaseEvent(QKeyEvent *event) { WRITE_FUNCTION_LOG(); int nKey = event->key(); SThreadImageSave* pThread = MainWindow::GetCommonData()->GetThreadCaptureImage(); if(nKey==Qt::Key_F10) { pThread->MissCapture(); } else if(nKey==Qt::Key_F11) { pThread->MissCapture(); } } void MainWindow::ViewDialogMissCapture() { m_TimerMissCapture.start(); if(m_pDialogMissCapture!=NULL) { m_pDialogMissCapture->show(); m_pDialogMissCapture->SetErrorType(0); } } void MainWindow::MissCapture() { //m_pDialogMissCapture->show(); m_pDialogMissCapture->exec(); m_TimerMissCapture.stop(); } void MainWindow::CompleteAcquisitionFromHistory() { } void MainWindow::LoadInfoAcquisitionFromHistory(int nCurrent, int nTotal) { } void MainWindow::ResetScreenSaverTimer() { m_TimeCheckScreenSaver.restart(); } void MainWindow::StartScreenSaverTimer() { m_TimerScreenSaver.start(); } void MainWindow::StopScreenSaverTimer() { m_TimerScreenSaver.stop(); } void MainWindow::CaptureHandSwitch() { WRITE_FUNCTION_LOG(); if(m_nCurrentWidgetID==SWIDGET_CAPTURE) { SendCaptureHandSwitch(); } else { SThreadImageSave* pThread = MainWindow::GetCommonData()->GetThreadCaptureImage(); pThread->MissCapture(); } } void MainWindow::CaptureFootSwitch() { WRITE_FUNCTION_LOG(); if(m_nCurrentWidgetID==SWIDGET_CAPTURE) { SendCaptureFootSwitch(); } else { SThreadImageSave* pThread = MainWindow::GetCommonData()->GetThreadCaptureImage(); pThread->MissCapture(); } } void MainWindow::WriteLogTimer() { if(m_pCommonData->GetLogLevel()==0) { return; } QStringList* pListLog = m_pCommonData->GetListLog(); QString strLog = QString::fromStdString(pListLog->join("").toStdString()); pListLog->clear(); m_pCommonData->ReleaseListLog(); if(strLog.size()>0) { m_pCommonData->WriteLogFile(strLog); } } void MainWindow::SetDisplayError(int nTypeError) { m_TimerMissCapture.start(); if(m_pDialogMissCapture!=NULL) { m_pDialogMissCapture->show(); m_pDialogMissCapture->SetErrorType(nTypeError); } } void MainWindow::SetDisplayError(QString strError) { CommonData::RestartNetworkManager(); m_TimerMissCapture.start(); if(m_pDialogMissCapture!=NULL) { m_pDialogMissCapture->show(); m_pDialogMissCapture->SetErrorString(strError); } } void MainWindow::CheckDiskUSB() { bool bExists = false; QString strDevice; #ifdef _PC strDevice = "/dev/sdb1"; #else strDevice = "/dev/sda1"; #endif QFileInfo fi(strDevice); bExists = fi.exists(); if(ui->widgetMainMenu->isVisible()==false) { return; } if(bExists==true) { if(m_bMountDiskUSB==false) { bool bMount = false; QProcess processCheckMount; processCheckMount.start("mount"); while(processCheckMount.waitForStarted(100)==false) { usleep(1000); } bool retval = false; QByteArray buffer; while ((retval = processCheckMount.waitForFinished(100))) { usleep(1000); } buffer.append(processCheckMount.readAll()); processCheckMount.terminate(); processCheckMount.waitForFinished(100); processCheckMount.deleteLater(); QString strDeviceMount; QString strLocationMount; QString strBufferMount = buffer.toStdString().c_str(); QStringList listMount = strBufferMount.split("\n"); if(strBufferMount.contains(strDevice)==true) { int i=0; for(i=0 ; i=6) { for(j=2 ; jwidgetMainMenu->SetEnableDiskUSB(true); } } else { if(m_bMountDiskUSB==true) { UmountDiskUSB("/home/birdhead/backup"); } ui->widgetMainMenu->SetEnableDiskUSB(false); m_bMountDiskUSB = false; } } void MainWindow::MountDiskUSB(QString strDeviceMount, QString strLocationMount) { QString strCommand; strCommand = QString("sudo -S mount %1 %2").arg(strDeviceMount).arg(strLocationMount); CommandSudo(strCommand); } void MainWindow::UmountDiskUSB(QString strLocationMount) { QString strCommand; strCommand = QString("sudo -S umount %1").arg(strLocationMount); CommandSudo(strCommand); } void MainWindow::CommandSudo(QString strCommand) { bool retval = false; QByteArray buffer; QProcess process1; QProcess process2; process1.setStandardOutputProcess(&process2); process1.start("echo 1"); process2.start(strCommand); process2.setProcessChannelMode(QProcess::ForwardedChannels); // Wait for it to start if(!process1.waitForStarted()) return; // To be fair: you only need to wait here for a bit with shutdown, // but I will still leave the rest here for a generic solution while ((retval = process2.waitForFinished())) { buffer.append(process2.readAll()); } process1.terminate(); process2.terminate(); process1.waitForFinished(100); process2.waitForFinished(100); process1.deleteLater(); process2.deleteLater(); buffer.clear(); }