LCOV - code coverage report
Current view: top level - apps/xInstGraph - xInstGraph.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 47.3 % 224 106
Test Date: 2026-01-03 21:03:39 Functions: 83.3 % 12 10

            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
        

Generated by: LCOV version 2.0-1