Commit 29f6cb28 authored by Rossa, Lutz's avatar Rossa, Lutz
Browse files

put camera handling into background thread, fix some compiler warnings

parent 3ecf37a8
...@@ -133,6 +133,10 @@ XenethCamera::XenethCamera() : ...@@ -133,6 +133,10 @@ XenethCamera::XenethCamera() :
m_byPixelSize(1), m_byPixelSize(1),
m_szCameraPath("cam://default"), m_szCameraPath("cam://default"),
m_lExposurePropertyIndex(-1), m_lExposurePropertyIndex(-1),
m_bDoCameraThread(false),
m_pCameraThread(nullptr),
m_byCameraTrigger(0),
m_iLastCameraResult(DEVICE_OK),
m_uRoiX(0), m_uRoiX(0),
m_uRoiY(0), m_uRoiY(0),
m_uRoiW(0), m_uRoiW(0),
...@@ -188,6 +192,17 @@ XenethCamera::~XenethCamera() ...@@ -188,6 +192,17 @@ XenethCamera::~XenethCamera()
LOG(("XenethCamera::~XenethCamera(%p) m_bInitialized=%d\n", this, m_bInitialized)); LOG(("XenethCamera::~XenethCamera(%p) m_bInitialized=%d\n", this, m_bInitialized));
if (m_bInitialized) if (m_bInitialized)
Shutdown(); Shutdown();
m_hMutex.lock();
if (m_pCameraThread)
{
std::thread* pThread(m_pCameraThread);
m_pCameraThread = nullptr;
m_hMutex.unlock();
pThread->join();
delete pThread;
}
else
m_hMutex.unlock();
g_hLogMutex.lock(); g_hLogMutex.lock();
m_apInstances.remove(this); m_apInstances.remove(this);
g_hLogMutex.unlock(); g_hLogMutex.unlock();
...@@ -308,7 +323,7 @@ int XenethCamera::Initialize() ...@@ -308,7 +323,7 @@ int XenethCamera::Initialize()
AddAllowedValue(p.m_szName.c_str(), szRange.substr(0, iIndex).c_str(), lValue++); AddAllowedValue(p.m_szName.c_str(), szRange.substr(0, iIndex).c_str(), lValue++);
szRange.erase(0, iIndex + 1); szRange.erase(0, iIndex + 1);
} }
SetPropertyLimits(p.m_szName.c_str(), 0, lValue - 1); SetPropertyLimits(p.m_szName.c_str(), 0, static_cast<double>(lValue) - 1.);
} }
else if ((p.m_iType & XType_Base_Mask) == XType_Base_Number && !p.m_szRange.empty() && else if ((p.m_iType & XType_Base_Mask) == XType_Base_Number && !p.m_szRange.empty() &&
(iIndex = p.m_szRange.find('>')) != std::string::npos) (iIndex = p.m_szRange.find('>')) != std::string::npos)
...@@ -333,7 +348,8 @@ int XenethCamera::Initialize() ...@@ -333,7 +348,8 @@ int XenethCamera::Initialize()
do do
{ {
std::string szName(p.m_szName); std::string szName(p.m_szName);
std::transform(szName.begin(), szName.end(), szName.begin(), std::tolower); // see <https://en.cppreference.com/w/cpp/string/byte/tolower>, why this is needed:
std::transform(szName.begin(), szName.end(), szName.begin(), [](char c){return static_cast<char>(std::tolower(static_cast<unsigned char>(c)));});
for (iIndex = 0; iIndex < szName.size(); ++iIndex) for (iIndex = 0; iIndex < szName.size(); ++iIndex)
if (!std::isalnum(szName[iIndex])) if (!std::isalnum(szName[iIndex]))
szName.erase(iIndex--, 1); szName.erase(iIndex--, 1);
...@@ -352,8 +368,6 @@ int XenethCamera::Initialize() ...@@ -352,8 +368,6 @@ int XenethCamera::Initialize()
m_aProperties.push_back(p); m_aProperties.push_back(p);
} }
m_bInitialized = true;
m_bInitializing = false;
CreateStringProperty(MM::g_Keyword_Name, "Xeneth Camera", true); // Name CreateStringProperty(MM::g_Keyword_Name, "Xeneth Camera", true); // Name
CreateStringProperty(MM::g_Keyword_Description, "Xeneth/Xenics Camera", true); // Description CreateStringProperty(MM::g_Keyword_Description, "Xeneth/Xenics Camera", true); // Description
CreateStringProperty(MM::g_Keyword_CameraName, szCameraSerial.c_str(), true); // CameraName CreateStringProperty(MM::g_Keyword_CameraName, szCameraSerial.c_str(), true); // CameraName
...@@ -386,12 +400,34 @@ int XenethCamera::Initialize() ...@@ -386,12 +400,34 @@ int XenethCamera::Initialize()
} }
LOG(("XenethCamera::Initialize(%p) m_lExposurePropertyIndex=%d\n", this, static_cast<int>(m_lExposurePropertyIndex))); LOG(("XenethCamera::Initialize(%p) m_lExposurePropertyIndex=%d\n", this, static_cast<int>(m_lExposurePropertyIndex)));
ClearROI(); ClearROI();
// start background thread for camera readout and wait for successful run (at least one cycle)
m_hMutex.lock();
m_bDoCameraThread = false;
m_pCameraThread = new std::thread(&CameraThreadFunc, this);
m_hMutex.unlock();
while (!m_bDoCameraThread)
std::this_thread::sleep_for(std::chrono::milliseconds(50));
m_bInitialized = true;
m_bInitializing = false;
return DEVICE_OK; return DEVICE_OK;
} }
int XenethCamera::Shutdown() int XenethCamera::Shutdown()
{ {
LOG(("XenethCamera::Shutdown(%p) m_bInitialized=%d\n", this, m_bInitialized)); LOG(("XenethCamera::Shutdown(%p) m_bInitialized=%d\n", this, m_bInitialized));
m_hMutex.lock();
if (m_pCameraThread)
{
std::thread* pThread(m_pCameraThread);
m_pCameraThread = nullptr;
m_hMutex.unlock();
pThread->join();
delete pThread;
}
else
m_hMutex.unlock();
if (m_bInitialized) if (m_bInitialized)
{ {
if (XC_IsCapturing(m_hCamera)) if (XC_IsCapturing(m_hCamera))
...@@ -473,6 +509,9 @@ void XenethCamera::SetExposure(double exposure) ...@@ -473,6 +509,9 @@ void XenethCamera::SetExposure(double exposure)
XC_SetPropertyValue(m_hCamera, p->m_szName.c_str(), p->m_szValue.c_str(), nullptr); XC_SetPropertyValue(m_hCamera, p->m_szName.c_str(), p->m_szValue.c_str(), nullptr);
break; break;
} }
m_hMutex.lock();
m_byCameraTrigger = 2;
m_hMutex.unlock();
} }
} }
...@@ -508,7 +547,7 @@ double XenethCamera::GetExposure() const ...@@ -508,7 +547,7 @@ double XenethCamera::GetExposure() const
{ {
std::string szValue; std::string szValue;
szValue.resize(65536); szValue.resize(65536);
if (XC_GetPropertyValue(m_hCamera, p->m_szName.c_str(), &szValue[0], szValue.size()) == I_OK) if (XC_GetPropertyValue(m_hCamera, p->m_szName.c_str(), &szValue[0], static_cast<int>(szValue.size())) == I_OK)
{ {
szValue[szValue.size() - 1] = '\0'; szValue[szValue.size() - 1] = '\0';
szValue.resize(strlen(szValue.c_str())); szValue.resize(strlen(szValue.c_str()));
...@@ -607,10 +646,11 @@ void XenethCamera::UpdateROI() ...@@ -607,10 +646,11 @@ void XenethCamera::UpdateROI()
pSrc.u8 = m_abyImage.data(); pSrc.u8 = m_abyImage.data();
pDst.u8 = m_abyROI.data(); pDst.u8 = m_abyROI.data();
unsigned uRoiRight(m_uRoiX + m_uRoiW);
unsigned uRoiBottom(m_uRoiX + m_uRoiW);
for (iSrc = iDst = iX = iY = 0; iY < m_uRoiH; ++iSrc) for (iSrc = iDst = iX = iY = 0; iY < m_uRoiH; ++iSrc)
{ {
if (iX >= m_uRoiX && iX < (m_uRoiX + m_uRoiW) && if (iX >= m_uRoiX && iX < uRoiRight && iY >= m_uRoiY && iY < uRoiBottom)
iY >= m_uRoiY && iY < (m_uRoiY + m_uRoiH))
{ {
switch (iFrameType) switch (iFrameType)
{ {
...@@ -694,26 +734,58 @@ unsigned XenethCamera::GetImageBytesPerPixel() const ...@@ -694,26 +734,58 @@ unsigned XenethCamera::GetImageBytesPerPixel() const
int XenethCamera::SnapImage() int XenethCamera::SnapImage()
{ {
static int g_iSnapCount(0); if (!m_bInitialized || !m_pCameraThread)
if (g_iSnapCount < 1000) return DEVICE_ERR;
LOG(("XenethCamera::SnapImage(%p) trigger\n", this)); m_hMutex.lock();
m_abyImage.resize(XC_GetFrameSize(m_hCamera), 0);
if (!XC_IsCapturing(m_hCamera)) if (!XC_IsCapturing(m_hCamera))
{ {
int iResult(ConvertXenethResult(XC_StartCapture(m_hCamera))); int iResult(ConvertXenethResult(XC_StartCapture(m_hCamera)));
LOG(("XenethCamera::SnapImage(%p) started camera, iResult=%d\n", this, iResult)); LOG(("XenethCamera::SnapImage(%p) started camera, iResult=%d\n", this, iResult));
if (iResult != DEVICE_OK)
{
m_hMutex.unlock();
return iResult;
}
m_abyImage.resize(XC_GetFrameSize(m_hCamera), 0);
} }
LOG(("XenethCamera::SnapImage(%p) frame count=%u\n", this, XC_GetFrameCount(m_hCamera))); m_bDoCameraThread = false;
int iResult(ConvertXenethResult(XC_GetFrame(m_hCamera, FT_NATIVE, XGF_Blocking, m_hMutex.unlock();
static_cast<void*>(m_abyImage.data()), static_cast<int>(m_abyImage.size())))); while (m_bInitialized && m_pCameraThread && !m_bDoCameraThread && m_byCameraTrigger)
if (g_iSnapCount < 1000 || iResult != DEVICE_OK) std::this_thread::sleep_for(std::chrono::milliseconds(20));
UpdateROI();
return m_iLastCameraResult;
}
void XenethCamera::CameraThread()
{
LOG(("XenethCamera::CameraThread(%p) start\n", this));
while (m_pCameraThread)
{ {
++g_iSnapCount; m_hMutex.lock();
LOG(("XenethCamera::SnapImage(%p) size=%u iResult=%d\n", this, m_abyImage.size(), iResult)); if (m_bInitialized && XC_IsCapturing(m_hCamera))
{
m_abyImageTmp.resize(XC_GetFrameSize(m_hCamera), 0);
ErrCode dwErr(XC_GetFrame(m_hCamera, FT_NATIVE, 0, static_cast<void*>(m_abyImageTmp.data()), static_cast<int>(m_abyImageTmp.size())));
m_iLastCameraResult = ConvertXenethResult(dwErr);
if (dwErr == I_OK)
{
m_abyImage.swap(m_abyImageTmp);
if (m_byCameraTrigger > 0)
--m_byCameraTrigger;
}
else
{
LOG(("XenethCamera::CameraThread(%p) camera error %u, iResult=%d\n", this, dwErr, m_iLastCameraResult));
if (m_byCameraTrigger > 0)
m_byCameraTrigger = 1;
}
}
m_bDoCameraThread = true;
m_hMutex.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(20));
} }
if (iResult == DEVICE_OK) m_bDoCameraThread = false;
UpdateROI(); LOG(("XenethCamera::CameraThread(%p) end\n", this));
return iResult;
} }
int XenethCamera::StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow) int XenethCamera::StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow)
...@@ -722,6 +794,19 @@ int XenethCamera::StartSequenceAcquisition(long numImages, double interval_ms, b ...@@ -722,6 +794,19 @@ int XenethCamera::StartSequenceAcquisition(long numImages, double interval_ms, b
LOG(("XenethCamera::StartSequenceAcquisition(%p) started camera, iResult=%d\n", this, iResult)); LOG(("XenethCamera::StartSequenceAcquisition(%p) started camera, iResult=%d\n", this, iResult));
if (iResult != DEVICE_OK) if (iResult != DEVICE_OK)
return iResult; return iResult;
m_hMutex.lock();
if (XC_IsCapturing(m_hCamera)) // restart camera to handle longer exposure times
{
XC_StopCapture(m_hCamera);
std::this_thread::sleep_for(std::chrono::milliseconds(50));
XC_StartCapture(m_hCamera);
}
m_abyImage.resize(XC_GetFrameSize(m_hCamera), 0);
if (!m_byCameraTrigger)
m_byCameraTrigger = 1; // wait for next image
m_hMutex.unlock();
return CCameraBase::StartSequenceAcquisition(numImages, interval_ms, stopOnOverflow); return CCameraBase::StartSequenceAcquisition(numImages, interval_ms, stopOnOverflow);
} }
......
...@@ -78,31 +78,37 @@ private: ...@@ -78,31 +78,37 @@ private:
XenethProperty& operator=(const XenethProperty&) = default; XenethProperty& operator=(const XenethProperty&) = default;
XenethProperty& operator=(XenethProperty&&) = default; XenethProperty& operator=(XenethProperty&&) = default;
XPropType m_iType; XPropType m_iType; /// type of property (data type and more)
std::string m_szName; std::string m_szName; /// name of property
std::string m_szCategory; std::string m_szCategory; /// property category
std::string m_szUnit; std::string m_szUnit; /// unit of property
std::string m_szRange; std::string m_szRange; /// data range of property
std::string m_szValue; std::string m_szValue; /// value of property
}; };
mutable bool m_bInitialized; mutable bool m_bInitialized; /// flag, if driver is initialized
mutable bool m_bInitializing; mutable bool m_bInitializing; /// flag, while initializing
unsigned char m_byPixelSize; std::recursive_mutex m_hMutex; /// mutex (thread synchronisation)
std::string m_szCameraPath; unsigned char m_byPixelSize; /// byte size of one pixel
std::string m_szCalibrationFile; std::string m_szCameraPath; /// Xeneth path to camera
XCHANDLE m_hCamera; std::string m_szCalibrationFile; /// file path to calibration file
std::vector<XenethProperty> m_aProperties; XCHANDLE m_hCamera; /// camera handle
long m_lExposurePropertyIndex; std::vector<XenethProperty> m_aProperties; /// list of camera properties
long m_lExposurePropertyIndex; /// index of exposure property
bool m_bDoCameraThread; /// background thread info
std::thread* m_pCameraThread; /// camera background thread handle
unsigned char m_byCameraTrigger; /// trigger count for camera
int m_iLastCameraResult; /// last camera result
static std::list<XenethCamera*> m_apInstances; /// list of active instances for exit static std::list<XenethCamera*> m_apInstances; /// list of active instances for exit
mutable unsigned m_uRoiX; mutable unsigned m_uRoiX; /// horizontal position of ROI
mutable unsigned m_uRoiY; mutable unsigned m_uRoiY; /// vertical position of ROI
mutable unsigned m_uRoiW; mutable unsigned m_uRoiW; /// width of ROI
mutable unsigned m_uRoiH; mutable unsigned m_uRoiH; /// height of ROI
std::vector<unsigned char> m_abyImage; std::vector<unsigned char> m_abyImage; /// last valid camera image
std::vector<unsigned char> m_abyROI; std::vector<unsigned char> m_abyImageTmp; /// temporary camera image
std::vector<unsigned char> m_abyROI; /// ROI of last valid camera image
/// <summary> /// <summary>
/// convienence function to create a MM property with local action function /// convienence function to create a MM property with local action function
...@@ -165,6 +171,18 @@ private: ...@@ -165,6 +171,18 @@ private:
/// <returns>success or error code</returns> /// <returns>success or error code</returns>
int OnCameraProperty(MM::PropertyBase* pProp, MM::ActionType eAct); int OnCameraProperty(MM::PropertyBase* pProp, MM::ActionType eAct);
/// <summary>
/// implements the camera background thread
/// </summary>
void CameraThread();
/// <summary>
/// called for camera background thread
/// </summary>
/// <param name="pInstance">pointer to this instance</param>
static void CameraThreadFunc(void* pInstance)
{ reinterpret_cast<XenethCamera*>(pInstance)->CameraThread(); }
/// <summary> /// <summary>
/// update ROI image from camera image /// update ROI image from camera image
/// </summary> /// </summary>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment