File LayerLogRecorder.cpp¶
File List > AIAC > LayerLogRecorder.cpp
Go to the documentation of this file
// #####################################################################
// >>>>>>>>>>>>>>>>>>>>> BEGINNING OF LEGAL NOTICE >>>>>>>>>>>>>>>>>>>>>
//######################################################################
//
// This source file, along with its associated content, was authored by
// Andrea Settimi, Hong-Bin Yang, Naravich Chutisilp, and numerous other
// contributors. The code was originally developed at the Laboratory for
// Timber Construction (IBOIS, director: Prof. Yves Weinand) at the School of
// Architecture, Civil and Environmental Engineering (ENAC) at the Swiss
// Federal Institute of Technology in Lausanne (EPFL) for the Doctoral
// Research "Augmented Carpentry" (PhD researcher: Andrea Settimi,
// co-director: Dr. Julien Gamerro, director: Prof. Yves Weinand).
//
// Although the entire repository is distributed under the GPL license,
// these particular source files may also be used under the terms of the
// MIT license. By accessing or using this file, you agree to the following:
//
// 1. You may reproduce, modify, and distribute this file in accordance
// with the terms of the MIT license.
// 2. You must retain this legal notice in all copies or substantial
// portions of this file.
// 3. This file is provided "AS IS," without any express or implied
// warranties, including but not limited to the implied warranties of
// merchantability and fitness for a particular purpose.
//
// If you cannot or will not comply with the above conditions, you are
// not permitted to use this file. By proceeding, you acknowledge and
// accept all terms and conditions herein.
//
//######################################################################
// <<<<<<<<<<<<<<<<<<<<<<< END OF LEGAL NOTICE <<<<<<<<<<<<<<<<<<<<<<<<
// #####################################################################
//
#include "LayerLogRecorder.h"
#include "Application.h"
#include "utils/MatrixUtils.h"
#include "utils/utils.h"
#include "utils/SystemUtils.h"
#include "LayerUtils.h"
#include <utility>
void AIAC::LayerLogRecorder::OnAttach() {
#ifdef HEADLESS_TEST
StartRecording();
#endif
}
void AIAC::LayerLogRecorder::OnFrameStart() {
if (!m_IsRecording || m_IsPaused) return;
bool isTracked = AIAC_APP.GetLayer<AIAC::LayerSlam>()->IsTracked();
m_LogFile << "#" << m_FrameCount << " " << GetCurrentTimestamp() << std::endl;
if (isTracked) LogSlamStatus();
LogTToolHead();
if (isTracked) LogTToolPose();
LogACIM();
m_FrameCount++;
}
void AIAC::LayerLogRecorder::StartRecording(std::string logRootFolderPath) {
if (m_IsRecording) {
AIAC_WARN("Already recording, stop the current recording first.");
return;
}
// The default log file path will be './temp/log_recorders/<logName>/log.txt'
// Where <logName> is the <acim_model_name>_<current_date_time>
std::string acimModelName = GetFileNameFromPath(AIAC_APP.GetLayer<AIAC::LayerModel>()->GetACInfoModelPath(), false);
std::string currentDateTime = GetCurrentDateTime();
#ifdef HEADLESS_TEST
const std::string logName = acimModelName;
#else
std::string logName = acimModelName + "_" + currentDateTime;
#endif
if (logRootFolderPath.empty()) {
logRootFolderPath = AIAC_APP.GetLayer<AIAC::LayerUtils>()->GetSaveFolderPath();
if (logRootFolderPath.empty()) {
logRootFolderPath = "./temp/";
}
if (logRootFolderPath.back() != '/') {
logRootFolderPath += "/";
}
logRootFolderPath += "log_recorders/";
}
if (logRootFolderPath.back() != '/') {
logRootFolderPath += "/";
}
m_LogFolderPath = logRootFolderPath + logName;
// create the directory if not exist
if (!std::filesystem::exists(m_LogFolderPath)) {
std::filesystem::create_directories(m_LogFolderPath);
}
// copy the dependency files to the log folder
std::string acimModelPath = AIAC_APP.GetLayer<AIAC::LayerModel>()->GetACInfoModelPath();
std::string scannedModelPath = AIAC_APP.GetLayer<AIAC::LayerModel>()->GetScannedModelPath();
std::string ttoolModelPath = AIAC::Config::Get<std::string>(
AIAC::Config::SEC_TTOOL, AIAC::Config::CONFIG_FILE, "deps/TTool/assets/config.yml");
CopyFile(acimModelPath, m_LogFolderPath + "/AC_info_model.acim");
CopyFile(scannedModelPath, m_LogFolderPath + "/scanned_model.ply");
// start recording
m_LogFilePath = m_LogFolderPath + "/log.txt";
AIAC_INFO("Start recording log to: {}", m_LogFilePath);
m_IsRecording = true;
m_LogFile.open(m_LogFilePath, std::ios::out);
m_FrameCount = 0;
LogHeader();
m_LogFile << "[Content]" << std::endl;
}
void AIAC::LayerLogRecorder::StopRecording() {
if (!m_IsRecording) return;
m_IsRecording = false;
m_LogFile << "[End]" << std::endl;
m_LogFile.close();
AIAC_INFO("Stop recording log to: {}", m_LogFilePath);
// compress to a zip file
AIAC_INFO("Compressing the log folder...");
std::string zipPath = m_LogFolderPath + ".zip";
std::string cmd = "zip -r -j " + zipPath + " " + m_LogFolderPath;
AIAC_INFO("Run zip Command: {}", cmd);
ExecuteSystemCommand(cmd.c_str());
AIAC_INFO("Compressed log folder to: {}", zipPath);
// remove the original log folder
AIAC_INFO("Removing the log folder...");
cmd = "rm -rf " + m_LogFolderPath;
ExecuteSystemCommand(cmd.c_str());
AIAC_INFO("Done!");
}
void AIAC::LayerLogRecorder::PauseRecording() {
m_IsPaused = true;
m_LogFile << "Pause" << std::endl;
}
void AIAC::LayerLogRecorder::ResumeRecording() {
m_IsPaused = false;
InitACIMStatus();
InitTToolStatus();
}
void AIAC::LayerLogRecorder::LogHeader() {
// get the latest version of the TTool files on Zenodo
std::string cmd = "curl -Ls -o /dev/null -w %{url_effective} https://zenodo.org/doi/10.5281/zenodo.7956930";
std::string ttoolZenodoVersion = ExecuteSystemCommand(cmd.c_str());
m_LogFile << "[Header]" << std::endl;
m_LogFile << "Created Time: " << GetCurrentTimestamp() << std::endl;
m_LogFile << "AC Info Model path: " << AIAC_APP.GetLayer<AIAC::LayerModel>()->GetACInfoModelPath() << std::endl;
m_LogFile << "Scanned Model path: " << AIAC_APP.GetLayer<AIAC::LayerModel>()->GetScannedModelPath() << std::endl;
m_LogFile << "TTool Zenodo Version: " << ttoolZenodoVersion << std::endl;
m_LogFile << std::endl;
m_LogFile << "[Legend]" << std::endl;
m_LogFile << "SLAM <t.x> <t.y> <t.z> <q.x> <q.y> <q.z> <q.w> // SLAM Camera Pose" << std::endl;
m_LogFile << "TTool-head <toolhead_name> // TTool Head Changed" << std::endl;
m_LogFile << "TTool-pose <status> <t.x> <t.y> <t.z> <q.x> <q.y> <q.z> <q.w> // TTool Pose" << std::endl;
m_LogFile << "ACIM-activate-component <component_id> // ACIM Activated Component" << std::endl;
m_LogFile << "ACIM-component-status <component_id> <status> // ACIM Component Status" << std::endl;
m_LogFile << "ACIM-transform <t.x> <t.y> <t.z> <q.x> <q.y> <q.z> <q.w> // ACIM Transformation" << std::endl;
m_LogFile << "Pause // Paused by user" << std::endl;
m_LogFile << std::endl;
m_LogFile << "[Init]" << std::endl;
InitACIMStatus();
InitTToolStatus();
m_LogFile << std::endl;
}
void AIAC::LayerLogRecorder::LogSlamStatus() {
bool isTracked = AIAC_APP.GetLayer<AIAC::LayerSlam>()->IsTracked();
if (!isTracked) {
return;
}
// get pose
cv::Vec4f quaternion;
cv::Vec3f tvec;
AIAC_APP.GetLayer<AIAC::LayerSlam>()->GetCamPoseQuaternionAndTvec(quaternion, tvec);
// pose[x, y, z] = (0, 0, 0) and quaternion[x, y, z, w]
m_LogFile << "SLAM " << tvec[0] << " " << tvec[1] << " " << tvec[2] << " "
<< quaternion[0] << " " << quaternion[1] << " " << quaternion[2] << " " << quaternion[3] << endl;
}
void AIAC::LayerLogRecorder::LogTToolHead() {
std::string toolheadName = AIAC_APP.GetLayer<AIAC::LayerToolhead>()->ACInfoToolheadManager->GetActiveToolhead()->GetName();
if (m_TToolPreviousToolheadName.empty() || (!toolheadName.empty() && toolheadName != m_TToolPreviousToolheadName)) {
m_LogFile << "TTool-head " << toolheadName << endl;
m_TToolPreviousToolheadName = toolheadName;
}
}
void AIAC::LayerLogRecorder::LogTToolPose() {
// position
std::string status;
auto ttoolState = AIAC_APP.GetLayer<AIAC::LayerToolhead>()->GetTtoolState();
switch (ttoolState) {
case ttool::EventType::Tracking:
status = "Tracking";
break;
case ttool::EventType::PoseInput:
status = "PoseInput";
break;
default:
status = "None";
break;
}
LogTToolTransformation(status);
}
void AIAC::LayerLogRecorder::InitTToolStatus() {
m_TToolPreviousToolheadName = AIAC_APP.GetLayer<AIAC::LayerToolhead>()->ACInfoToolheadManager->GetActiveToolhead()->GetName();
m_LogFile << "TTool-head " << m_TToolPreviousToolheadName << endl;
LogTToolPose();
}
void AIAC::LayerLogRecorder::LogTToolTransformation(const std::string& status){
// Get the relative pose to the camera
cv::Mat ttoolPose(4, 4, CV_32F);
cv::Vec3f tvec;
cv::Vec4f qvec;
auto ttoolPoseGlm = AIAC_APP.GetLayer<AIAC::LayerToolhead>()->GetWorldPose();
CvtGlmMat2CvMat(ttoolPoseGlm, ttoolPose);
ConvertTransMatToTvecAndQvec(ttoolPose, tvec, qvec);
m_LogFile << "TTool-pose " << status << " " << tvec[0] << " " << tvec[1] << " " << tvec[2] << " "
<< qvec[0] << " " << qvec[1] << " " << qvec[2] << " " << qvec[3] << endl;
}
void AIAC::LayerLogRecorder::LogACIM() {
// if activated component changed
std::string componentID = AIAC_APP.GetLayer<AIAC::LayerModel>()->GetACInfoModel().GetTimberInfo().GetCurrentComponentID();
if (m_ACIMPreviousActivatedComponentID.empty() || (!componentID.empty() && componentID != m_ACIMPreviousActivatedComponentID)) {
m_LogFile << "ACIM-activate-component " << componentID << endl;
m_ACIMPreviousActivatedComponentID = componentID;
m_IsActivatedComponentDone = AIAC_APP.GetLayer<AIAC::LayerModel>()->GetACInfoModel().GetTimberInfo().GetComponent(componentID)->IsMarkedDone;
}
// if activated is activated done / not done
bool isActivatedComponentDone = AIAC_APP.GetLayer<AIAC::LayerModel>()->GetACInfoModel().GetTimberInfo().GetCurrentComponent()->IsMarkedDone;
if (m_IsActivatedComponentDone != isActivatedComponentDone) {
m_LogFile << "ACIM-component-status " << AIAC_APP.GetLayer<AIAC::LayerModel>()->GetACInfoModel().GetTimberInfo().GetCurrentComponentID() << " "
<< (isActivatedComponentDone ? "Done" : "NotDone") << endl;
m_IsActivatedComponentDone = isActivatedComponentDone;
}
// if transformation changed
float offset = AIAC_APP.GetLayer<AIAC::LayerModel>()->GetAlignOffset();
int rotation = AIAC_APP.GetLayer<AIAC::LayerModel>()->GetAlignRotation();
bool flip = AIAC_APP.GetLayer<AIAC::LayerModel>()->GetAlignFlip();
if (m_ACIMOffset != offset || m_ACIMRotation != rotation || m_ACIMFlip != flip) {
m_ACIMOffset = offset;
m_ACIMRotation = rotation;
m_ACIMFlip = flip;
LogACIMTransformation();
}
}
void AIAC::LayerLogRecorder::InitACIMStatus() {
// init variables storing the state
m_ACIMComponentStatus.clear();
m_ACIMPreviousActivatedComponentID = AIAC_APP.GetLayer<AIAC::LayerModel>()->GetACInfoModel().GetTimberInfo().GetCurrentComponentID();
m_LogFile << "ACIM-activate-component " << m_ACIMPreviousActivatedComponentID << endl;
auto allComponentIDs = AIAC_APP.GetLayer<AIAC::LayerModel>()->GetACInfoModel().GetTimberInfo().GetAllComponentsIDs();
for (const auto &componentID : allComponentIDs) {
bool isDone = AIAC_APP.GetLayer<AIAC::LayerModel>()->GetACInfoModel().GetTimberInfo().GetComponent(componentID)->IsMarkedDone;
m_ACIMComponentStatus[componentID] = isDone;
m_LogFile << "ACIM-component-status " << componentID << " " << (isDone ? "Done" : "NotDone") << endl;
}
m_ACIMOffset = AIAC_APP.GetLayer<AIAC::LayerModel>()->GetAlignOffset();
m_ACIMRotation = AIAC_APP.GetLayer<AIAC::LayerModel>()->GetAlignRotation();
m_ACIMFlip = AIAC_APP.GetLayer<AIAC::LayerModel>()->GetAlignFlip();
LogACIMTransformation();
}
void AIAC::LayerLogRecorder::LogACIMTransformation() {
glm::mat4x4 transformMat = AIAC_APP.GetLayer<AIAC::LayerModel>()->GetTransformMat();
cv::Mat transformMatCv;
CvtGlmMat2CvMat(transformMat, transformMatCv);
cv::Vec3f tvec;
cv::Vec4f qvec;
ConvertTransMatToTvecAndQvec(transformMatCv, tvec, qvec);
m_LogFile << "ACIM-transform " << tvec[0] << " " << tvec[1] << " " << tvec[2] << " "
<< qvec[0] << " " << qvec[1] << " " << qvec[2] << " " << qvec[3] << endl;
}