Line data Source code
1 : /** \file xInstGraph.hpp
2 : * \brief The MagAO-X Instrument Graph header file
3 : *
4 : * \ingroup instGraph_files
5 : */
6 :
7 : #ifndef xInstGraph_hpp
8 : #define xInstGraph_hpp
9 :
10 : #include <instGraph/instGraphXML.hpp>
11 : using namespace ingr;
12 :
13 : #include "../../libMagAOX/libMagAOX.hpp" //Note this is included on command line to trigger pch
14 : #include "../../magaox_git_version.h"
15 :
16 : #include "xigNodes/indiPropNode.hpp"
17 : #include "xigNodes/fsmNode.hpp"
18 : #include "xigNodes/pwrOnOffNode.hpp"
19 : #include "xigNodes/stdMotionNode.hpp"
20 : #include "xigNodes/staticNode.hpp"
21 :
22 : /** \defgroup instGraph
23 : * \brief The XXXXXX application to do YYYYYYY
24 : *
25 : * <a href="../handbook/operating/software/apps/XXXXXX.html">Application Documentation</a>
26 : *
27 : * \ingroup apps
28 : *
29 : */
30 :
31 : /** \defgroup instGraph_files
32 : * \ingroup instGraph
33 : */
34 :
35 : //forward for test harness
36 : namespace xInstGraph_test
37 : {
38 : class xInstGraph;
39 : }
40 :
41 : namespace MagAOX
42 : {
43 : namespace app
44 : {
45 :
46 : /// The MagAO-X xxxxxxxx
47 : /**
48 : * \ingroup instGraph
49 : */
50 : class xInstGraph : public MagAOXApp<true>
51 : {
52 : // Give the test harness access.
53 : friend class xInstGraph_test::xInstGraph;
54 :
55 : protected:
56 : /** \name Configurable Parameters
57 : *@{
58 : */
59 :
60 : // here add parameters which will be config-able at runtime
61 :
62 : ///@}
63 :
64 : ingr::instGraphXML m_graph;
65 :
66 : std::map<std::string, xigNode *> m_nodes;
67 :
68 : std::vector<pcf::IndiProperty *> m_nodeProps; ///< The node INDI properties to register for SetProperty
69 :
70 : std::multimap<std::string, xigNode *> m_nodeHandleSets; /**< Map from propery keys to nodes which
71 : have registered for them*/
72 :
73 : public:
74 : /// Default c'tor.
75 : xInstGraph();
76 :
77 : /// D'tor
78 : ~xInstGraph() noexcept;
79 :
80 : virtual void setupConfig();
81 :
82 : /// Implementation of loadConfig logic, separated for testing.
83 : /** This is called by loadConfig().
84 : */
85 : int loadConfigImpl( mx::app::appConfigurator &_config /**< [in] an application configuration from
86 : which to load values*/
87 : );
88 :
89 : virtual void loadConfig();
90 :
91 : /// Startup function
92 : /**
93 : *
94 : */
95 : virtual int appStartup();
96 :
97 : /// Implementation of the FSM for xInstGraph.
98 : /**
99 : * \returns 0 on no critical error
100 : * \returns -1 on an error requiring shutdown
101 : */
102 : virtual int appLogic();
103 :
104 : /// Shutdown the app.
105 : /**
106 : *
107 : */
108 : virtual int appShutdown();
109 :
110 : static int st_igHandleSetProperty( void *igapp, /**< [in] this pointer */
111 : const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with
112 : the the set property message.*/
113 : );
114 :
115 : int igHandleSetProperty( const pcf::IndiProperty &ipRecv /**< [in] the INDI property sent with
116 : the the set property message.*/
117 : );
118 : };
119 :
120 3 : xInstGraph::xInstGraph() : MagAOXApp( MAGAOX_CURRENT_SHA1, MAGAOX_REPO_MODIFIED )
121 : {
122 1 : return;
123 0 : }
124 :
125 1 : xInstGraph::~xInstGraph()
126 : {
127 7 : for( auto p : m_nodeProps )
128 : {
129 6 : delete p;
130 : }
131 1 : }
132 :
133 1 : void xInstGraph::setupConfig()
134 : {
135 14 : config.add( "graph.file",
136 : "",
137 : "graph.file",
138 : argType::Required,
139 : "graph",
140 : "file",
141 : false,
142 : "string",
143 : "name of input graph drawio file, including extension, in the config directory" );
144 :
145 14 : config.add( "graph.outputPath",
146 : "",
147 : "graph.outputPath",
148 : argType::Required,
149 : "graph",
150 : "outputPath",
151 : false,
152 : "string",
153 : "path to the output graph .drawio file" );
154 1 : }
155 :
156 1 : int xInstGraph::loadConfigImpl( mx::app::appConfigurator &_config )
157 : {
158 : ///\todo this should be relative to config path
159 1 : std::string file;
160 1 : _config( file, "graph.file" );
161 :
162 1 : if( file == "" )
163 : {
164 0 : return log<software_error, -1>( { __FILE__, __LINE__, "no graph file in configuration (graph.file)" } );
165 : }
166 :
167 1 : file = m_configDir + '/' + file;
168 :
169 :
170 1 : std::string outputPath = m_graph.outputPath();
171 1 : _config( outputPath, "graph.outputPath" );
172 1 : m_graph.outputPath( outputPath );
173 :
174 1 : std::string emsg;
175 1 : if( m_graph.loadXMLFile( emsg, file ) < 0 )
176 : {
177 0 : return log<software_error, -1>( { __FILE__, __LINE__, "error loading graph file: " + emsg } );
178 : }
179 :
180 1 : std::vector<std::string> sections;
181 :
182 1 : _config.unusedSections( sections );
183 :
184 1 : if( sections.size() == 0 )
185 : {
186 0 : return log<software_error, -1>( { __FILE__, __LINE__, "no nodes found in configuration" } );
187 : }
188 :
189 6 : for( size_t i = 0; i < sections.size(); ++i )
190 : {
191 10 : bool isNode = config.isSetUnused( mx::app::iniFile::makeKey( sections[i], "type" ) );
192 :
193 5 : if( !isNode )
194 : {
195 0 : continue;
196 : }
197 :
198 5 : std::string type;
199 5 : _config.configUnused( type, mx::app::iniFile::makeKey( sections[i], "type" ) );
200 :
201 : // std::cerr << "found node " << sections[i] << ": " << type << "\n";
202 :
203 5 : xigNode *xn = nullptr;
204 :
205 5 : if( type == "indiProp" )
206 : {
207 1 : indiPropNode *ip = nullptr;
208 : try
209 : {
210 1 : ip = new indiPropNode( sections[i], &m_graph );
211 : }
212 0 : catch( const std::exception &e )
213 : {
214 0 : std::string msg = XIGN_EXCEPTION( "indiGraph::loadConfigImpl", "exception caught" );
215 0 : msg += ": ";
216 0 : msg += e.what();
217 0 : throw std::runtime_error( msg );
218 0 : }
219 :
220 1 : if( ip == nullptr )
221 : {
222 0 : std::string msg = XIGN_EXCEPTION( "indiGraph::loadConfigImpl", "failed to allocate node" );
223 0 : throw std::runtime_error( msg );
224 0 : }
225 :
226 : try
227 : {
228 1 : ip->loadConfig( _config );
229 : }
230 0 : catch( const std::exception &e )
231 : {
232 0 : std::string msg = XIGN_EXCEPTION( "indiGraph::loadConfigImpl", "exception caught" );
233 0 : msg += ": ";
234 0 : msg += e.what();
235 0 : throw std::runtime_error( msg );
236 0 : }
237 :
238 1 : xn = ip;
239 : }
240 4 : else if( type == "pwrOnOff" )
241 : {
242 1 : pwrOnOffNode *nn = nullptr;
243 :
244 : try
245 : {
246 1 : nn = new pwrOnOffNode( sections[i], &m_graph );
247 : }
248 0 : catch( const std::exception &e )
249 : {
250 0 : std::string msg = XIGN_EXCEPTION( "indiGraph::loadConfigImpl", "exception caught" );
251 0 : msg += ": ";
252 0 : msg += e.what();
253 0 : throw std::runtime_error( msg );
254 0 : }
255 :
256 1 : if( nn == nullptr )
257 : {
258 0 : std::string msg = XIGN_EXCEPTION( "indiGraph::loadConfigImpl", "failed to allocate node" );
259 0 : throw std::runtime_error( msg );
260 0 : }
261 :
262 : try
263 : {
264 1 : nn->loadConfig( _config );
265 : }
266 0 : catch( const std::exception &e )
267 : {
268 0 : std::string msg = XIGN_EXCEPTION( "indiGraph::loadConfigImpl", "exception caught" );
269 0 : msg += ": ";
270 0 : msg += e.what();
271 0 : throw std::runtime_error( msg );
272 0 : }
273 :
274 1 : xn = nn;
275 : }
276 3 : else if( type == "fsm" )
277 : {
278 1 : fsmNode *nn = nullptr;
279 :
280 : try
281 : {
282 1 : nn = new fsmNode( sections[i], &m_graph );
283 : }
284 0 : catch( const std::exception &e )
285 : {
286 0 : std::string msg = XIGN_EXCEPTION( "indiGraph::loadConfigImpl", "exception caught" );
287 0 : msg += ": ";
288 0 : msg += e.what();
289 0 : throw std::runtime_error( msg );
290 0 : }
291 :
292 1 : if( nn == nullptr )
293 : {
294 0 : std::string msg = XIGN_EXCEPTION( "indiGraph::loadConfigImpl", "failed to allocate node" );
295 0 : throw std::runtime_error( msg );
296 0 : }
297 :
298 : try
299 : {
300 1 : nn->loadConfig( _config );
301 : }
302 0 : catch( const std::exception &e )
303 : {
304 0 : std::string msg = XIGN_EXCEPTION( "indiGraph::loadConfigImpl", "exception caught" );
305 0 : msg += ": ";
306 0 : msg += e.what();
307 0 : throw std::runtime_error( msg );
308 0 : }
309 :
310 1 : xn = nn;
311 : }
312 2 : else if( type == "stdMotion" )
313 : {
314 1 : stdMotionNode *nn = nullptr;
315 :
316 : try
317 : {
318 1 : nn = new stdMotionNode( sections[i], &m_graph );
319 : }
320 0 : catch( const std::exception &e )
321 : {
322 0 : std::string msg = XIGN_EXCEPTION( "indiGraph::loadConfigImpl", "exception caught" );
323 0 : msg += ": ";
324 0 : msg += e.what();
325 0 : throw std::runtime_error( msg );
326 0 : }
327 :
328 1 : if( nn == nullptr )
329 : {
330 0 : std::string msg = XIGN_EXCEPTION( "indiGraph::loadConfigImpl", "failed to allocate node" );
331 0 : throw std::runtime_error( msg );
332 0 : }
333 :
334 : try
335 : {
336 1 : nn->loadConfig( _config );
337 : }
338 0 : catch( const std::exception &e )
339 : {
340 0 : std::string msg = XIGN_EXCEPTION( "indiGraph::loadConfigImpl", "exception caught" );
341 0 : msg += ": ";
342 0 : msg += e.what();
343 0 : throw std::runtime_error( msg );
344 0 : }
345 :
346 1 : xn = nn;
347 : }
348 1 : else if( type == "static" )
349 : {
350 1 : staticNode *nn = nullptr;
351 :
352 : try
353 : {
354 1 : nn = new staticNode( sections[i], &m_graph );
355 : }
356 0 : catch( const std::exception &e )
357 : {
358 0 : std::string msg = XIGN_EXCEPTION( "indiGraph::loadConfigImpl", "exception caught" );
359 0 : msg += ": ";
360 0 : msg += e.what();
361 0 : throw std::runtime_error( msg );
362 0 : }
363 :
364 1 : if( nn == nullptr )
365 : {
366 0 : std::string msg = XIGN_EXCEPTION( "indiGraph::loadConfigImpl", "failed to allocate node" );
367 0 : throw std::runtime_error( msg );
368 0 : }
369 :
370 : try
371 : {
372 1 : nn->loadConfig( _config );
373 : }
374 0 : catch( const std::exception &e )
375 : {
376 0 : std::string msg = XIGN_EXCEPTION( "indiGraph::loadConfigImpl", "exception caught" );
377 0 : msg += ": ";
378 0 : msg += e.what();
379 0 : throw std::runtime_error( msg );
380 0 : }
381 :
382 1 : xn = nn;
383 : }
384 :
385 5 : if( xn != nullptr )
386 : {
387 : try
388 : {
389 5 : m_nodes.insert( { xn->node()->name(), xn } );
390 : }
391 0 : catch( const std::exception &e )
392 : {
393 0 : std::string msg = e.what();
394 0 : msg += "\ncaught at ";
395 0 : msg += __FILE__;
396 0 : msg += " " + std::to_string( __LINE__ );
397 0 : throw std::runtime_error( msg );
398 0 : }
399 : }
400 5 : }
401 :
402 1 : m_graph.hideLinks();
403 1 : m_graph.hidePuts();
404 :
405 1 : return 0;
406 1 : }
407 :
408 1 : void xInstGraph::loadConfig()
409 : {
410 1 : if( loadConfigImpl( config ) < 0 )
411 : {
412 0 : log<software_error>( { __FILE__, __LINE__, "error loading configuration" } );
413 0 : m_shutdown = true;
414 : }
415 1 : }
416 :
417 6 : std::string deviceFromKey( const std::string &key )
418 : {
419 6 : size_t dot = key.find( '.' );
420 :
421 6 : if( dot == std::string::npos )
422 : {
423 0 : return "";
424 : }
425 :
426 6 : return key.substr( 0, dot );
427 : }
428 :
429 6 : std::string nameFromKey( const std::string &key )
430 : {
431 6 : size_t dot = key.find( '.' );
432 6 : if( dot == std::string::npos )
433 : {
434 0 : return "";
435 : }
436 :
437 6 : return key.substr( dot + 1 );
438 : }
439 :
440 1 : int xInstGraph::appStartup()
441 : {
442 6 : for( auto it = m_nodes.begin(); it != m_nodes.end(); ++it )
443 : {
444 11 : for( auto kit = it->second->keys().begin(); kit != it->second->keys().end(); ++kit )
445 : {
446 : try
447 : {
448 6 : std::string devName = deviceFromKey( *kit );
449 6 : std::string propName = nameFromKey( *kit );
450 :
451 6 : if( devName == "" )
452 : {
453 0 : return log<software_error, -1>(
454 0 : { __FILE__, __LINE__, "bad devName from key: " + it->second->name() } );
455 : }
456 :
457 6 : if( propName == "" )
458 : {
459 0 : return log<software_error, -1>(
460 0 : { __FILE__, __LINE__, "bad propName from key: " + it->second->name() } );
461 : }
462 :
463 6 : m_nodeHandleSets.insert( { *kit, it->second } );
464 :
465 6 : pcf::IndiProperty *p = new pcf::IndiProperty;
466 :
467 6 : p->setDevice( devName );
468 6 : p->setName( propName );
469 :
470 6 : m_nodeProps.push_back( p );
471 :
472 6 : if( !m_indiSetCallBacks.contains( *kit ) )
473 : {
474 : callBackInsertResult result =
475 6 : m_indiSetCallBacks.insert( callBackValueType( *kit, { p, &st_igHandleSetProperty } ) );
476 :
477 6 : if( !result.second )
478 : {
479 0 : return log<software_error, -1>(
480 0 : { __FILE__, __LINE__, "failed to insert INDI property: " + p->createUniqueKey() } );
481 : }
482 : }
483 6 : }
484 0 : catch( std::exception &e )
485 : {
486 0 : return log<software_error, -1>(
487 0 : { __FILE__, __LINE__, std::string( "Exception caught: " ) + e.what() } );
488 0 : }
489 0 : catch( ... )
490 : {
491 0 : return log<software_error, -1>( { __FILE__, __LINE__, "Unknown exception caught." } );
492 0 : }
493 : }
494 : }
495 :
496 1 : state( stateCodes::READY );
497 :
498 1 : return 0;
499 : }
500 :
501 0 : int xInstGraph::appLogic()
502 : {
503 0 : return 0;
504 : }
505 :
506 0 : int xInstGraph::appShutdown()
507 : {
508 : //remove the output file so that it is clear there is no valid graph
509 0 : std::filesystem::remove(m_graph.outputPath());
510 :
511 0 : return 0;
512 : }
513 :
514 1 : int xInstGraph::st_igHandleSetProperty( void *igapp, const pcf::IndiProperty &ipRecv )
515 : {
516 1 : if( igapp == nullptr )
517 : {
518 0 : return -1;
519 : }
520 :
521 1 : return reinterpret_cast<xInstGraph *>( igapp )->igHandleSetProperty( ipRecv );
522 : }
523 :
524 1 : int xInstGraph::igHandleSetProperty( const pcf::IndiProperty &ipRecv )
525 : {
526 1 : std::cerr << ipRecv.createUniqueKey() << '\n';
527 : try
528 : {
529 1 : auto range = m_nodeHandleSets.equal_range( ipRecv.createUniqueKey() );
530 :
531 2 : for( auto it = range.first; it != range.second; ++it )
532 : {
533 1 : std::cerr << it->second->name() << '\n';
534 :
535 1 : int rv = it->second->handleSetProperty( ipRecv );
536 1 : if( rv != 0 )
537 : {
538 0 : return log<software_error, -1>(
539 0 : { __FILE__, __LINE__, "error from handleSetProperty for " + it->second->name() } );
540 : }
541 : }
542 :
543 1 : return 0;
544 : }
545 0 : catch( std::exception &e )
546 : {
547 0 : return log<software_error, -1>( { __FILE__, __LINE__, std::string( "Exception caught: " ) + e.what() } );
548 0 : }
549 0 : catch( ... )
550 : {
551 0 : return log<software_error, -1>( { __FILE__, __LINE__, "Unknown exception caught." } );
552 0 : }
553 : }
554 :
555 : } // namespace app
556 : } // namespace MagAOX
557 :
558 : #endif // xInstGraph_hpp
|