Line data Source code
1 : /** \file mzmqServer.hpp
2 : * \brief The MagAO-X milkzmqServer wrapper
3 : *
4 : * \author Jared R. Males (jaredmales@gmail.com)
5 : *
6 : * \ingroup mzmqServer_files
7 : */
8 :
9 : #ifndef mzmqServer_hpp
10 : #define mzmqServer_hpp
11 :
12 : // #include <ImageStruct.h>
13 : // #include <ImageStreamIO.h>
14 :
15 : #include <milkzmqServer.hpp>
16 :
17 : // #include <mx/timeUtils.hpp>
18 :
19 : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
20 : #include "../../magaox_git_version.h"
21 :
22 : namespace MagAOX
23 : {
24 : namespace app
25 : {
26 :
27 : /** \defgroup mzmqServer ImageStreamIO Stream Server
28 : * \brief Writes the contents of an ImageStreamIO image stream over a zeroMQ channel
29 : *
30 : * <a href="../handbook/operating/software/apps/mzmqServer.html">Application Documentation</a>
31 : *
32 : * \ingroup apps
33 : *
34 : */
35 :
36 : /** \defgroup mzmqServer_files ImageStreamIO Stream Synchronization
37 : * \ingroup mzmqServer
38 : */
39 :
40 : /// MagAO-X application to control writing ImageStreamIO streams to a zeroMQ channel
41 : /** \todo document this better
42 : * \todo implement thread-kills for shutdown. Maybe switch to USR1, with library wide empty handler, so it isn't logged.
43 : * \ingroup mzmqServer
44 : *
45 : */
46 : class mzmqServer : public MagAOXApp<>, public milkzmq::milkzmqServer
47 : {
48 :
49 : public:
50 : /// Default c'tor
51 : mzmqServer();
52 :
53 : /// Destructor
54 : ~mzmqServer() noexcept;
55 :
56 : /// Setup the configuration system (called by MagAOXApp::setup())
57 : virtual void setupConfig();
58 :
59 : /// load the configuration system results (called by MagAOXApp::setup())
60 : virtual void loadConfig();
61 :
62 : /// Startup functions
63 : /** Sets up the INDI vars.
64 : *
65 : */
66 : virtual int appStartup();
67 :
68 : /// Implementation of the FSM for the Siglent SDG
69 : virtual int appLogic();
70 :
71 : /// Do any needed shutdown tasks. Currently nothing in this app.
72 : virtual int appShutdown();
73 :
74 : protected:
75 : bool m_compress{ false };
76 : std::vector<std::string> m_shMemImNames;
77 :
78 : /** \name SIGSEGV & SIGBUS signal handling
79 : * These signals occur as a result of a ImageStreamIO source server resetting (e.g. changing frame sizes).
80 : * When they occur a restart of the framegrabber and framewriter thread main loops is triggered.
81 : *
82 : * @{
83 : */
84 : static mzmqServer *m_selfWriter; ///< Static pointer to this (set in constructor). Used for getting out of the
85 : ///< static SIGSEGV handler.
86 :
87 : /// Sets the handler for SIGSEGV and SIGBUS
88 : /** These are caused by ImageStreamIO server resets.
89 : */
90 : int setSigSegvHandler();
91 :
92 : /// The handler called when SIGSEGV or SIGBUS is received, which will be due to ImageStreamIO server resets. Just a
93 : /// wrapper for handlerSigSegv.
94 : static void _handlerSigSegv( int signum, siginfo_t *siginf, void *ucont );
95 :
96 : /// Handles SIGSEGV and SIGBUS. Sets m_restart to true.
97 : void handlerSigSegv( int signum, siginfo_t *siginf, void *ucont );
98 : ///@}
99 :
100 : /** \name milkzmq Status and Error Handling
101 : * Implementation of status updates, warnings, and errors from milkzmq using logs.
102 : *
103 : * @{
104 : */
105 :
106 : /// Log status (with LOG_INFO level of priority).
107 : virtual void reportInfo( const std::string &msg /**< [in] the status message */ );
108 :
109 : /// Log status (with LOG_NOTICE level of priority).
110 : virtual void reportNotice( const std::string &msg /**< [in] the status message */ );
111 :
112 : /// Log a warning.
113 : virtual void reportWarning( const std::string &msg /**< [in] the warning message */ );
114 :
115 : /// Log an error.
116 : virtual void reportError( const std::string &msg, ///< [in] the error message
117 : const std::string &file, ///< [in] the name of the file where the error occurred
118 : int line ///< [in] the line number of the error
119 : );
120 : ///@}
121 : };
122 :
123 : // Set self pointer to null so app starts up uninitialized.
124 : mzmqServer *mzmqServer::m_selfWriter = nullptr;
125 :
126 3 : inline mzmqServer::mzmqServer() : MagAOXApp( MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED )
127 : {
128 1 : m_powerMgtEnabled = false;
129 1 : m_selfWriter = this;
130 :
131 1 : return;
132 0 : }
133 :
134 1 : inline mzmqServer::~mzmqServer() noexcept
135 : {
136 1 : return;
137 1 : }
138 :
139 0 : inline void mzmqServer::setupConfig()
140 : {
141 0 : config.add(
142 : "server.imagePort", "", "server.imagePort", argType::Required, "server", "imagePort", false, "int", "" );
143 :
144 0 : config.add(
145 : "server.shmimNames", "", "server.shmimNames", argType::Required, "server", "shmimNames", false, "string", "" );
146 :
147 0 : config.add(
148 : "server.usecSleep", "", "server.usecSleep", argType::Required, "server", "usecSleep", false, "int", "" );
149 :
150 0 : config.add( "server.fpsTgt", "", "server.fpsTgt", argType::Required, "server", "fpsTgt", false, "float", "" );
151 :
152 0 : config.add( "server.fpsGain", "", "server.fpsGain", argType::Required, "server", "fpsGain", false, "float", "" );
153 :
154 0 : config.add( "server.compress",
155 : "",
156 : "server.compress",
157 : argType::Required,
158 : "server",
159 : "compress",
160 : false,
161 : "bool",
162 : "Flag to turn on compression for INT16 and UINT16." );
163 0 : }
164 :
165 0 : inline void mzmqServer::loadConfig()
166 : {
167 0 : m_argv0 = m_configName;
168 :
169 0 : config( m_imagePort, "server.imagePort" );
170 :
171 0 : config( m_shMemImNames, "server.shmimNames" );
172 0 : config( m_usecSleep, "server.usecSleep" );
173 0 : config( m_fpsTgt, "server.fpsTgt" );
174 :
175 0 : config( m_fpsGain, "server.fpsGain" );
176 :
177 0 : config( m_compress, "server.compress" );
178 0 : }
179 :
180 : #include <sys/syscall.h>
181 :
182 0 : inline int mzmqServer::appStartup()
183 : {
184 :
185 : // Now set up the framegrabber and writer threads.
186 : // - need SIGSEGV and SIGBUS handling for ImageStreamIO restarts
187 : // - initialize the semaphore
188 : // - start the threads
189 :
190 0 : if( setSigSegvHandler() < 0 )
191 : {
192 0 : log<software_error>( { __FILE__, __LINE__ } );
193 0 : return -1;
194 : }
195 :
196 0 : if( m_compress )
197 0 : defaultCompression();
198 :
199 0 : for( size_t n = 0; n < m_shMemImNames.size(); ++n )
200 : {
201 0 : shMemImName( m_shMemImNames[n] );
202 : }
203 :
204 0 : if( serverThreadStart() < 0 )
205 : {
206 0 : log<software_critical>( { __FILE__, __LINE__ } );
207 0 : return -1;
208 : }
209 :
210 0 : for( size_t n = 0; n < m_imageThreads.size(); ++n )
211 : {
212 0 : if( imageThreadStart( n ) > 0 )
213 : {
214 0 : log<software_critical>( { __FILE__, __LINE__, "Starting image thread " + m_imageThreads[n].m_imageName } );
215 0 : return -1;
216 : }
217 : }
218 :
219 0 : std::cerr << "Main Thread: " << syscall( SYS_gettid ) << "\n";
220 :
221 0 : return 0;
222 : }
223 :
224 0 : inline int mzmqServer::appLogic()
225 : {
226 : // first do a join check to see if other threads have exited.
227 :
228 0 : if( pthread_tryjoin_np( m_serverThread.native_handle(), 0 ) == 0 )
229 : {
230 0 : log<software_error>( { __FILE__, __LINE__, "server thread has exited" } );
231 :
232 0 : return -1;
233 : }
234 :
235 0 : for( size_t n = 0; n < m_imageThreads.size(); ++n )
236 : {
237 0 : if( pthread_tryjoin_np( m_imageThreads[n].m_thread->native_handle(), 0 ) == 0 )
238 : {
239 0 : log<software_error>(
240 0 : { __FILE__, __LINE__, "image thread " + m_imageThreads[n].m_imageName + " has exited" } );
241 :
242 0 : return -1;
243 : }
244 : }
245 :
246 0 : return 0;
247 : }
248 :
249 0 : inline int mzmqServer::appShutdown()
250 : {
251 0 : m_timeToDie.store( true, std::memory_order_relaxed );
252 :
253 0 : serverThreadKill();
254 :
255 0 : if( m_serverThread.joinable() )
256 : {
257 0 : m_serverThread.join();
258 : }
259 :
260 0 : for( size_t n = 0; n < m_imageThreads.size(); ++n )
261 : {
262 0 : imageThreadKill( n );
263 : }
264 :
265 0 : for( size_t n = 0; n < m_imageThreads.size(); ++n )
266 : {
267 0 : if( m_imageThreads[n].m_thread->joinable() )
268 : {
269 0 : m_imageThreads[n].m_thread->join();
270 : }
271 : }
272 :
273 0 : return 0;
274 : }
275 :
276 0 : inline int mzmqServer::setSigSegvHandler()
277 : {
278 : struct sigaction act;
279 : sigset_t set;
280 :
281 0 : act.sa_sigaction = &mzmqServer::_handlerSigSegv;
282 0 : act.sa_flags = SA_SIGINFO;
283 0 : sigemptyset( &set );
284 0 : act.sa_mask = set;
285 :
286 0 : errno = 0;
287 0 : if( sigaction( SIGSEGV, &act, 0 ) < 0 )
288 : {
289 0 : std::string logss = "Setting handler for SIGSEGV failed. Errno says: ";
290 0 : logss += strerror( errno );
291 :
292 0 : log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
293 :
294 0 : return -1;
295 0 : }
296 :
297 0 : errno = 0;
298 0 : if( sigaction( SIGBUS, &act, 0 ) < 0 )
299 : {
300 0 : std::string logss = "Setting handler for SIGBUS failed. Errno says: ";
301 0 : logss += strerror( errno );
302 :
303 0 : log<software_error>( { __FILE__, __LINE__, errno, 0, logss } );
304 :
305 0 : return -1;
306 0 : }
307 :
308 0 : log<text_log>( "Installed SIGSEGV/SIGBUS signal handler.", logPrio::LOG_DEBUG );
309 :
310 0 : return 0;
311 : }
312 :
313 0 : inline void mzmqServer::_handlerSigSegv( int signum, siginfo_t *siginf, void *ucont )
314 : {
315 0 : m_selfWriter->handlerSigSegv( signum, siginf, ucont );
316 0 : }
317 :
318 0 : inline void mzmqServer::handlerSigSegv( int signum, siginfo_t *siginf, void *ucont )
319 : {
320 : static_cast<void>( signum );
321 : static_cast<void>( siginf );
322 : static_cast<void>( ucont );
323 :
324 0 : milkzmqServer::requestRestart();
325 :
326 0 : return;
327 : }
328 :
329 0 : inline void mzmqServer::reportInfo( const std::string &msg )
330 : {
331 0 : log<text_log>( msg, logPrio::LOG_INFO );
332 0 : }
333 :
334 0 : inline void mzmqServer::reportNotice( const std::string &msg )
335 : {
336 0 : log<text_log>( msg, logPrio::LOG_NOTICE );
337 0 : }
338 :
339 0 : inline void mzmqServer::reportWarning( const std::string &msg )
340 : {
341 0 : log<text_log>( msg, logPrio::LOG_WARNING );
342 0 : }
343 :
344 0 : inline void mzmqServer::reportError( const std::string &msg, const std::string &file, int line )
345 : {
346 0 : log<software_error>( { file.c_str(), (uint32_t)line, msg } );
347 0 : }
348 :
349 : } // namespace app
350 : } // namespace MagAOX
351 : #endif
|