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() :
m_byPixelSize(1),
m_szCameraPath("cam://default"),
m_lExposurePropertyIndex(-1),
m_bDoCameraThread(false),
m_pCameraThread(nullptr),
m_byCameraTrigger(0),
m_iLastCameraResult(DEVICE_OK),
m_uRoiX(0),
m_uRoiY(0),
m_uRoiW(0),
......@@ -188,6 +192,17 @@ XenethCamera::~XenethCamera()
LOG(("XenethCamera::~XenethCamera(%p) m_bInitialized=%d\n", this, m_bInitialized));
if (m_bInitialized)
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();
m_apInstances.remove(this);
g_hLogMutex.unlock();
......@@ -308,7 +323,7 @@ int XenethCamera::Initialize()
AddAllowedValue(p.m_szName.c_str(), szRange.substr(0, iIndex).c_str(), lValue++);
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() &&
(iIndex = p.m_szRange.find('>')) != std::string::npos)
......@@ -333,7 +348,8 @@ int XenethCamera::Initialize()
do
{
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)
if (!std::isalnum(szName[iIndex]))
szName.erase(iIndex--, 1);
......@@ -352,8 +368,6 @@ int XenethCamera::Initialize()
m_aProperties.push_back(p);
}
m_bInitialized = true;
m_bInitializing = false;
CreateStringProperty(MM::g_Keyword_Name, "Xeneth Camera", true); // Name
CreateStringProperty(MM::g_Keyword_Description, "Xeneth/Xenics Camera", true); // Description
CreateStringProperty(MM::g_Keyword_CameraName, szCameraSerial.c_str(), true); // CameraName
......@@ -386,12 +400,34 @@ int XenethCamera::Initialize()
}
LOG(("XenethCamera::Initialize(%p) m_lExposurePropertyIndex=%d\n", this, static_cast<int>(m_lExposurePropertyIndex)));
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;
}
int XenethCamera::Shutdown()
{
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 (XC_IsCapturing(m_hCamera))
......@@ -473,6 +509,9 @@ void XenethCamera::SetExposure(double exposure)
XC_SetPropertyValue(m_hCamera, p->m_szName.c_str(), p->m_szValue.c_str(), nullptr);
break;
}
m_hMutex.lock();
m_byCameraTrigger = 2;
m_hMutex.unlock();
}
}
......@@ -508,7 +547,7 @@ double XenethCamera::GetExposure() const
{
std::string szValue;
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.resize(strlen(szValue.c_str()));
......@@ -607,10 +646,11 @@ void XenethCamera::UpdateROI()
pSrc.u8 = m_abyImage.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)
{
if (iX >= m_uRoiX && iX < (m_uRoiX + m_uRoiW) &&
iY >= m_uRoiY && iY < (m_uRoiY + m_uRoiH))
if (iX >= m_uRoiX && iX < uRoiRight && iY >= m_uRoiY && iY < uRoiBottom)
{
switch (iFrameType)
{
......@@ -694,26 +734,58 @@ unsigned XenethCamera::GetImageBytesPerPixel() const
int XenethCamera::SnapImage()
{
static int g_iSnapCount(0);
if (g_iSnapCount < 1000)
LOG(("XenethCamera::SnapImage(%p) trigger\n", this));
m_abyImage.resize(XC_GetFrameSize(m_hCamera), 0);
if (!m_bInitialized || !m_pCameraThread)
return DEVICE_ERR;
m_hMutex.lock();
if (!XC_IsCapturing(m_hCamera))
{
int iResult(ConvertXenethResult(XC_StartCapture(m_hCamera)));
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)));
int iResult(ConvertXenethResult(XC_GetFrame(m_hCamera, FT_NATIVE, XGF_Blocking,
static_cast<void*>(m_abyImage.data()), static_cast<int>(m_abyImage.size()))));
if (g_iSnapCount < 1000 || iResult != DEVICE_OK)
m_bDoCameraThread = false;
m_hMutex.unlock();
while (m_bInitialized && m_pCameraThread && !m_bDoCameraThread && m_byCameraTrigger)
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;
LOG(("XenethCamera::SnapImage(%p) size=%u iResult=%d\n", this, m_abyImage.size(), iResult));
m_hMutex.lock();
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)
UpdateROI();
return iResult;
m_bDoCameraThread = false;
LOG(("XenethCamera::CameraThread(%p) end\n", this));
}
int XenethCamera::StartSequenceAcquisition(long numImages, double interval_ms, bool stopOnOverflow)
......@@ -722,6 +794,19 @@ int XenethCamera::StartSequenceAcquisition(long numImages, double interval_ms, b
LOG(("XenethCamera::StartSequenceAcquisition(%p) started camera, iResult=%d\n", this, iResult));
if (iResult != DEVICE_OK)
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);
}
......
......@@ -78,31 +78,37 @@ private:
XenethProperty& operator=(const XenethProperty&) = default;
XenethProperty& operator=(XenethProperty&&) = default;
XPropType m_iType;
std::string m_szName;
std::string m_szCategory;
std::string m_szUnit;
std::string m_szRange;
std::string m_szValue;
XPropType m_iType; /// type of property (data type and more)
std::string m_szName; /// name of property
std::string m_szCategory; /// property category
std::string m_szUnit; /// unit of property
std::string m_szRange; /// data range of property
std::string m_szValue; /// value of property
};
mutable bool m_bInitialized;
mutable bool m_bInitializing;
unsigned char m_byPixelSize;
std::string m_szCameraPath;
std::string m_szCalibrationFile;
XCHANDLE m_hCamera;
std::vector<XenethProperty> m_aProperties;
long m_lExposurePropertyIndex;
mutable bool m_bInitialized; /// flag, if driver is initialized
mutable bool m_bInitializing; /// flag, while initializing
std::recursive_mutex m_hMutex; /// mutex (thread synchronisation)
unsigned char m_byPixelSize; /// byte size of one pixel
std::string m_szCameraPath; /// Xeneth path to camera
std::string m_szCalibrationFile; /// file path to calibration file
XCHANDLE m_hCamera; /// camera handle
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
mutable unsigned m_uRoiX;
mutable unsigned m_uRoiY;
mutable unsigned m_uRoiW;
mutable unsigned m_uRoiH;
mutable unsigned m_uRoiX; /// horizontal position of ROI
mutable unsigned m_uRoiY; /// vertical position of ROI
mutable unsigned m_uRoiW; /// width of ROI
mutable unsigned m_uRoiH; /// height of ROI
std::vector<unsigned char> m_abyImage;
std::vector<unsigned char> m_abyROI;
std::vector<unsigned char> m_abyImage; /// last valid camera image
std::vector<unsigned char> m_abyImageTmp; /// temporary camera image
std::vector<unsigned char> m_abyROI; /// ROI of last valid camera image
/// <summary>
/// convienence function to create a MM property with local action function
......@@ -165,6 +171,18 @@ private:
/// <returns>success or error code</returns>
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>
/// update ROI image from camera image
/// </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