Kaynağa Gözat

1.为TiRay探测器增加DDR模式
2.修改存图逻辑,确保可以连续上图

lwk 1 ay önce
ebeveyn
işleme
2f77f65bc7

+ 47 - 0
Detector/TiRay/CCOS.Dev.FPD.TiRayDR/CCOS.Dev.FPD.TiRayDR.cpp

@@ -721,6 +721,11 @@ RET_STATUS nsFPD::FPDDeviceTiRay::SetAcqMode(string strMode)
 	m_DetectorCtrlUnit->SetDetectorStatus(to_string(DETECTOR_STATUS_WAKEUP));
 
 	int nMode = AcqMode::RAD;
+	bool bRet = GetLogicMode(strMode, nMode);
+	if (!bRet)
+	{
+		return RET_STATUS::RET_FAILED;
+	}
 
 	try
 	{
@@ -772,6 +777,48 @@ RET_STATUS nsFPD::FPDDeviceTiRay::SetAcqMode(string strMode)
 	return ret;
 }
 
+bool nsFPD::FPDDeviceTiRay::GetLogicMode(string& strAcqMode, int& nLogicMode)
+{
+	if (strAcqMode == "RAD")
+	{
+		nLogicMode = RAD;
+	}
+	else if (strAcqMode == "DualEnergy")
+	{
+		nLogicMode = DUAL_ENERGY;
+	}
+	else if (strAcqMode == "DDR"||strAcqMode == "TOMO")
+	{
+		nLogicMode = DDR;
+	}
+	else if (strAcqMode == "PF")
+	{
+		nLogicMode = PF;
+	}
+	else if (strAcqMode == "1")
+	{
+		nLogicMode = RAD;
+	}
+	else if (strAcqMode == "2")
+	{
+		nLogicMode = DUAL_ENERGY;
+	}
+	else if (strAcqMode == "3")
+	{
+		nLogicMode = DDR;
+	}
+	else if (strAcqMode == "4")
+	{
+		nLogicMode = PF;
+	}
+	else
+	{
+		FERROR("Not support mode!");
+		return false;
+	}
+	return true;
+}
+
 RET_STATUS nsFPD::FPDDeviceTiRay::GetSyncMode(SYNC_MODE& eSyncMode)
 {
 	FINFO("--Func Device-- GetSyncMode Start");

+ 1 - 0
Detector/TiRay/CCOS.Dev.FPD.TiRayDR/CCOS.Dev.FPD.TiRayDR.h

@@ -125,6 +125,7 @@ namespace CCOS::Dev::Detail::Detector
 		void OnFPDCallback(int nDetectorID, int nEventID, int nEventLevel, const char* pszMsg, int nParam1, float fParam2, int nPtrParamLen, void* pParam);
 		bool Support_DarkCalib();
 		bool Support_XrayCalib();
+		bool GetLogicMode(string& strAcqMode, int& nLogicMode);
 
 	private:
 		void SendTemperatureValue(float fValue);

+ 3 - 1
Detector/TiRay/CCOS.Dev.FPD.TiRayDR/DetectorCtrlDefinition.h

@@ -9,7 +9,9 @@ enum AcqMode
 {
 	RAD = 1,
 	DUAL_ENERGY,
-	DDR
+	DDR,
+	PF,
+	CF
 };
 
 //̽²âÆ÷Ò첽ʼþ״̬

+ 270 - 128
Detector/TiRay/CCOS.Dev.FPD.TiRayDR/Detector_TiRayDR.cpp

@@ -1,13 +1,16 @@
 #include "Detector_TiRayDR.h"
 #include "CCOS.Dev.FPD.TiRayDR.h"
 #include "MyPingip.h"
-#include <dlfcn.h> 
+#include <dlfcn.h>
 #include <pthread.h>
-#include "LogLocalHelper.h"       
+#include "LogLocalHelper.h"
 #include "Log4CPP.h"
 using EventListenerType = void(*)(TiRayEvent, void*);
 Detector_TiRayDR* g_pDetector = nullptr;
 
+// 函数声明
+static std::string generateReadableTimestamp();
+
 //extern Log4CPP::Logger* mLog::gLogger;
 
 /****************************************/
@@ -80,6 +83,8 @@ Detector_TiRayDR::Detector_TiRayDR()
 	m_pZSKKCalib{},
 	m_strDetectorType{},
 	m_strSerialNum{},
+	m_strCurrentSessionTimestamp{},
+	m_nSessionFrameCounter{0},
 	m_nImageNum{},
 	m_nDetectorID{},
 	m_nNotifyStatusTimePeriod{},
@@ -331,79 +336,95 @@ void Detector_TiRayDR::EnterExamMode(int nExamMode)
 bool Detector_TiRayDR::SetAcqMode(int nMode)
 {
 	FINFO("--TiRayDR Func-- SetAcqMode Start");
-	FINFO("Detector_TiRayDR::SetAcqMode mode:{$}", nMode);
+	FINFO("SetAcqMode mode: {$}", nMode);
+
 	if (!m_pStPanelStatus[m_nCurrentPanelID]->bConnectState)
 	{
 		FERROR("Detector not connected, return");
 		return false;
 	}
 
-	int nRes;
 	m_bExitRadAcqStatus = false;
 
+	// 从配置中读取图像参数
 	try
 	{
-		//设置采集模式,根据不同的场景设置不同的采集模式
 		m_nImageWidth = (int)m_ModeConfig["ModeTable"][0]["ImageWidth"];
 		m_nImageHeight = (int)m_ModeConfig["ModeTable"][0]["ImageHeight"];
 		m_nWidthOffset = (int)m_ModeConfig["ModeTable"][0]["WidthOffset"];
 		m_nHeightOffset = (int)m_ModeConfig["ModeTable"][0]["HeightOffset"];
-		FINFO("After crop image width: {$}, height: {$}, WidthOffset: {$}, HeightOffset: {$}", m_nImageWidth, m_nImageHeight, m_nWidthOffset, m_nHeightOffset);
+		FINFO("Image config - Width: {$}, Height: {$}, WidthOffset: {$}, HeightOffset: {$}",
+			m_nImageWidth, m_nImageHeight, m_nWidthOffset, m_nHeightOffset);
 
 		m_bSaveRaw = (int)m_ModeConfig["ModeTable"][0]["IsSaveRaw"];
-		FINFO("m_nSaveRaw:{$}", m_bSaveRaw);
+		FINFO("SaveRaw: {$}", m_bSaveRaw);
 
-		if (nullptr != m_pImgBuffer)
+		// 重新分配图像缓冲区
+		if (m_pImgBuffer != nullptr)
 		{
 			delete[] m_pImgBuffer;
 			m_pImgBuffer = nullptr;
 		}
 		m_pImgBuffer = new WORD[(size_t)m_nImageWidth * (size_t)m_nImageHeight];
-
 	}
 	catch (ResDataObjectExption& e)
 	{
-		FERROR("Get config FERROR: {$}", e.what());
+		FERROR("Failed to get config: {$}", e.what());
 		return false;
 	}
 
-	TiRayVariant Param[2]{};
-	Param[0].Type = TiRayVariant::TiRayInt;
-	Param[0].IntValue = TiRayAttribute::Attr_BinningMode;
-	Param[1].Type = TiRayVariant::TiRayInt;
-	Param[1].IntValue = BinningMode::Binning_None;
+	// 根据采集模式设置工作模式
+	int workMode;
+	const char* modeName;
 
-	nRes = Execute_Ptr(m_nDetectorID, Cmd_WriteAttribute, Param, 2);
-	if ((TiRayError)nRes != TiRayError::Err_Success)
+	// 设置拍摄参数
+	int nXWindow = (int)m_ModeConfig["ModeTable"][0]["XWindow"];
+
+	if (nMode == AcqMode::RAD)
 	{
-		FERROR("Set BinningMode Failed, Reason:{$}", nRes);
-		return false;
+		workMode = WorkMode_FreeSync;
+		modeName = "RAD";
+	}
+	else if (nMode == AcqMode::DDR)
+	{
+		workMode = WorkMode_DDR;
+		modeName = "DDR";
+		nXWindow = 800;
+	}
+	else if (nMode == AcqMode::PF)
+	{
+		workMode = WorkMode_SyncIn;
+		modeName = "PF";
+		nXWindow = 800;
 	}
 	else
 	{
-		FINFO("Set BinningMode Success");
+		FERROR("Invalid acquisition mode: {$}", nMode);
+		return false;
 	}
 
-	TiRayVariant ParamAED[2]{};
-	ParamAED[0].Type = TiRayVariant::TiRayInt;
-	ParamAED[0].IntValue = TiRayAttribute::Attr_AEDSensitivity;
-	ParamAED[1].Type = TiRayVariant::TiRayInt;
-	ParamAED[1].IntValue = 200;
+	FINFO("Setting work mode to {$} (WorkMode: {$})", modeName, workMode);
 
-	nRes = Execute_Ptr(m_nDetectorID, Cmd_WriteAttribute, ParamAED, 2);
-	if ((TiRayError)nRes != TiRayError::Err_Success)
+	// 设置工作模式
+	auto err = write_attribute(Attr_WorkMode, workMode);
+	if (err != Err_Success)
 	{
-		FERROR("Set BinningMode Failed, Reason:{$}", nRes);
+		FERROR("Failed to set work mode, error code: {$}", err);
 		return false;
 	}
-	else
+
+	FINFO("Work mode set successfully");
+
+	err = write_attribute(Attr_PhotoInterval, nXWindow);
+	if (err != Err_Success)
 	{
-		FINFO("Set BinningMode Success");
+		FERROR("Failed to write Attr_PhotoInterval, error code: {$}", err);
+		return false;
 	}
 
 	StatusFeedback(EVT_STATUS_PANEL, PANEL_SLEEP);
 	FINFO("TiRayDR SetAcqMode Over");
-	
+
 	return true;
 }
 
@@ -500,6 +521,10 @@ bool Detector_TiRayDR::PrepareAcquisition(FPDDeviceTiRay* pDrvDPC)
 	}
 	g_pDetector->StatusFeedback(EVT_STATUS_PANEL, PANEL_STANDBY);
 
+	// 生成新的会话时间戳,这次拍摄的所有图片都将使用这个时间戳
+	m_strCurrentSessionTimestamp = generateReadableTimestamp();
+	m_nSessionFrameCounter = 0; // 重置帧计数器
+	FINFO("New session started with timestamp: {$}", m_strCurrentSessionTimestamp);
 
 	//m_hRadEvent->SetEvent();
 
@@ -1899,94 +1924,195 @@ bool Detector_TiRayDR::cropImage(unsigned short* srcData, int srcWidth, int srcH
 	return true;
 }
 
+// 生成可读的时间戳字符串: YYYYMMDD_HHMMSS_mmm
+static std::string generateReadableTimestamp()
+{
+	GlobalTime currentTime = { 0 };
+	GetLocalTime(&currentTime);
+
+	char timestamp[32];
+	snprintf(timestamp, sizeof(timestamp), "%04d%02d%02d_%02d%02d%02d_%03d",
+		currentTime.wYear, currentTime.wMonth, currentTime.wDay,
+		currentTime.wHour, currentTime.wMinute, currentTime.wSecond,
+		currentTime.wMilliseconds);
+
+	return std::string(timestamp);
+}
+
 std::string Detector_TiRayDR::saveProcessedImage(Detector_TiRayDR& detector, unsigned short* data, size_t size, const std::string& baseTimestamp)
 {
 	const size_t MAX_PROC_FILE_COUNT = 10;
 	const std::string TMPFS_DIR = "/mnt/tmpfs/";
+	const std::string BACKUP_DIR = "/userdata/RawData/";
 	const std::string ORIGINAL_DIR = detector.m_strWorkPath + "/RawData/";
+	const std::string FILE_PREFIX = "Proc_";
+	const uintmax_t MIN_REQUIRED_SPACE = size * 2; // 至少需要两倍图像大小的空间
+
 	std::string procDir;
 	bool useTmpfs = false;
+	std::string filePath;
 
-	// 确定存储目录
-	if (std::filesystem::exists(TMPFS_DIR) && std::filesystem::is_directory(TMPFS_DIR))
-	{
-		procDir = TMPFS_DIR;
-		useTmpfs = true;
-		std::cout << "[Detector_TiRayDR::saveProcessedImage] Using tmpfs directory: " << procDir << std::endl;
-	}
-	else
-	{
-		procDir = ORIGINAL_DIR;
-		std::cout << "[Detector_TiRayDR::saveProcessedImage] /mnt/tmpfs not found, using original directory: " << procDir << std::endl;
-	}
+	// 辅助函数:检查目录空间是否足够
+	auto hasEnoughSpace = [&](const std::string& dirPath) -> bool {
+		try {
+			if (!std::filesystem::exists(dirPath))
+				return false;
 
-	const std::string FILE_PREFIX = "Proc_";
-	std::string filePath;
+			auto fsInfo = std::filesystem::space(dirPath);
+			bool hasSpace = fsInfo.available >= MIN_REQUIRED_SPACE;
+			return hasSpace;
+		}
+		catch (const std::filesystem::filesystem_error& e) {
+			FINFO("Failed to check space for {$}: {$}", dirPath, e.what());
+			return false;
+		}
+	};
 
-	try
-	{
-		std::filesystem::create_directories(procDir);
-		filePath = procDir + FILE_PREFIX + baseTimestamp + ".raw";
+	// 辅助函数:尝试在指定目录保存图像
+	auto tryToSaveInDir = [&](const std::string& dirPath, bool isTmpfs) -> bool {
+		try {
+			std::filesystem::create_directories(dirPath);
+			filePath = dirPath + FILE_PREFIX + baseTimestamp + ".raw";
+
+			std::ofstream file(filePath, std::ios::binary);
+			if (!file.is_open())
+			{
+				FINFO("Failed to open file: {$}", filePath);
+				return false;
+			}
 
-		std::ofstream file(filePath, std::ios::binary);
-		if (file.is_open())
-		{
 			file.write(reinterpret_cast<const char*>(detector.m_pImgBuffer), size);
-			std::cout << "[Detector_TiRayDR::saveProcessedImage] Saved image: " << filePath << std::endl;
+			file.close();
+
+			if (file.fail())
+			{
+				FINFO("Failed to write processed image: {$}", filePath);
+				// 尝试删除不完整的文件
+				std::filesystem::remove(filePath);
+				return false;
+			}
+
+			FINFO("Saved processed image: {$}", filePath);
+
+			// 使用完整的 baseTimestamp 作为会话标识(格式:YYYYMMDD_HHMMSS_mmm)
+			// 清理旧文件,但保护当前会话的文件
+			cleanOldFiles(dirPath, FILE_PREFIX, MAX_PROC_FILE_COUNT, isTmpfs, baseTimestamp);
+			return true;
 		}
-		else
+		catch (const std::filesystem::filesystem_error& e)
 		{
-			std::cout << "[Detector_TiRayDR::saveProcessedImage] Failed to open file: " << filePath << std::endl;
-			return "";
+			FINFO("File system error in {$}: {$}", dirPath, e.what());
+			return false;
+		}
+		catch (const std::exception& e)
+		{
+			FINFO("Unexpected error in {$}: {$}", dirPath, e.what());
+			return false;
 		}
+	};
 
-		cleanOldFiles(procDir, FILE_PREFIX, MAX_PROC_FILE_COUNT, useTmpfs);
+	// 策略1: 优先使用 tmpfs(如果存在且空间足够)
+	if (std::filesystem::exists(TMPFS_DIR) && std::filesystem::is_directory(TMPFS_DIR))
+	{
+		if (hasEnoughSpace(TMPFS_DIR))
+		{
+			if (tryToSaveInDir(TMPFS_DIR, true))
+				return filePath;
+		}
+		else
+		{
+			FINFO("tmpfs space insufficient, trying alternatives");
+		}
 	}
-	catch (const std::filesystem::filesystem_error& e)
+
+	// 策略2: 尝试原始目录
+	if (hasEnoughSpace(ORIGINAL_DIR))
 	{
-		std::cout << "[Detector_TiRayDR::saveProcessedImage] File system error: " << e.what() << std::endl;
-		return "";
+		if (tryToSaveInDir(ORIGINAL_DIR, false))
+			return filePath;
 	}
-	catch (const std::exception& e)
+	else
 	{
-		std::cout << "[Detector_TiRayDR::saveProcessedImage] Unexpected error: " << e.what() << std::endl;
-		return "";
+		FINFO("Original directory space insufficient, trying backup");
 	}
 
-	return filePath;
+	// 策略3: 使用备用目录 /userdata/
+	FINFO("Using backup directory: {$}", BACKUP_DIR);
+	if (tryToSaveInDir(BACKUP_DIR, false))
+		return filePath;
+
+	// 所有策略都失败
+	FINFO("ERROR: Failed to save processed image in all available directories!");
+	return "";
 }
 
 void Detector_TiRayDR::saveRawImage(Detector_TiRayDR& detector, unsigned short* data, size_t size, const std::string& baseTimestamp)
 {
 	const std::string RAW_DIR = detector.m_strWorkPath + "/OriginalData/";
+	const std::string BACKUP_RAW_DIR = "/userdata/OriginalData/";
 	const std::string FILE_PREFIX = "orig_";
-	const size_t MAX_RAW_FILE_COUNT = 20; 
+	const size_t MAX_RAW_FILE_COUNT = 20;
+	const uintmax_t MIN_REQUIRED_SPACE = size * 2;
 	std::string filePath;
 
-	try
-	{
-		std::filesystem::create_directories(RAW_DIR);
-		filePath = RAW_DIR + FILE_PREFIX + baseTimestamp + ".raw";
+	// 辅助函数:尝试保存到指定目录
+	auto tryToSaveRawInDir = [&](const std::string& dirPath) -> bool {
+		try {
+			std::filesystem::create_directories(dirPath);
+			filePath = dirPath + FILE_PREFIX + baseTimestamp + ".raw";
+
+			std::ofstream file(filePath, std::ios::binary);
+			if (!file.is_open())
+			{
+				FINFO("Failed to open raw image file: {$}", filePath);
+				return false;
+			}
 
-		std::ofstream file(filePath, std::ios::binary);
-		if (file.is_open())
-		{
-			// 直接写入unsigned short缓冲区数据
 			file.write(reinterpret_cast<const char*>(data), size);
-			cout << "[Detector_TiRayDR::saveRawImage] Saved raw image: " << filePath << endl;
+			file.close();
+
+			if (file.fail())
+			{
+				FINFO("Failed to write raw image: {$}", filePath);
+				std::filesystem::remove(filePath);
+				return false;
+			}
+
+			FINFO("Saved raw image: {$}", filePath);
+
+			// 使用完整的 baseTimestamp 作为会话标识(格式:YYYYMMDD_HHMMSS_mmm)
+			// 清理旧的原始图,但保护当前会话
+			cleanOldFiles(dirPath, FILE_PREFIX, MAX_RAW_FILE_COUNT, false, baseTimestamp);
+			return true;
 		}
-		else
+		catch (const std::filesystem::filesystem_error& e)
 		{
-			cout << "[Detector_TiRayDR::saveRawImage] Failed to open file: " << filePath << endl;
-			return;
+			FINFO("File system error in {$}: {$}", dirPath, e.what());
+			return false;
 		}
+	};
 
-		// 清理旧的原始图
-		cleanOldFiles(RAW_DIR, FILE_PREFIX, MAX_RAW_FILE_COUNT, false);
+	// 尝试主目录
+	try {
+		auto fsInfo = std::filesystem::space(RAW_DIR);
+		if (fsInfo.available >= MIN_REQUIRED_SPACE)
+		{
+			if (tryToSaveRawInDir(RAW_DIR))
+				return;
+		}
+		else
+		{
+			FINFO("Primary directory space insufficient, using backup");
+		}
 	}
-	catch (const std::filesystem::filesystem_error& e)
+	catch (const std::filesystem::filesystem_error& e) {
+		FINFO("Failed to check primary directory space: {$}", e.what());
+	}
+
+	// 尝试备用目录
+	if (!tryToSaveRawInDir(BACKUP_RAW_DIR))
 	{
-		cout << "[Detector_TiRayDR::saveRawImage] File system error: " << e.what() << endl;
+		FINFO("ERROR: Failed to save raw image in all available directories!");
 	}
 }
 
@@ -1995,27 +2121,25 @@ std::filesystem::space_info getFileSystemInfo(const std::string& dirPath) {
 		return std::filesystem::space(dirPath);
 	}
 	catch (const std::filesystem::filesystem_error& e) {
-		std::cout << "[getFileSystemInfo] Failed to get filesystem info for "
-			<< dirPath << ": " << e.what() << std::endl;
+		FINFO("Failed to get filesystem info for {$}: {$}", dirPath, e.what());
 		return {};
 	}
 }
 
 void Detector_TiRayDR::cleanOldFiles(const std::string& dirPath, const std::string& filePrefix,
-	size_t maxCount, bool checkSize)
+	size_t maxCount, bool checkSize, const std::string& currentSessionTimestamp)
 {
-	const std::string funcTag = "[Detector_TiRayDR::cleanOldFiles] ";
-
 	try
 	{
 		// 检查目录是否存在
 		if (!std::filesystem::exists(dirPath) || !std::filesystem::is_directory(dirPath))
 		{
-			std::cout << funcTag << "Directory not found: " << dirPath << std::endl;
 			return;
 		}
 
 		std::vector<std::filesystem::directory_entry> targetFiles;
+		std::vector<std::filesystem::directory_entry> currentSessionFiles;
+
 		for (const auto& entry : std::filesystem::directory_iterator(dirPath))
 		{
 			// 只处理常规文件
@@ -2034,10 +2158,39 @@ void Detector_TiRayDR::cleanOldFiles(const std::string& dirPath, const std::stri
 
 			if (filename.substr(0, filePrefix.length()) == filePrefix)
 			{
-				targetFiles.push_back(entry);
+				// 如果提供了当前会话时间戳,检查文件是否属于当前会话
+				bool isCurrentSession = false;
+				if (!currentSessionTimestamp.empty())
+				{
+					// 文件名格式:Proc_YYYYMMDD_HHMMSS_mmm_frameNNN.raw
+					// currentSessionTimestamp 格式:YYYYMMDD_HHMMSS_mmm
+					// 同一次拍摄的所有图片共享相同的会话时间戳(在PrepareAcquisition时生成)
+					// 检查文件名是否以 prefix + currentSessionTimestamp 开头
+					std::string expectedPrefix = filePrefix + currentSessionTimestamp;
+					if (filename.length() >= expectedPrefix.length())
+					{
+						std::string fileTimestampPart = filename.substr(0, expectedPrefix.length());
+						isCurrentSession = (fileTimestampPart == expectedPrefix);
+					}
+				}
+
+				if (isCurrentSession)
+				{
+					// 当前会话的文件,单独保存,不参与清理
+					currentSessionFiles.push_back(entry);
+				}
+				else
+				{
+					targetFiles.push_back(entry);
+				}
 			}
 		}
 
+		if (!currentSessionFiles.empty())
+		{
+			FINFO("Protected {$} files from current session (timestamp: {$})", currentSessionFiles.size(), currentSessionTimestamp);
+		}
+
 		// 按文件最后修改时间排序(最旧的在前)
 		std::sort(targetFiles.begin(), targetFiles.end(),
 			[](const std::filesystem::directory_entry& a, const std::filesystem::directory_entry& b)
@@ -2055,13 +2208,9 @@ void Detector_TiRayDR::cleanOldFiles(const std::string& dirPath, const std::stri
 			for (size_t i = 0; i < filesToDelete; ++i)
 			{
 				const auto& fileToDelete = targetFiles[i];
-				if (std::filesystem::remove(fileToDelete.path()))
-				{
-					std::cout << funcTag << "Deleted old file (count limit): " << fileToDelete.path() << std::endl;
-				}
-				else
+				if (!std::filesystem::remove(fileToDelete.path()))
 				{
-					std::cout << funcTag << "Failed to delete file: " << fileToDelete.path() << std::endl;
+					FINFO("Failed to delete file: {$}", fileToDelete.path().string());
 				}
 			}
 
@@ -2077,7 +2226,6 @@ void Detector_TiRayDR::cleanOldFiles(const std::string& dirPath, const std::stri
 			auto fsInfo = getFileSystemInfo(dirPath);
 			if (fsInfo.capacity == 0) // 获取信息失败
 			{
-				std::cout << funcTag << "Cannot check filesystem size, skipping size check" << std::endl;
 				return;
 			}
 
@@ -2095,8 +2243,6 @@ void Detector_TiRayDR::cleanOldFiles(const std::string& dirPath, const std::stri
 				};
 
 			uintmax_t currentTotalSize = calculateTotalSize(targetFiles);
-			std::cout << funcTag << "Current files total size: " << currentTotalSize
-				<< ", Max allowed size: " << maxAllowedSize << std::endl;
 
 			// 如果当前总大小超过限制,继续删除最旧的文件
 			while (currentTotalSize > maxAllowedSize && !targetFiles.empty())
@@ -2106,9 +2252,6 @@ void Detector_TiRayDR::cleanOldFiles(const std::string& dirPath, const std::stri
 
 				if (std::filesystem::remove(fileToDelete.path()))
 				{
-					std::cout << funcTag << "Deleted old file (size limit): " << fileToDelete.path()
-						<< " (" << fileSize << " bytes)" << std::endl;
-
 					// 更新总量和文件列表
 					currentTotalSize -= fileSize;
 					targetFiles.erase(targetFiles.begin());
@@ -2116,22 +2259,24 @@ void Detector_TiRayDR::cleanOldFiles(const std::string& dirPath, const std::stri
 				}
 				else
 				{
-					std::cout << funcTag << "Failed to delete file: " << fileToDelete.path() << std::endl;
+					FINFO("Failed to delete file: {$}", fileToDelete.path().string());
 					break; // 删除失败时停止处理
 				}
 			}
 		}
 
-		std::cout << funcTag << "Cleanup completed. Deleted " << filesToDelete
-			<< " files. Remaining: " << targetFiles.size() << std::endl;
+		if (filesToDelete > 0)
+		{
+			FINFO("Cleanup completed. Deleted {$} files. Remaining: {$}", filesToDelete, targetFiles.size());
+		}
 	}
 	catch (const std::filesystem::filesystem_error& e)
 	{
-		std::cout << funcTag << "File system error: " << e.what() << std::endl;
+		FINFO("File system error during cleanup: {$}", e.what());
 	}
 	catch (const std::exception& e)
 	{
-		std::cout << funcTag << "Unexpected error: " << e.what() << std::endl;
+		FINFO("Unexpected error during cleanup: {$}", e.what());
 	}
 }
 
@@ -2157,12 +2302,11 @@ void Detector_TiRayDR::handleHardwareSyncImage(Detector_TiRayDR& detector, TiRay
 
 	const size_t processedSize = detector.m_nImageWidth * detector.m_nImageHeight * sizeof(unsigned short);
 
-	// 生成关联的基础文件名
-	const std::string baseTimestamp = std::to_string(
-		std::chrono::duration_cast<std::chrono::milliseconds>(
-			std::chrono::system_clock::now().time_since_epoch()
-		).count()
-	);
+	// 使用会话时间戳 + 帧号生成文件名
+	detector.m_nSessionFrameCounter++;
+	char frameStr[16];
+	snprintf(frameStr, sizeof(frameStr), "_frame%03d", detector.m_nSessionFrameCounter);
+	const std::string baseTimestamp = detector.m_strCurrentSessionTimestamp + std::string(frameStr);
 
 	std::string processedImagePath = saveProcessedImage(
 		detector,
@@ -2211,12 +2355,11 @@ void Detector_TiRayDR::handleSoftwareSyncImage(Detector_TiRayDR& detector, TiRay
 
 	const size_t processedSize = detector.m_nImageWidth * detector.m_nImageHeight * sizeof(unsigned short);
 
-	// 生成关联的基础文件名
-	const std::string baseTimestamp = std::to_string(
-		std::chrono::duration_cast<std::chrono::milliseconds>(
-			std::chrono::system_clock::now().time_since_epoch()
-		).count()
-	);
+	// 使用会话时间戳 + 帧号生成文件名
+	detector.m_nSessionFrameCounter++;
+	char frameStr[16];
+	snprintf(frameStr, sizeof(frameStr), "_frame%03d", detector.m_nSessionFrameCounter);
+	const std::string baseTimestamp = detector.m_strCurrentSessionTimestamp + std::string(frameStr);
 
 	std::string processedImagePath = saveProcessedImage(
 		detector,
@@ -2267,12 +2410,11 @@ void Detector_TiRayDR::handleAedSyncImage(Detector_TiRayDR& detector, TiRayVaria
 
 	const size_t processedSize = detector.m_nImageWidth * detector.m_nImageHeight * sizeof(unsigned short);
 
-	// 生成关联的基础文件名
-	const std::string baseTimestamp = std::to_string(
-		std::chrono::duration_cast<std::chrono::milliseconds>(
-			std::chrono::system_clock::now().time_since_epoch()
-		).count()
-	);
+	// 使用会话时间戳 + 帧号生成文件名
+	detector.m_nSessionFrameCounter++;
+	char frameStr[16];
+	snprintf(frameStr, sizeof(frameStr), "_frame%03d", detector.m_nSessionFrameCounter);
+	const std::string baseTimestamp = detector.m_strCurrentSessionTimestamp + std::string(frameStr);
 
 	std::string processedImagePath = saveProcessedImage(
 		detector,
@@ -2345,7 +2487,7 @@ void Detector_TiRayDR::handleDarkCalibration(Detector_TiRayDR& detector, TiRayVa
 		template_image.height = image.height;
 
 		// 生成模板并检查结果
-		auto err = GenerateTemplate_Ptr(Offset, datas.data(), static_cast<int>(datas.size()),
+		auto err = GenerateTemplate_Ptr(TemplateType_Offset, datas.data(), static_cast<int>(datas.size()),
 			template_image.data.data(), static_cast<int>(template_image.data.size()));
 
 		if (err != Err_Success) {
@@ -2414,7 +2556,7 @@ void Detector_TiRayDR::handleGainCalibration(Detector_TiRayDR& detector, TiRayVa
 			template_image.height = detector.m_gainCalibImages[0].height;
 			template_image.data.resize(datas[0].DataLen);
 
-			auto err = GenerateTemplate_Ptr(Gain, datas.data(), static_cast<int>(datas.size()),
+			auto err = GenerateTemplate_Ptr(TemplateType_Gain, datas.data(), static_cast<int>(datas.size()),
 				template_image.data.data(), static_cast<int>(template_image.data.size()));
 			if (err != Err_Success)
 			{
@@ -2470,7 +2612,7 @@ void Detector_TiRayDR::handleGainCalibration(Detector_TiRayDR& detector, TiRayVa
 			mean_image.height = detector.m_currentDoseImages[0].height;
 			mean_image.data.resize(datas[0].DataLen);
 
-			auto err = GenerateTemplate_Ptr(Mean, datas.data(), static_cast<int>(datas.size()),
+			auto err = GenerateTemplate_Ptr(TemplateType_Mean, datas.data(), static_cast<int>(datas.size()),
 				mean_image.data.data(), static_cast<int>(mean_image.data.size()));
 			if (err != Err_Success)
 			{
@@ -2510,7 +2652,7 @@ void Detector_TiRayDR::handleGainCalibration(Detector_TiRayDR& detector, TiRayVa
 				final_template.height = detector.m_gainV2MeanImages[0].height;
 				final_template.data.resize(meanDatas[0].DataLen);
 
-				auto err = GenerateTemplate_Ptr(GainV2, meanDatas.data(), static_cast<int>(meanDatas.size()),
+				auto err = GenerateTemplate_Ptr(TemplateType_GainV2, meanDatas.data(), static_cast<int>(meanDatas.size()),
 					final_template.data.data(), static_cast<int>(final_template.data.size()));
 				if (err != Err_Success)
 				{
@@ -2623,7 +2765,7 @@ void Detector_TiRayDR::on_event_callback(int detectorId, TiRayEvent eventType, T
 	}
 	case TiRayEvent::Evt_ExposureStatus:
 	{
-		FINFO("argv {$}, argc {$}", argv[0].IntValue, argc);
+		FINFO("Evt_ExposureStatus:argv {$}, argc {$}", argv[0].IntValue, argc);
 		if (argv[0].IntValue == 0)
 		{
 			detector.m_bAEDReady = true;

+ 4 - 2
Detector/TiRay/CCOS.Dev.FPD.TiRayDR/Detector_TiRayDR.h

@@ -117,7 +117,7 @@ public:
 		int destWidth, int destHeight);
 	static std::string saveProcessedImage(Detector_TiRayDR& detector, unsigned short* data, size_t size, const std::string& baseTimestamp);
 	static void saveRawImage(Detector_TiRayDR& detector, unsigned short* data, size_t size, const std::string& baseTimestamp);
-	static void cleanOldFiles(const std::string& dirPath, const std::string& filePrefix,size_t maxCount, bool checkSize);
+	static void cleanOldFiles(const std::string& dirPath, const std::string& filePrefix,size_t maxCount, bool checkSize, const std::string& currentSessionTimestamp = "");
 	static void handleHardwareSyncImage(Detector_TiRayDR& detector, TiRayVariant argv[]);
 	static void handleSoftwareSyncImage(Detector_TiRayDR& detector, TiRayVariant argv[]);
 	static void handleAedSyncImage(Detector_TiRayDR& detector, TiRayVariant argv[]);
@@ -344,7 +344,9 @@ private:
 	std::string m_strWirelessIP;
 	std::string m_strLocalIP;
 	std::string m_strSerialNum;
-	
+	std::string m_strCurrentSessionTimestamp; //当前拍摄会话的时间戳(同一次拍摄的所有图片共享)
+	int m_nSessionFrameCounter; //当前会话的帧计数器
+
 	WORD* m_pRawImgBuffer; //原始图
 	WORD* m_pImgBuffer; //裁剪后图像
 

+ 13 - 4
Detector/TiRay/CCOS.Dev.FPD.TiRayDR/TiRayApi.h

@@ -56,18 +56,18 @@ extern "C" {
    * @param interfaceIp [in] network interface ip for communication, specify null or "0.0.0.0" for all interface.
    * @param scanDuration [in] the scan duration in milliseconds, which will wait the execute finish. 
    * @return Success or possible error: NetworkFailure or other error
-   * @note callback parameters: (string)serial number, (string)model, (string)upper ip, (string)lower ip
+   * @note callback parameters: (string)serial number, (string)model, (string)host ip, (string)detector ip
   */
   TIRAY_API TiRayError Scan(ResultCallback fn, const char* interfaceIp, int scanDuration);
   /**
    * @brief set detector ip addresses synchronized return the result.
    * @param detectorSN [in] the detector serial number.
-   * @param upperIp [in] the upper machine ip.
-   * @param lowerIp [in] the detector local ip.
+   * @param hostIp [in] the host machine ip.
+   * @param deviceIp [in] the detector ip.
    * @param interfaceIp [in] network interface ip for communication, specify null or "0.0.0.0" for all interface.
    * @return Success or possible error: NetworkFailure or other error
   */
-  TIRAY_API TiRayError SetIp(const char* detectorSN, const char* upperIp, const char* lowerIp, const char* interfaceIp);
+  TIRAY_API TiRayError SetIp(const char* detectorSN, const char* hostIp, const char* deviceIp, const char* interfaceIp);
 
   /**
    * @brief flat panel event callback
@@ -89,6 +89,15 @@ extern "C" {
   */
   TIRAY_API TiRayError Startup(TiRayModel model, EventCallback fn, const StartupOption* option);
 
+  /**
+  * @brief startup the TiRay flat panel interface
+  * @param model [in] TiRay flat panel model
+  * @param fn [in] the event callback
+  * @param option [in] [optional] startup option, specify null for use default option
+  * @param userData [in] [optional] User-defined data will be passed back through the reservedArg parameter of the callback function
+  * @return Success or possible error: WrongModel, NetworkFailure(network setting is wrong)
+  */
+  TIRAY_API TiRayError StartupEx(TiRayModel model, EventCallback fn, const StartupOption* option, void* userData);
   /**
    * @brief stop the TiRay flat panel interface
   */

+ 47 - 22
Detector/TiRay/CCOS.Dev.FPD.TiRayDR/TiRayDef.h

@@ -2,8 +2,8 @@
 #define TIRAY_DEF_H
 
 typedef enum {
-  Err_Success = 0,
-  Err_SystemFailure = 1,          //system internal error
+  Err_Success,
+  Err_SystemFailure,              //system internal error
   Err_WrongModel,                 //the specified model is incorrect
   Err_DetectorNonExists,          //the specified detector with the given ID does not exist 
   Err_NetworkFailure,             //the network communication has failed
@@ -11,7 +11,7 @@ typedef enum {
   Err_UploadInProgress,           //the upload is in progress
   Err_Busy,                       //the device is busy processing, can't do anything else.
   Err_Timeout,                    //process timed out
-  Err_ImageNotExist,			  //image not exist when fetch historical image
+  Err_ImageNotExist,              //image not exist when fetch historical image
 } TiRayError;
 
 typedef enum {
@@ -26,19 +26,23 @@ typedef enum {
   Model_DY3543W,
   Model_DY4343W,
   Model_DY3543,
+  Model_DY2430DP,
+  Model_DY2530D,
 } TiRayModel;
 
 typedef enum {
   Cmd_ReadAttribute = 1,
   Cmd_WriteAttribute,
   Cmd_Photo,
-  Cmd_UploadOffsetTemplate,
-  Cmd_UploadGainTemplate,
+  Cmd_UploadOffsetTemplate,  //upload offset template to detector
+  Cmd_UploadGainTemplate,    //upload gain template to detector
   Cmd_UpdateFirmware,
   Cmd_ResetInner,
   Cmd_StopPhoto,
   Cmd_FetchHistoricalImageList,
   Cmd_FetchHistoricalImage,
+  Cmd_DownloadOffsetTemplate, //download offset template from detector to pc
+  Cmd_DownloadGainTemplate,   //download gain template from detector to pc
 } TiRayCommand;
 
 typedef enum {
@@ -57,20 +61,26 @@ typedef enum {
   Attr_WifiApPSK,               //string Wi-Fi ap psk
   Attr_WifiStationSSID,         //string Wi-Fi station ssid
   Attr_WifiStationPSK,          //string Wi-Fi station psk
-  Attr_HibernationTimeout,      //int Hibernation after timeout for wireless detector
-  Attr_BiasVoltMode,            //int BiasVoltMode for GZ0404
-  Attr_BiasVoltage,             //float bias voltage for GZ0404
-
-  //The following attributes currently apply only to DY2530W
-  Attr_BatteryStatus = 1000,    //int battery_level, int charging_status
+  Attr_HibernationTimeout,      //int Hibernation after timeout for wireless detector, at least 60 seconds
+  Attr_BiasVoltMode,            //int BiasVoltMode [GZ0404]
+  Attr_BiasVoltage,             //float bias voltage [GZ0404]
+  Attr_OverlayNumber,           //int image overlay number
+  Attr_OverlayMode,             //int image overlay mode 
+  Attr_RefDac,                  //int ref dac
+  Attr_WifiStationHostIp,       //string Wi-Fi station mode host ip
+  Attr_WifiStationDeviceIp,     //string Wi-Fi station mode detector ip
+  //readonly attribute
+  Attr_BatteryStatus = 1000,    //int battery_level, int charging_status 
   Attr_WifiStatus,              //int signal_intensity, int link_quality(percent)
   Attr_Humiture,                //int Celsius temperature, int humidity(percent)
   Attr_DeviceTime,              //device datetime
   Attr_ExpiredTime,             //device activation expired time
   Attr_DeviceSN,                //device serial number
-  Attr_ControllerVersion,       
+  Attr_CtrlVersion,       
   Attr_FpgaVersion,
-  Attr_ArmVersion,
+  Attr_McuVersion,
+  Attr_ConnectionType,
+
 } TiRayAttribute;
 
 typedef enum {
@@ -86,10 +96,13 @@ typedef enum {
   Evt_ExposureStatus,  //1 closed 0 open
   Evt_FetchHistoricalImageList,
   Evt_FetchHistoricalImage,
+  Evt_DownloadOffsetTemplate,
+  Evt_DownloadGainTemplate,
+  Evt_DetectorStatus,  //0 idle 1 busy
 } TiRayEvent;
 
 typedef enum {
-  WorkMode_Idle = 0,
+  WorkMode_Idle,
   WorkMode_AED = 2,
   WorkMode_SyncOut,
   WorkMode_SyncIn = 5,
@@ -97,6 +110,7 @@ typedef enum {
   WorkMode_Continuous,
   WorkMode_Inner,
   WorkMode_FreeSync,
+  WorkMode_DDR,
 } WorkMode;
 
 typedef enum {
@@ -113,22 +127,33 @@ typedef enum {
 } CalibrationMode;
 
 typedef enum {
-  Gain,
-  Offset,
-  Mean,
-  GainV2,
+  TemplateType_Gain,
+  TemplateType_Offset,
+  TemplateType_Mean,
+  TemplateType_GainV2,
 } TemplateType;
 
 typedef enum {
-  AP,
-  Station,
+  WifiMode_AP,
+  WifiMode_Station,
 } WifiMode;
 
 typedef enum {
-  LowVoltage,
-  HighVoltage,
+  ConnectionType_None,
+  ConnectionType_Wired,
+  ConnectionType_Wireless
+} ConnectionType;
+
+typedef enum {
+  BiasVoltMode_LowVoltage,
+  BiasVoltMode_HighVoltage,
 } BiasVoltMode;
 
+typedef enum {
+  OverlayMode_Average,        //overlay value = sum / number
+  OverlayMode_Additive,       //overlay value = sum
+} OverlayMode;
+
 #pragma pack(push, 1)
 typedef struct {
   char Ip[20];