#include "BaseProcess.h"
#include "Worker.h"
#include "ECTrainer.h"
#include "EyeTrack.h"
#include "Stimulus.h"
#include "BitalMonitor.h"
#include "Marker.h"
#include "MovieObject.h"
#include "myWinUtils.h"
// コンストラクタ
Worker::Worker(ECTrainer* pEct) : BaseProcess(pEct)
, _AppStatus(APP_STATUS::BOOT)
, _fpLogData(NULL)
, _fpLogEvent(NULL)
, _pExpTimer(NULL)
, _pContactTimer(NULL)
, _TargetImage(ECTrainer::RINGBUFSIZE)
{
_pExpTimer = new mwut::HPTimer();
_pContactTimer = new mwut::HPTimer();
}
// デストラクタ
Worker::~Worker() {
if (_fpLogData) fclose(_fpLogData);
if (_fpLogEvent) fclose(_fpLogEvent);
mwut::SafeDelete((void**)&_pExpTimer);
mwut::SafeDelete((void**)&_pContactTimer);
}
// 初期化
bool Worker::Init() {
if (_tfopen_s(&_fpLogData, DATA_LOG_FILE, _T("w")) != 0) {
_pEct->MsgBox(_T("Can't open data log file."), MB_ICONERROR);
return false;
}
_ftprintf(_fpLogData, _T("time,stimNo,stimTime,gazeVx,gazeVy,gazeIx,gazeIy,target,contact time,Feedback,RR,"
"H11,H12,H13,H21,H22,H23,H31,H32,H33\n"));
if (_tfopen_s(&_fpLogEvent, EVENT_LOG_FILE, _T("w")) != 0) {
_pEct->MsgBox(_T("Can't open event log file."), MB_ICONERROR);
return false;
}
_AppStatus = APP_STATUS::IDLE;
return true;
}
// 基本処理
bool Worker::Routine() {
// 視線情報更新を待つ
//Sleep(0);
if (!_pEct->PEyeTrack()->IsNewGazeV()) return true;
cv::Point2f gazeV = (_pEct->PEyeTrack()->GetGazeV());
// 注視点を画像座標に変換
auto gazeI = gazeV.x < 0 ? cv::Point2f(-1, -1) : _pEct->PMarker()->ConvV2I(gazeV);
// ターゲット画像生成
int hit = -1;
double dtMin = 0;
int stimNo = _pEct->PStimulus()->GetStimNo();
int fb = 0;
std::vector<Element> elems = _pEct->PStimulus()->GetMovieObject();
if (stimNo >= 0 && elems.size() > 0) {
cv::Mat stimImg = _pEct->PStimulus()->GetDisplay().clone();
for (int e = 0; e < elems.size(); e++) {
// ターゲット描画
auto target = cv::Point2f(elems[e].x, elems[e].y);
cv::circle(stimImg, target, (int)elems[e].d, CV_RGB(255, 0, 0), 2);
if (gazeI.x >= 0) {
// ターゲット判定
auto dt = cv::norm(gazeI - target);
if (dt < elems[e].d && (hit < 0 || dt < dtMin)) {
hit = e;
dtMin = dt;
}
}
}
if (gazeI.x >= 0) {
// 画像上の注視点描画
cv::circle(stimImg, gazeI, 20, CV_RGB(0, 0, 255), 3);
if (hit < 0) {
_pContactTimer->Reset(); // 視線取得時&ターゲット判定時にコンタクト時間リセット
} else {
if (_pContactTimer->Elapse() > 3000.) {
fb = 1;
this->PostMsg(ECTMSG::FB_GOOD);
_pContactTimer->Reset();
}
}
}
_TargetImage.Put(stimImg);
} else {
_pContactTimer->Reset(); // 非実験時&ターゲット非設定時にコンタクト時間リセット
}
// ログ出力
if (stimNo >= 0) {
_ftprintf(_fpLogData, _T("%.3f"), _pExpTimer->Elapse() / 1000.); // 経過時間
_ftprintf(_fpLogData, _T(",%d"), stimNo); // 刺激データ番号
_ftprintf(_fpLogData, _T(",%.3f"), _pEct->PStimulus()->GetStimTime() / 1000.); // 刺激提示の経過時間
_ftprintf(_fpLogData, _T(",%.1f,%.1f"), gazeV.x, gazeV.y); // 注視点(視野カメラ座標)
_ftprintf(_fpLogData, _T(",%.1f,%.1f"), gazeI.x, gazeI.y); // 注視点(画像座標)
_ftprintf(_fpLogData, _T(",%d"), hit + 1); // ターゲット判定
_ftprintf(_fpLogData, _T(",%.2f"), hit < 0 ? 0 : _pContactTimer->Elapse()/1000.); // 目標コンタクト時間
_ftprintf(_fpLogData, _T(",%d"), fb); // フィードバック
_ftprintf(_fpLogData, _T(",%d"), _pEct->PBitalMonitor()->GetRR()); // バイタル出力(RR間隔)
// ホモグラフィ行列出力
cv::Mat h = _pEct->PMarker()->GetHomography();
if (!h.empty() && h.rows == 3 && h.cols == 3) {
double* ptr = h.ptr<double>(0);
for (int i = 0; i < 9; i++) {
_ftprintf(_fpLogData, _T(",%lf"), *(ptr + i));
}
}
_ftprintf(_fpLogData, _T("\n"));
}
return true;
}
// イベント処理
bool Worker::EventProc(MSG& msg) {
switch (msg.message) {
case (int)ECTMSG::SOFTWARE_START: // ソフトウェア起動
this->EventLog(_T("Software Start"));
break;
case (int)ECTMSG::SOFTWARE_END: // ソフトウェア終了
this->EventLog(_T("Software End"));
_pEct->StopApp();
break;
case (int)ECTMSG::CALIB_START: // キャリブレーション開始
if (_AppStatus == APP_STATUS::IDLE) {
_AppStatus = APP_STATUS::CALIB;
((BaseProcess*)_pEct->PStimulus())->PostMsg(ECTMSG::CALIB_START);
((BaseProcess*)_pEct->PTobiiREST())->PostMsg(ECTMSG::CALIB_START);
this->EventLog(_T("Calibration Start"));
}
break;
case (int)ECTMSG::CALIB_OK: // キャリブレーション成功
_AppStatus = APP_STATUS::IDLE;
((BaseProcess*)_pEct->PStimulus())->PostMsg(ECTMSG::CALIB_OK);
this->EventLog(_T("Calibration Success"));
break;
case (int)ECTMSG::CALIB_FAILED: // キャリブレーション失敗
case (int)ECTMSG::CALIB_ERR: // キャリブレーションエラー
_AppStatus = APP_STATUS::IDLE;
((BaseProcess*)_pEct->PStimulus())->PostMsg(ECTMSG::CALIB_FAILED);
this->EventLog(_T("Calibration Failed"));
break;
case (int)ECTMSG::EXP_START: // 実験開始
if (_AppStatus == APP_STATUS::IDLE) {
_AppStatus = APP_STATUS::STIM;
((BaseProcess*)_pEct->PStimulus())->PostMsg(ECTMSG::EXP_START);
this->EventLog(_T("Experiment Start"));
_pExpTimer->Reset();
_pContactTimer->Reset();
}
break;
case (int)ECTMSG::EXP_STOP: // 実験停止
if (_AppStatus == APP_STATUS::STIM) {
_AppStatus = APP_STATUS::IDLE;
((BaseProcess*)_pEct->PStimulus())->PostMsg(ECTMSG::EXP_STOP);
this->EventLog(_T("Experiment Stopped"));
}
break;
case (int)ECTMSG::EXP_END: // 実験終了
if (_AppStatus == APP_STATUS::STIM) {
_AppStatus = APP_STATUS::IDLE;
((BaseProcess*)_pEct->PStimulus())->PostMsg(ECTMSG::EXP_STOP);
this->EventLog(_T("Experiment Finished"));
}
break;
case (int)ECTMSG::EXP_NEXT: // 次の刺激に移動
this->EventLog(_T("Next stimulation"));
break;
case (int)ECTMSG::MOVIE_START: // 動画再生開始
this->EventLog(_T("Movie Start"));
break;
case (int)ECTMSG::MOVIE_STOP: // 動画再生停止
this->EventLog(_T("Movie Stopped"));
break;
case (int)ECTMSG::MOVIE_END: // 動画再生終了
this->EventLog(_T("Movie End"));
((BaseProcess*)_pEct->PStimulus())->PostMsg(ECTMSG::MOVIE_END);
break;
case (int)ECTMSG::MOVIE_ERROR: // 動画エラー
this->EventLog(_T("Movie Error"));
break;
case (int)ECTMSG::FB_OK: // フィードバック:OK
PlaySound(SOUND_OK, NULL, SND_FILENAME | SND_ASYNC);
this->EventLog(_T("Feedback OK"));
break;
case (int)ECTMSG::FB_GOOD: // フィードバック:Good
PlaySound(SOUND_GOOD, NULL, SND_FILENAME | SND_ASYNC);
this->EventLog(_T("Feedback Good"));
break;
case (int)ECTMSG::FB_NICE: // フィードバック:Nice
PlaySound(SOUND_NICE, NULL, SND_FILENAME | SND_ASYNC);
this->EventLog(_T("Feedback Nice"));
break;
case (int)ECTMSG::FB_GREAT: // フィードバック:Great
PlaySound(SOUND_GREAT, NULL, SND_FILENAME | SND_ASYNC);
this->EventLog(_T("Feedback Great"));
break;
case (int)ECTMSG::FB_EXCELLENT: // フィードバック:Excellent
PlaySound(SOUND_EXCELLENT, NULL, SND_FILENAME | SND_ASYNC);
this->EventLog(_T("Feedback Excellent"));
break;
}
return true;
}
// イベントログ出力
void Worker::EventLog(const TCHAR* msg) {
std::wstring datestr = mwut::Multi2Wide(mwut::DateTimeStr());
_ftprintf(_fpLogEvent, _T("%s %s\n"), datestr.c_str(), msg);
mwut::DebugPrintf(_T("%s %s\n"), datestr.c_str(), msg);
}