9 #ifndef shmimMonitor_hpp
10 #define shmimMonitor_hpp
12 #include <ImageStreamIO/ImageStruct.h>
13 #include <ImageStreamIO/ImageStreamIO.h>
15 #include "../../libMagAOX/common/paths.hpp"
28 return "shmimMonitor";
96 template <
class derivedT,
class specificT = shmimT>
256 return *
static_cast<derivedT *
>(
this);
261 template <
class derivedT,
class specificT>
264 template <
class derivedT,
class specificT>
270 template <
class derivedT,
class specificT>
276 template <
class derivedT,
class specificT>
282 template <
class derivedT,
class specificT>
288 template <
class derivedT,
class specificT>
294 template <
class derivedT,
class specificT>
300 template <
class derivedT,
class specificT>
303 config.add(specificT::configSection() +
".threadPrio",
"", specificT::configSection() +
".threadPrio", argType::Required, specificT::configSection(),
"threadPrio",
false,
"int",
"The real-time priority of the shmimMonitor thread.");
305 config.add(specificT::configSection() +
".cpuset",
"", specificT::configSection() +
".cpuset", argType::Required, specificT::configSection(),
"cpuset",
false,
"string",
"The cpuset for the shmimMonitor thread.");
307 config.add(specificT::configSection() +
".shmimName",
"", specificT::configSection() +
".shmimName", argType::Required, specificT::configSection(),
"shmimName",
false,
"string",
"The name of the ImageStreamIO shared memory image. Will be used as /tmp/<shmimName>.im.shm.");
309 config.add(specificT::configSection() +
".getExistingFirst",
"", specificT::configSection() +
".getExistingFirst", argType::Required, specificT::configSection(),
"getExistingFirst",
false,
"bool",
"If true an existing image is loaded. If false we wait for a new image.");
312 m_shmimName = derived().configName();
317 template <
class derivedT,
class specificT>
320 config(m_smThreadPrio, specificT::configSection() +
".threadPrio");
321 config(m_smCpuset, specificT::configSection() +
".cpuset");
322 config(m_shmimName, specificT::configSection() +
".shmimName");
323 config(m_getExistingFirst, specificT::configSection() +
".getExistingFirst");
328 template <
class derivedT,
class specificT>
332 m_indiP_shmimName = pcf::IndiProperty(pcf::IndiProperty::Text);
333 m_indiP_shmimName.setDevice(derived().configName());
334 m_indiP_shmimName.setName(specificT::indiPrefix() +
"_shmimName");
335 m_indiP_shmimName.setPerm(pcf::IndiProperty::ReadOnly);
336 m_indiP_shmimName.setState(pcf::IndiProperty::Idle);
337 m_indiP_shmimName.add(pcf::IndiElement(
"name"));
338 m_indiP_shmimName[
"name"] = m_shmimName;
340 if (derived().registerIndiPropertyNew(m_indiP_shmimName,
nullptr) < 0)
342 #ifndef SHMIMMONITOR_TEST_NOLOG
343 derivedT::template log<software_error>({__FILE__, __LINE__});
349 m_indiP_frameSize = pcf::IndiProperty(pcf::IndiProperty::Number);
350 m_indiP_frameSize.setDevice(derived().configName());
351 m_indiP_frameSize.setName(specificT::indiPrefix() +
"_frameSize");
352 m_indiP_frameSize.setPerm(pcf::IndiProperty::ReadOnly);
353 m_indiP_frameSize.setState(pcf::IndiProperty::Idle);
354 m_indiP_frameSize.add(pcf::IndiElement(
"width"));
355 m_indiP_frameSize[
"width"] = 0;
356 m_indiP_frameSize.add(pcf::IndiElement(
"height"));
357 m_indiP_frameSize[
"height"] = 0;
359 if (derived().registerIndiPropertyNew(m_indiP_frameSize,
nullptr) < 0)
361 #ifndef SHMIMMONITOR_TEST_NOLOG
362 derivedT::template log<software_error>({__FILE__, __LINE__});
368 struct sigaction act;
372 act.sa_flags = SA_SIGINFO;
377 if (sigaction(SIGUSR1, &act, 0) < 0)
379 std::string logss =
"Setting handler for SIGUSR1 failed. Errno says: ";
380 logss += strerror(errno);
382 derivedT::template log<software_error>({__FILE__, __LINE__, errno, 0, logss});
387 if (derived().threadStart(m_smThread, m_smThreadInit, m_smThreadID, m_smThreadProp, m_smThreadPrio, m_smCpuset, specificT::configSection(),
this, smThreadStart) < 0)
389 derivedT::template log<software_error>({__FILE__, __LINE__});
396 template <
class derivedT,
class specificT>
400 if (pthread_tryjoin_np(m_smThread.native_handle(), 0) == 0)
402 derivedT::template log<software_error>({__FILE__, __LINE__,
"shmimMonitor thread " + std::to_string(m_smThreadID) +
" has exited"});
410 template <
class derivedT,
class specificT>
413 if (m_smThread.joinable())
415 pthread_kill(m_smThread.native_handle(), SIGUSR1);
428 template <
class derivedT,
class specificT>
434 template <
class derivedT,
class specificT>
437 m_smThreadID = syscall(SYS_gettid);
440 while (m_smThreadInit ==
true && derived().shutdown() == 0)
449 while (derived().shutdown() == 0)
451 while ((derived().state() !=
stateCodes::OPERATING || m_shmimName ==
"") && !derived().shutdown() && !m_restart)
456 if (derived().shutdown())
471 ImageStreamIO_filename(SM_fname,
sizeof(SM_fname), m_shmimName.c_str());
472 SM_fd = open(SM_fname, O_RDWR);
476 derivedT::template log<text_log>(
"ImageStream " + m_shmimName +
" not found (yet). Retrying . . .",
logPrio::LOG_NOTICE);
486 if (ImageStreamIO_openIm(&m_imageStream, m_shmimName.c_str()) == 0)
488 if (m_imageStream.md[0].sem <= m_semaphoreNumber)
490 ImageStreamIO_closeIm(&m_imageStream);
497 ImageStreamIO_filename(SM_fname,
sizeof(SM_fname), m_shmimName.c_str());
500 int rv = stat(SM_fname, &buffer);
504 derivedT::template log<software_critical>({__FILE__, __LINE__, errno,
"Could not get inode for " + m_shmimName +
". Source process will need to be restarted."});
505 ImageStreamIO_closeIm(&m_imageStream);
508 m_inode = buffer.st_ino;
523 if (derived().m_shutdown)
528 ImageStreamIO_closeIm(&m_imageStream);
532 m_semaphoreNumber = ImageStreamIO_getsemwaitindex(&m_imageStream, m_semaphoreNumber);
534 if (m_semaphoreNumber < 0)
536 derivedT::template log<software_critical>({__FILE__, __LINE__,
"No valid semaphore found for " + m_shmimName +
". Source process will need to be restarted."});
540 derivedT::template log<software_info>({__FILE__, __LINE__,
"got semaphore index " + std::to_string(m_semaphoreNumber) +
" for " + m_shmimName});
542 ImageStreamIO_semflush(&m_imageStream, m_semaphoreNumber);
544 sem_t *sem = m_imageStream.semptr[m_semaphoreNumber];
546 m_dataType = m_imageStream.md[0].datatype;
547 m_typeSize = ImageStreamIO_typesize(m_dataType);
548 m_width = m_imageStream.md[0].size[0];
550 if (m_imageStream.md[0].naxis > 1)
552 m_height = m_imageStream.md[0].size[1];
554 if (m_imageStream.md[0].naxis > 2)
557 m_depth = m_imageStream.md[0].size[2];
571 if (derived().allocate(specificT()) < 0)
573 derivedT::template log<software_error>({__FILE__, __LINE__,
"allocation failed"});
578 size_t snx, sny, snz;
581 if (m_getExistingFirst && !m_restart && derived().shutdown() == 0)
583 if (m_imageStream.md[0].size[2] > 0)
585 curr_image = m_imageStream.md[0].cnt1;
592 atype = m_imageStream.md[0].datatype;
593 snx = m_imageStream.md[0].size[0];
597 sny = m_imageStream.md[0].size[1];
602 sny = m_imageStream.md[0].size[1];
603 snz = m_imageStream.md[0].size[2];
611 if (atype != m_dataType || snx != m_width || sny != m_height || snz != m_depth)
616 char *curr_src = (
char *)m_imageStream.array.raw + curr_image * m_width * m_height * m_typeSize;
618 if (derived().processImage(curr_src, specificT()) < 0)
620 derivedT::template log<software_error>({__FILE__, __LINE__});
630 if (clock_gettime(CLOCK_REALTIME, &ts) < 0)
632 derivedT::template log<software_critical>({__FILE__, __LINE__, errno, 0,
"clock_gettime"});
638 if (sem_timedwait(sem, &ts) == 0)
640 if (m_imageStream.md[0].size[2] > 0)
642 curr_image = m_imageStream.md[0].cnt1;
647 atype = m_imageStream.md[0].datatype;
648 snx = m_imageStream.md[0].size[0];
652 sny = m_imageStream.md[0].size[1];
657 sny = m_imageStream.md[0].size[1];
658 snz = m_imageStream.md[0].size[2];
666 if (atype != m_dataType || snx != m_width || sny != m_height || snz != m_depth)
674 char *curr_src = (
char *)m_imageStream.array.raw + curr_image * m_width * m_height * m_typeSize;
676 if (derived().processImage(curr_src, specificT()) < 0)
678 derivedT::template log<software_error>({__FILE__, __LINE__});
683 if (m_imageStream.md[0].sem <= 0)
692 if (errno != ETIMEDOUT)
694 derivedT::template log<software_error>({__FILE__, __LINE__, errno,
"sem_timedwait"});
701 ImageStreamIO_filename(SM_fname,
sizeof(SM_fname), m_shmimName.c_str());
702 SM_fd = open(SM_fname, O_RDWR);
711 int rv = stat(SM_fname, &buffer);
717 if (buffer.st_ino != m_inode)
729 if (m_semaphoreNumber >= 0)
730 m_imageStream.semReadPID[m_semaphoreNumber] = 0;
731 ImageStreamIO_closeIm(&m_imageStream);
744 ImageStreamIO_closeIm(&m_imageStream);
749 template <
class derivedT,
class specificT>
752 if (!derived().m_indiDriver)
766 #define SHMIMMONITOR_SETUP_CONFIG( cfig ) \
767 if(shmimMonitorT::setupConfig(cfig) < 0) \
769 log<software_error>({__FILE__, __LINE__, "Error from shmimMonitorT::setupConfig"}); \
779 #define SHMIMMONITORT_SETUP_CONFIG( SHMIMMONITORT, cfig ) \
780 if(SHMIMMONITORT::setupConfig(cfig) < 0) \
782 log<software_error>({__FILE__, __LINE__, "Error from " #SHMIMMONITORT "::setupConfig"}); \
791 #define SHMIMMONITOR_LOAD_CONFIG( cfig ) \
792 if(shmimMonitorT::loadConfig(cfig) < 0) \
794 return log<software_error,-1>({__FILE__, __LINE__, "Error from shmimMonitorT::loadConfig"}); \
802 #define SHMIMMONITORT_LOAD_CONFIG( SHMIMMONITORT, cfig ) \
803 if(SHMIMMONITORT::loadConfig(cfig) < 0) \
805 return log<software_error,-1>({__FILE__, __LINE__, "Error from " #SHMIMMONITORT "::loadConfig"}); \
809 #define SHMIMMONITOR_APP_STARTUP \
810 if(shmimMonitorT::appStartup() < 0) \
812 return log<software_error,-1>({__FILE__, __LINE__, "Error from shmimMonitorT::appStartup"}); \
819 #define SHMIMMONITORT_APP_STARTUP( SHMIMMONITORT ) \
820 if(SHMIMMONITORT::appStartup() < 0) \
822 return log<software_error,-1>({__FILE__, __LINE__, "Error from " #SHMIMMONITORT "::appStartup"}); \
826 #define SHMIMMONITOR_APP_LOGIC \
827 if(shmimMonitorT::appLogic() < 0) \
829 return log<software_error,-1>({__FILE__, __LINE__, "Error from shmimMonitorT::appLogic"}); \
836 #define SHMIMMONITORT_APP_LOGIC( SHMIMMONITORT ) \
837 if(SHMIMMONITORT::appLogic() < 0) \
839 return log<software_error,-1>({__FILE__, __LINE__, "Error from " #SHMIMMONITORT "::appLogic"}); \
843 #define SHMIMMONITOR_UPDATE_INDI \
844 if(shmimMonitorT::updateINDI() < 0) \
846 return log<software_error,-1>({__FILE__, __LINE__, "Error from shmimMonitorT::updateINDI"}); \
853 #define SHMIMMONITORT_UPDATE_INDI( SHMIMMONITORT ) \
854 if(SHMIMMONITORT::updateINDI() < 0) \
856 return log<software_error,-1>({__FILE__, __LINE__, "Error from " #SHMIMMONITORT "::updateINDI"}); \
860 #define SHMIMMONITOR_APP_SHUTDOWN \
861 if(shmimMonitorT::appShutdown() < 0) \
863 return log<software_error,-1>({__FILE__, __LINE__, "Error from shmimMonitorT::appShutdown"}); \
870 #define SHMIMMONITORT_APP_SHUTDOWN( SHMIMMONITORT ) \
871 if(SHMIMMONITORT::appShutdown() < 0) \
873 return log<software_error,-1>({__FILE__, __LINE__, "Error from " #SHMIMMONITORT "::appShutdown"}); \
bool m_smThreadInit
Synchronizer for thread startup, to allow priority setting to finish.
uint32_t m_depth
The depth of the circular buffer in the stream.
int appStartup()
Startup function.
pid_t m_smThreadID
The s.m. thread PID.
void smThreadExec()
Execute the monitoring thread.
static shmimMonitor * m_selfMonitor
Static pointer to this (set in constructor). Used for getting out of the static SIGSEGV handler.
uint32_t m_width
The width of the images in the stream.
const uint8_t & dataType() const
const size_t & typeSize() const
int setupConfig(mx::app::appConfigurator &config)
Setup the configuration system.
int updateINDI()
Update the INDI properties for this device controller.
int appLogic()
Checks the shmimMonitor thread.
uint32_t m_height
The height of the images in the stream.
std::thread m_smThread
A separate thread for the actual monitoring.
int m_smThreadPrio
Priority of the shmimMonitor thread, should normally be > 00.
IMAGE m_imageStream
The ImageStreamIO shared memory buffer.
pcf::IndiProperty m_indiP_shmimName
Property used to report the shmim buffer name.
const uint32_t & depth() const
pcf::IndiProperty m_indiP_frameSize
Property used to report the current frame size.
static void smThreadStart(shmimMonitor *s)
Thread starter, called by MagAOXApp::threadStart on thread construction. Calls smThreadExec.
int m_semaphoreNumber
The image structure semaphore index.
std::string m_shmimName
The name of the shared memory image, is used in /tmp/<shmimName>.im.shm. Derived classes should set a...
const uint32_t & width() const
int appShutdown()
Shuts down the shmimMonitor thread.
const uint32_t & height() const
uint8_t m_dataType
The ImageStreamIO type code.
bool m_restart
Flag indicating tha the shared memory should be reinitialized.
int loadConfig(mx::app::appConfigurator &config)
load the configuration system results
pcf::IndiProperty m_smThreadProp
The property to hold the s.m. thread details.
size_t m_typeSize
The size of the type, in bytes. Result of sizeof.
ino_t m_inode
The inode of the image stream file.
const std::string & shmimName() const
std::string m_smCpuset
The cpuset to assign the shmimMonitor thread to. Ignored if empty (the default).
bool m_getExistingFirst
If set to true by derivedT, any existing image will be grabbed and sent to processImage before waitin...
@ OPERATING
The device is operating, other than homing.
void updateIfChanged(pcf::IndiProperty &p, const std::string &el, const T &newVal, indiDriverT *indiDriver, pcf::IndiProperty::PropertyStateType newState=pcf::IndiProperty::Ok)
Update the value of the INDI element, but only if it has changed.
void sigUsr1Handler(int signum, siginfo_t *siginf, void *ucont)
Empty signal handler. SIGUSR1 is used to interrupt sleep in various threads.
if(ipRecv.find(m_powerElement))
constexpr static logPrioT LOG_NOTICE
A normal but significant condition.
static std::string configSection()
static std::string indiPrefix()