LCOV - code coverage report
Current view: top level - utils/cursesINDI - cursesINDI.hpp (source / functions) Coverage Total Hit
Test: MagAOX Lines: 0.0 % 541 0
Test Date: 2026-04-15 19:34:29 Functions: 0.0 % 25 0

            Line data    Source code
       1              : 
       2              : #include <fstream>
       3              : 
       4              : #include <thread>
       5              : #include <mutex>
       6              : #include <set>
       7              : 
       8              : 
       9              : #include "../../INDI/libcommon/IndiClient.hpp"
      10              : #include "cursesTableGrid.hpp"
      11              : 
      12              : 
      13              : ///Simple utility to get the display string of a property
      14              : /**
      15              :   * \returns the properly formatted element value
      16              :   * \returns empty string on error
      17              :   */
      18            0 : std::string displayProperty( pcf::IndiProperty & ip /**< [in] the INDI property */ )
      19              : {
      20            0 :    std::string str = ip.getName();
      21            0 :    str += " [";
      22              : 
      23            0 :    std::string tstr = "n";
      24            0 :    if(ip.getType() == pcf::IndiProperty::Text) tstr = "t";
      25            0 :    if(ip.getType() == pcf::IndiProperty::Switch) tstr = "s";
      26            0 :    if(ip.getType() == pcf::IndiProperty::Light) tstr = "l";
      27              : 
      28            0 :    str += tstr;
      29              : 
      30            0 :    str += "]";
      31              : 
      32              : 
      33            0 :    if(ip.getState() == pcf::IndiProperty::Idle) str += "~";
      34            0 :    if(ip.getState() == pcf::IndiProperty::Ok) str += "-";
      35            0 :    if(ip.getState() == pcf::IndiProperty::Busy) str += "*";
      36            0 :    if(ip.getState() == pcf::IndiProperty::Alert) str += "!";
      37              : 
      38            0 :    return str;
      39            0 : }
      40              : 
      41              : ///Simple utility to get the display value of an element
      42              : /**
      43              :   * \returns the properly formatted element value
      44              :   * \returns empty string if element is not in property
      45              :   */
      46            0 : std::string displayValue( pcf::IndiProperty & ip, ///< [in] the INDI property
      47              :                           std::string & el ///< [in] the name of the element
      48              :                         )
      49              : {
      50            0 :    if(!ip.find(el)) return "";
      51              : 
      52            0 :    if(ip.getType() == pcf::IndiProperty::Switch)
      53              :    {
      54            0 :       if( ip[el].getSwitchState() == pcf::IndiElement::Off ) return "|O|";
      55            0 :       if( ip[el].getSwitchState() == pcf::IndiElement::On ) return "|X|";
      56            0 :       return pcf::IndiElement::getSwitchStateString(ip[el].getSwitchState());
      57              :    }
      58              :    else
      59              :    {
      60            0 :       return ip[el].getValue();
      61              :    }
      62              : }
      63              : 
      64              : class cursesINDI : public pcf::IndiClient, public cursesTableGrid
      65              : {
      66              : 
      67              : public:
      68              : 
      69              :    int m_redraw {0};
      70              : 
      71              :    int m_update {0};
      72              : 
      73              :    int m_cursStat {1};
      74              : 
      75              :    WINDOW * w_interactWin {nullptr};
      76              :    WINDOW * w_curvalWin {nullptr};
      77              :    WINDOW * w_attentionWin {nullptr};
      78              : 
      79              :    WINDOW * w_countWin {nullptr};
      80              : 
      81              :    bool m_shutdown {false};
      82              :    bool m_connectionLost{false};
      83              : 
      84              :    std::thread m_drawThread;
      85              :    std::mutex m_drawMutex;
      86              : 
      87              :    std::string m_msgFile {"/tmp/cursesINDI_logs.txt"};
      88              :    std::ofstream m_msgout;
      89              :    int m_msgsPrinted {0};
      90              :    int m_msgsMax {10000};
      91              : 
      92              :    cursesINDI( const std::string &szName,
      93              :                const std::string &szVersion,
      94              :                const std::string &szProtocolVersion
      95              :              );
      96              : 
      97              :    ~cursesINDI();
      98              : 
      99              :    typedef std::map< std::string, pcf::IndiProperty> propMapT;
     100              :    typedef propMapT::value_type propMapValueT;
     101              :    typedef propMapT::iterator propMapIteratorT;
     102              : 
     103              :    propMapT knownProps;
     104              : 
     105              :    struct elementSpec
     106              :    {
     107              :       std::string propKey;
     108              :       std::string device;
     109              :       std::string propertyName;
     110              :       std::string name;
     111              :       int tableRow {-1};
     112              :    };
     113              : 
     114              :    typedef std::map< std::string, elementSpec> elementMapT;
     115              :    typedef elementMapT::value_type elementMapValueT;
     116              :    typedef elementMapT::iterator elementMapIteratorT;
     117              : 
     118              :    elementMapT knownElements;
     119              : 
     120              : 
     121              :    bool m_deviceSearching {false};
     122              :    std::string m_deviceTarget;
     123              : 
     124              :    virtual void handleDefProperty( const pcf::IndiProperty &ipRecv );
     125              : 
     126              :    virtual void handleDelProperty( const pcf::IndiProperty &ipRecv );
     127              : 
     128              :    virtual void handleMessage( const pcf::IndiProperty &ipRecv );
     129              : 
     130              :    virtual void handleSetProperty( const pcf::IndiProperty &ipRecv );
     131              : 
     132              :    virtual void execute();
     133              : 
     134              :    void cursStat(int cs);
     135              : 
     136              :    int cursStat();
     137              : 
     138              :    void startUp();
     139              : 
     140              :    void shutDown();
     141              : 
     142              :    static void _drawThreadStart( cursesINDI * c /**< [in]  */);
     143              : 
     144              :    /// Start the draw thread.
     145              :    int drawThreadStart();
     146              : 
     147              :    /// Execute the draw thread.
     148              :    void drawThreadExec();
     149              : 
     150              : 
     151              :    void redrawTable();
     152              : 
     153              :    void updateTable();
     154              : 
     155              :    /// Update the current-value window.
     156              :    void updateCurVal();
     157              : 
     158              :    void moveCurrent( int nextY,
     159              :                      int nextX
     160              :                    );
     161              : 
     162              :    void _moveCurrent( int nextY,
     163              :                       int nextX
     164              :                     );
     165              : 
     166              :    void keyPressed( int ch );
     167              : 
     168              :    /// If a key is pressed in column 1 (the device column), this function searchs for devices alphabetically.
     169              :    void deviceSearch( int ch );
     170              : 
     171            0 :    virtual int postDraw()
     172              :    {
     173              :       //if(fpout) *fpout << "post draw" << std::endl;
     174              : 
     175            0 :       if(w_countWin)
     176              :       {
     177            0 :          wclear(w_countWin);
     178            0 :          delwin(w_countWin);
     179              :       }
     180            0 :       w_countWin = newwin( 1, m_minWidth, m_yTop+tabHeight()+1, m_xLeft);
     181              : 
     182            0 :       return postPrint();
     183              :    }
     184              : 
     185            0 :    virtual int postPrint()
     186              :    {
     187            0 :       if(! w_countWin) return 0;
     188              : 
     189            0 :       int shown = tabHeight();
     190            0 :       if( m_cellContents.size() - m_startRow <  (size_t) shown ) shown = m_cellContents.size() - m_startRow;
     191              : 
     192            0 :       wclear(w_countWin);
     193            0 :       wprintw(w_countWin, "%i/%zu elements shown.", shown, knownElements.size());
     194            0 :       wrefresh(w_countWin);
     195              : 
     196            0 :       return 0;
     197              :    }
     198              : 
     199              : 
     200              : };
     201              : 
     202            0 : cursesINDI::cursesINDI( const std::string &szName,
     203              :                         const std::string &szVersion,
     204              :                         const std::string &szProtocolVersion
     205            0 :                       ) : pcf::IndiClient(szName, szVersion, szProtocolVersion, "127.0.0.1", 7624)
     206              : {
     207            0 :    m_yTop = 6;
     208            0 :    colWidth({4, 19, 18, 18, 18});
     209              : 
     210            0 :    m_yBot = 1;
     211              : 
     212              : 
     213            0 :    m_msgout.open(m_msgFile);
     214              : 
     215            0 :    if(!m_msgout)
     216              :    {
     217            0 :       throw std::filesystem::filesystem_error(std::format("can't open log file.  you probably need to delete {}", m_msgFile), std::error_code(EPERM, std::system_category()));
     218              :    }
     219              : 
     220              : 
     221            0 : }
     222              : 
     223            0 : cursesINDI::~cursesINDI()
     224              : {
     225            0 :    if( w_interactWin ) delwin(w_interactWin);
     226            0 :    if( w_curvalWin ) delwin(w_curvalWin);
     227            0 :    if( w_attentionWin ) delwin(w_attentionWin);
     228            0 :    if( w_countWin) delwin(w_countWin);
     229            0 : }
     230              : 
     231            0 : void cursesINDI::handleDefProperty( const pcf::IndiProperty &ipRecv )
     232              : {
     233            0 :    if(!ipRecv.hasValidDevice() && !ipRecv.hasValidName())
     234              :    {
     235            0 :       return;
     236              :    }
     237              : 
     238            0 :    std::pair<propMapIteratorT, bool> result;
     239              : 
     240            0 :    std::lock_guard<std::mutex> lock(m_drawMutex);
     241              : 
     242            0 :    result = knownProps.insert(propMapValueT( ipRecv.createUniqueKey(), ipRecv ));
     243              : 
     244              : 
     245            0 :    if(result.second == false)
     246              :    {
     247            0 :       result.first->second = ipRecv; //We already have it, so we're already registered
     248              :    }
     249              :    else
     250              :    {
     251            0 :       sendGetProperties(ipRecv); //Otherwise register for it
     252              :    }
     253              : 
     254            0 :    auto elIt = ipRecv.getElements().begin();
     255              : 
     256            0 :    while(elIt != ipRecv.getElements().end())
     257              :    {
     258            0 :       elementSpec es;
     259            0 :       es.propKey = ipRecv.createUniqueKey();
     260            0 :       es.device = ipRecv.getDevice();
     261            0 :       es.propertyName = ipRecv.getName();
     262            0 :       es.name = elIt->second.getName();
     263              : 
     264            0 :       std::string key = es.propKey + "." + es.name;
     265            0 :       auto elResult = knownElements.insert( elementMapValueT(key, es));
     266              : 
     267            0 :       if(elResult.second == true)
     268              :       {
     269              :          //If result is new insert, add to TUI table if filter requires
     270            0 :          ++m_redraw;
     271              :       }
     272              :       else
     273              :       {
     274              :          //Or just update the table.
     275              :          //Should check if this element actually changed....
     276            0 :          ++m_update;
     277              :       }
     278            0 :       ++elIt;
     279            0 :    }
     280              : 
     281            0 : }
     282              : 
     283            0 : void cursesINDI::handleDelProperty( const pcf::IndiProperty &ipRecv )
     284              : {
     285              :    //if(fpout) *fpout << "got delete property" << std::endl;
     286              : 
     287            0 :    if(ipRecv.hasValidDevice())
     288              :    {
     289            0 :       if(!ipRecv.hasValidName())
     290              :       {
     291              :          //if(fpout) *fpout << "will delete: " << ipRecv.getDevice() << "\n";
     292              : 
     293            0 :          for(elementMapIteratorT elIt = knownElements.begin(); elIt != knownElements.end();)
     294              :          {
     295            0 :             if( elIt->second.device == ipRecv.getDevice()) elIt = knownElements.erase(elIt);
     296            0 :             else ++elIt;
     297              :          }
     298              : 
     299            0 :          for(propMapIteratorT pIt = knownProps.begin(); pIt != knownProps.end();)
     300              :          {
     301            0 :             if( pIt->first == ipRecv.createUniqueKey() ) pIt = knownProps.erase(pIt);
     302            0 :             else ++pIt;
     303              :          }
     304              :       }
     305              :       else
     306              :       {
     307              :          //if(fpout) *fpout << "will delete: " << ipRecv.createUniqueKey() << "\n";
     308              : 
     309            0 :          for(elementMapIteratorT elIt = knownElements.begin(); elIt != knownElements.end();)
     310              :          {
     311            0 :             if( elIt->second.propKey == ipRecv.createUniqueKey()) elIt = knownElements.erase(elIt);
     312            0 :             else ++elIt;
     313              :          }
     314              : 
     315            0 :          knownProps.erase(ipRecv.createUniqueKey());
     316              : 
     317              :       }
     318              :    }
     319              : 
     320            0 :    ++m_redraw;
     321              : 
     322            0 : }
     323              : 
     324            0 : void cursesINDI::handleMessage( const pcf::IndiProperty &ipRecv )
     325              : {
     326              :    tm bdt; //broken down time
     327            0 :    time_t tt = ipRecv.getTimeStamp().getTimeVal().tv_sec;
     328            0 :    gmtime_r( &tt, &bdt);
     329              : 
     330              :    char tstr1[25];
     331            0 :    strftime(tstr1, sizeof(tstr1), "%H:%M:%S", &bdt);
     332              :    char tstr2[11];
     333            0 :    snprintf(tstr2, sizeof(tstr2), ".%06i", static_cast<int>(ipRecv.getTimeStamp().getTimeVal().tv_usec)); //casting in case we switch to int64_t
     334              : 
     335              : 
     336            0 :    std::string msg = ipRecv.getMessage();
     337              : 
     338            0 :    if(msg.size() > 4)
     339              :    {
     340            0 :       std::string prio = msg.substr(0,4);
     341            0 :       if(prio == "NOTE")
     342              :       {
     343            0 :          m_msgout << "\033[1m";
     344              :       }
     345            0 :       else if(prio == "CAUT") //given by stateRuleEngine
     346              :       {
     347            0 :          m_msgout << "\033[93m\033[1m";
     348              :       }
     349            0 :       else if(prio == "WARN")
     350              :       {
     351            0 :          m_msgout << "\033[93m\033[1m";
     352              :       }
     353            0 :       else if(prio == "ERR ")
     354              :       {
     355            0 :          m_msgout << "\033[91m\033[1m";
     356              :       }
     357            0 :       else if(prio == "CRIT")
     358              :       {
     359            0 :          m_msgout << "\033[41m\033[1m";
     360              :       }
     361            0 :       else if(prio == "ALRT" || prio == "ALER")
     362              :       {
     363            0 :          m_msgout << "\033[41m\033[1m";
     364              :       }
     365            0 :       else if(prio == "EMER")
     366              :       {
     367            0 :          m_msgout << "\033[41m\033[1m";
     368              :       }
     369            0 :    }
     370            0 :    m_msgout << std::string(tstr1) << std::string(tstr2) << " [" << ipRecv.getDevice() << "] " << msg;
     371              : 
     372            0 :    m_msgout << "\033[0m";
     373            0 :    m_msgout << std::endl;
     374              : 
     375              : 
     376            0 :    ++m_msgsPrinted;
     377            0 :    if(m_msgsPrinted > m_msgsMax)
     378              :    {
     379            0 :       m_msgout.close();
     380            0 :       m_msgout.open(m_msgFile);
     381            0 :       m_msgsPrinted = 0;
     382              :    }
     383            0 : }
     384              : 
     385            0 : void cursesINDI::handleSetProperty( const pcf::IndiProperty &ipRecv )
     386              : {
     387            0 :    handleDefProperty(ipRecv);
     388            0 : }
     389              : 
     390              : 
     391            0 : void cursesINDI::execute()
     392              : {
     393            0 :    processIndiRequests(false);
     394            0 : }
     395              : 
     396            0 : void cursesINDI::cursStat(int cs)
     397              : {
     398            0 :    m_cursStat = cs;
     399            0 :    curs_set(m_cursStat);
     400              : 
     401            0 : }
     402              : 
     403            0 : int cursesINDI::cursStat()
     404              : {
     405            0 :    return m_cursStat;
     406              : }
     407              : 
     408            0 : void cursesINDI::startUp()
     409              : {
     410            0 :    if(w_interactWin == nullptr)
     411              :    {
     412            0 :       w_interactWin = newwin( 1, m_minWidth, m_yTop-3, m_xLeft);
     413              :    }
     414              : 
     415            0 :    if(w_curvalWin == nullptr)
     416              :    {
     417            0 :       w_curvalWin = newwin( 1, m_minWidth, m_yTop-2, m_xLeft);
     418              :    }
     419              : 
     420            0 :    if(w_attentionWin == nullptr)
     421              :    {
     422            0 :       w_attentionWin = newwin( 1, m_minWidth, m_yTop-4, m_xLeft);
     423              :    }
     424              : 
     425            0 :    keypad(w_interactWin, TRUE);
     426              : 
     427              : 
     428            0 :    m_shutdown = false;
     429            0 :    drawThreadStart();
     430            0 : }
     431              : 
     432            0 : void cursesINDI::shutDown()
     433              : {
     434            0 :    if(getQuitProcess() && !m_shutdown) m_connectionLost = true;
     435              : 
     436            0 :    m_shutdown = true;
     437              : 
     438            0 :    quitProcess();
     439            0 :    deactivate();
     440              : 
     441            0 :    m_drawThread.join();
     442              : 
     443            0 :    m_cellContents.clear();
     444              : 
     445            0 :    if(w_interactWin) delwin(w_interactWin);
     446            0 :    w_interactWin = nullptr;
     447              : 
     448            0 :    if(w_countWin) delwin(w_countWin);
     449            0 :    w_countWin = nullptr;
     450              : 
     451            0 : }
     452              : 
     453              : inline
     454            0 : void cursesINDI::_drawThreadStart( cursesINDI * c)
     455              : {
     456            0 :    c->drawThreadExec();
     457            0 : }
     458              : 
     459              : inline
     460            0 : int cursesINDI::drawThreadStart()
     461              : {
     462              :    try
     463              :    {
     464            0 :       m_drawThread = std::thread( _drawThreadStart, this);
     465              :    }
     466            0 :    catch( const std::exception & e )
     467              :    {
     468            0 :       return -1;
     469            0 :    }
     470            0 :    catch( ... )
     471              :    {
     472            0 :       return -1;
     473            0 :    }
     474              : 
     475            0 :    if(!m_drawThread.joinable())
     476              :    {
     477            0 :       return -1;
     478              :    }
     479              : 
     480            0 :    return 0;
     481              : }
     482              : 
     483              : inline
     484            0 : void cursesINDI::drawThreadExec()
     485              : {
     486            0 :    while(!m_shutdown && !getQuitProcess())
     487              :    {
     488              :       ////if(fpout) *fpout << "draw thread . . ." << std::endl;
     489            0 :       if(m_redraw > 0)
     490              :       {
     491            0 :          redrawTable();
     492              :       }
     493              : 
     494            0 :       if(m_update > 0)
     495              :       {
     496            0 :          updateTable();
     497              :       }
     498              : 
     499            0 :       std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::nano>(250000000));
     500              :    }
     501              : 
     502            0 :    if(getQuitProcess() && !m_shutdown)
     503              :    {
     504            0 :       m_connectionLost = true;
     505              : 
     506            0 :       m_cellContents.clear();
     507            0 :       redrawTable();
     508            0 :       m_shutdown = true;
     509              :    }
     510              : 
     511            0 : }
     512              : 
     513            0 : void cursesINDI::redrawTable()
     514              : {
     515            0 :    std::lock_guard<std::mutex> lock(m_drawMutex);
     516              : 
     517            0 :    int start_redraw = m_redraw;
     518              : 
     519              :    //if(fpout) *fpout << "redrawTable: " << m_redraw << std::endl;
     520              : 
     521            0 :    m_cellContents.clear();
     522              : 
     523            0 :    bool fsmAlerts = false;
     524              : 
     525            0 :    std::set<std::string> alertDev;
     526              : 
     527            0 :    for( elementMapIteratorT es = knownElements.begin(); es != knownElements.end(); ++es)
     528              :    {
     529              :       //if(fpout) *fpout << knownProps[es->second.propKey].getName() << " " << knownProps[es->second.propKey].getState() << "\n";
     530              : 
     531            0 :       if(knownProps[es->second.propKey].getName() == "fsm" &&
     532            0 :             knownProps[es->second.propKey].getState() == pcf::IndiProperty::Alert)
     533              :       {
     534            0 :          fsmAlerts = true;
     535            0 :          alertDev.insert(knownProps[es->second.propKey].getDevice());
     536              :       }
     537            0 :       std::vector<std::string> s;
     538              : 
     539            0 :       s.resize( m_colFraction.size() );
     540              : 
     541            0 :       s[0] = std::to_string(m_cellContents.size()+1);
     542            0 :       s[1] = knownProps[es->second.propKey].getDevice();
     543            0 :       s[2] = displayProperty( knownProps[es->second.propKey] );
     544              : 
     545            0 :       s[3] = es->second.name;
     546              : 
     547            0 :       s[4] = displayValue( knownProps[es->second.propKey], es->second.name);
     548              : 
     549            0 :       m_cellContents.push_back(s);
     550            0 :       es->second.tableRow = m_cellContents.size()-1;
     551            0 :    }
     552              : 
     553            0 :    draw();
     554              : 
     555              :    //if(fpout) *fpout << "fsmAlerts: " << fsmAlerts << "\n";
     556              : 
     557              :    int cx, cy;
     558            0 :    getyx(w_interactWin, cy, cx);
     559            0 :    int cs = cursStat();
     560            0 :    cursStat(0);
     561              : 
     562            0 :    wclear(w_attentionWin);
     563            0 :    if(fsmAlerts)
     564              :    {
     565            0 :       std::string alrt = "!! FSM alert: " + *alertDev.begin();
     566            0 :       if(alertDev.size() > 1) alrt += " (+" + std::to_string(alertDev.size()-1) + ")";
     567              : 
     568            0 :       wprintw(w_attentionWin, "%s", alrt.c_str());
     569            0 :    }
     570            0 :    wrefresh(w_attentionWin);
     571              : 
     572            0 :    wmove(w_interactWin,cy,cx);
     573            0 :    cursStat(cs);
     574            0 :    wrefresh(w_interactWin);
     575              : 
     576            0 :    m_redraw -= start_redraw;
     577            0 :    if(m_redraw <0) m_redraw = 0;
     578              : 
     579            0 :    _moveCurrent(m_currY, m_currX);
     580            0 : }
     581              : 
     582              : 
     583              : 
     584            0 : void cursesINDI::updateTable()
     585              : {
     586            0 :    if(m_redraw) return; //Pending redraw, so we skip it and let that take care of it.
     587              : 
     588            0 :    std::lock_guard<std::mutex> lock(m_drawMutex);
     589              : 
     590            0 :    int start_update = m_update;
     591              : 
     592              :    //if(fpout) *fpout << "updateTable: " << m_update << std::endl;
     593              :    int cx, cy;
     594              : 
     595            0 :    getyx(w_interactWin, cy, cx);
     596            0 :    int cs = cursStat();
     597              : 
     598            0 :    updateCurVal();
     599              : 
     600            0 :    bool fsmAlerts {false};
     601            0 :    std::set<std::string> alertDev;
     602              : 
     603            0 :    for(auto it = knownElements.begin(); it != knownElements.end(); ++it)
     604              :    {
     605              :       //if(fpout) *fpout << knownProps[it->second.propKey].getName() << " " << knownProps[it->second.propKey].getState() << "\n";
     606              : 
     607            0 :       if(knownProps[it->second.propKey].getName() == "fsm" &&
     608            0 :             knownProps[it->second.propKey].getState() == pcf::IndiProperty::Alert)
     609              :       {
     610            0 :          fsmAlerts = true;
     611            0 :          alertDev.insert(knownProps[it->second.propKey].getDevice());
     612              :       }
     613              : 
     614            0 :       if(it->second.tableRow == -1) continue;
     615              : 
     616            0 :       if(m_cellContents[it->second.tableRow][2] != displayProperty(knownProps[it->second.propKey]) )
     617              :       {
     618            0 :          m_cellContents[it->second.tableRow][2] = displayProperty(knownProps[it->second.propKey]) ;
     619              : 
     620            0 :          if(it->second.tableRow - m_startRow < (size_t) tabHeight()) //It's currently displayed
     621              :          {
     622            0 :             cursStat(0);
     623            0 :             wclear(m_gridWin[it->second.tableRow - m_startRow][2]);
     624            0 :             if(hasContent(it->second.tableRow,2)) wprintw(m_gridWin[it->second.tableRow - m_startRow][2], "%s", m_cellContents[it->second.tableRow][2].c_str());
     625            0 :             wrefresh(m_gridWin[it->second.tableRow - m_startRow][2]);
     626            0 :             wmove(w_interactWin,cy,cx);
     627            0 :             cursStat(cs);
     628            0 :             wrefresh(w_interactWin);
     629              :          }
     630              :       }
     631              : 
     632            0 :       if(m_cellContents[it->second.tableRow][4] != displayValue(knownProps[it->second.propKey], it->second.name)) //.getValue())
     633              :       {
     634            0 :          m_cellContents[it->second.tableRow][4] = displayValue(knownProps[it->second.propKey], it->second.name); //knownProps[it->second.propKey][it->second.name].getValue();
     635              : 
     636            0 :          if(it->second.tableRow - m_startRow < (size_t) tabHeight()) //It's currently displayed
     637              :          {
     638            0 :             cursStat(0);
     639            0 :             wclear(m_gridWin[it->second.tableRow - m_startRow][4]);
     640            0 :             if(hasContent(it->second.tableRow,4)) wprintw(m_gridWin[it->second.tableRow - m_startRow][4], "%s", m_cellContents[it->second.tableRow][4].c_str());
     641            0 :             wrefresh(m_gridWin[it->second.tableRow - m_startRow][4]);
     642            0 :             wmove(w_interactWin,cy,cx);
     643            0 :             cursStat(cs);
     644            0 :             wrefresh(w_interactWin);
     645              :          }
     646              : 
     647              :       };
     648              :       //updateContents( it->second.tableRow, 4,  knownProps[it->second.propKey][it->second.name].getValue());
     649              :    }
     650              : 
     651            0 :    wattron(m_gridWin[m_currY][m_currX], A_REVERSE);
     652              : 
     653              :    //if(fpout) *fpout << "fsmAlerts: " << fsmAlerts << "\n";
     654              : 
     655            0 :    getyx(w_interactWin, cy, cx);
     656            0 :    cs = cursStat();
     657            0 :    cursStat(0);
     658            0 :    wclear(w_attentionWin);
     659            0 :    if(fsmAlerts)
     660              :    {
     661            0 :       std::string alrt = "!! FSM alert: " + *alertDev.begin();
     662            0 :       if(alertDev.size() > 1) alrt += " (+" + std::to_string(alertDev.size()-1) + ")";
     663              : 
     664            0 :       wprintw(w_attentionWin, "%s", alrt.c_str());
     665            0 :    }
     666            0 :    wrefresh(w_attentionWin);
     667            0 :    wmove(w_interactWin,cy,cx);
     668            0 :    cursStat(cs);
     669            0 :    wrefresh(w_interactWin);
     670              : 
     671              :    //print();
     672              : 
     673              : //    wmove(w_interactWin,cy,cx);
     674              : //    cursStat(cs);
     675              : //    wrefresh(w_interactWin);
     676              : 
     677            0 :    m_update -= start_update;
     678            0 :    if(m_update <0) m_update = 0;
     679            0 : }
     680              : 
     681            0 : void cursesINDI::updateCurVal( )
     682              : {
     683              :    int cx, cy;
     684            0 :    getyx(w_interactWin, cy, cx);
     685            0 :    int cs = cursStat();
     686              : 
     687            0 :    cursStat(0);
     688            0 :    wclear(w_curvalWin);
     689              : 
     690            0 :    auto it = knownElements.begin();
     691            0 :    while(it != knownElements.end())
     692              :    {
     693            0 :       if( (size_t) it->second.tableRow == m_currY+m_startRow) break;
     694            0 :       ++it;
     695              :    }
     696              : 
     697            0 :    if(it == knownElements.end())
     698              :    {
     699            0 :       wrefresh(w_interactWin);
     700            0 :       cursStat(cs);
     701            0 :       return;
     702              :    }
     703              : 
     704            0 :    std::string cval = "> " + it->second.propKey + "." + it->second.name + " = ";
     705              : 
     706            0 :    cval += displayValue( knownProps[it->second.propKey], it->second.name);
     707              : 
     708            0 :    wprintw(w_curvalWin, "%s", cval.c_str());
     709            0 :    wrefresh(w_curvalWin);
     710              : 
     711            0 :    wmove(w_interactWin,cy,cx);
     712            0 :    cursStat(cs);
     713            0 :    wrefresh(w_interactWin);
     714              : 
     715            0 :    return;
     716            0 : }
     717              : 
     718            0 : void cursesINDI::moveCurrent( int nextY,
     719              :                               int nextX
     720              :                             )
     721              : {
     722            0 :    std::lock_guard<std::mutex> lock(m_drawMutex);
     723              : 
     724            0 :    _moveCurrent(nextY, nextX);
     725            0 : }
     726              : 
     727            0 : void cursesINDI::_moveCurrent( int nextY,
     728              :                                int nextX
     729              :                              )
     730              : {
     731            0 :    int currX = m_currX;
     732            0 :    int currY = m_currY;
     733              : 
     734            0 :    moveSelected(nextY, nextX);
     735              : 
     736              :    //if(fpout) *fpout << "moved: " << nextX << " " << nextY << " " << currX << "->" << m_currX << " " << currY << "->" << m_currY << std::endl;
     737              : 
     738            0 :    if(m_deviceSearching && nextX != 1)
     739              :    {
     740            0 :       wclear(w_interactWin);
     741            0 :       wrefresh(w_interactWin);
     742            0 :       m_deviceSearching = false;
     743              :    }
     744              : 
     745            0 :    updateCurVal();
     746              : 
     747            0 :    if(nextX == 1)
     748              :    {
     749            0 :       if( currX != nextX)
     750              :       {
     751            0 :          wclear(w_interactWin);
     752            0 :          wprintw(w_interactWin, "search: ");
     753            0 :          wrefresh(w_interactWin);
     754              :       }
     755              :    }
     756            0 :    else if(currY != nextY || currX == 1)
     757              :    {
     758            0 :       wclear(w_interactWin);
     759            0 :       if(m_currY + m_startRow >= knownElements.size())
     760              :       {
     761            0 :          wrefresh(w_interactWin);
     762            0 :          return;
     763              :       }
     764              : 
     765            0 :       auto it = knownElements.begin();
     766            0 :       while(it != knownElements.end())
     767              :       {
     768            0 :          if( (size_t) it->second.tableRow == m_currY+m_startRow) break;
     769            0 :          ++it;
     770              :       }
     771              : 
     772            0 :       if(it == knownElements.end())
     773              :       {
     774            0 :          wrefresh(w_interactWin);
     775            0 :          return;
     776              :       }
     777              : 
     778            0 :       if( knownProps[it->second.propKey].getPerm() != pcf::IndiProperty::ReadWrite)
     779              :       {
     780            0 :          wrefresh(w_interactWin);
     781            0 :          return;
     782              :       }
     783              : 
     784            0 :       if( knownProps[it->second.propKey].getType() == pcf::IndiProperty::Text)
     785              :       {
     786            0 :          wprintw(w_interactWin, "(e)dit this text");
     787              :       }
     788            0 :       else if( knownProps[it->second.propKey].getType() == pcf::IndiProperty::Number)
     789              :       {
     790            0 :          wprintw(w_interactWin, "(e)dit this number");
     791              :       }
     792            0 :       else if( knownProps[it->second.propKey].getType() == pcf::IndiProperty::Switch)
     793              :       {
     794            0 :          wprintw(w_interactWin, "(p)ress or (t)oggle this switch");
     795              :       }
     796              : 
     797              : 
     798            0 :       wrefresh(w_interactWin);
     799              :    }
     800              : 
     801              : }
     802              : 
     803              : 
     804            0 : void cursesINDI::deviceSearch( int ch )
     805              : {
     806            0 :    bool updated = false;
     807            0 :    if( m_deviceSearching == true )
     808              :    {
     809              : 
     810              :       // CTRL+L clears search bar
     811            0 :       if ( (ch == (0x1f & 'l')) && (m_deviceTarget.size() > 0) )
     812              :       {
     813            0 :          std::lock_guard<std::mutex> lock(m_drawMutex);
     814            0 :          m_deviceTarget = "search: ";
     815            0 :          wclear(w_interactWin);
     816            0 :          wprintw(w_interactWin, "%s", m_deviceTarget.c_str());
     817            0 :          wrefresh(w_interactWin);
     818            0 :          m_deviceSearching = false;
     819            0 :          return;
     820            0 :       }
     821            0 :       else if( (ch == KEY_BACKSPACE) && (m_deviceTarget.size() > 0) )
     822              :       {
     823            0 :          std::lock_guard<std::mutex> lock(m_drawMutex);
     824            0 :          m_deviceTarget.erase(m_deviceTarget.size()-1,1);
     825            0 :          wprintw(w_interactWin, "\b \b");
     826            0 :          wrefresh(w_interactWin);
     827            0 :          return;
     828            0 :       }
     829            0 :       else if (std::isprint(ch))
     830              :       {
     831            0 :          m_deviceTarget += ch;
     832            0 :          updated = true;
     833              :       }
     834            0 :       else return;
     835              :    }
     836            0 :    else if (std::isprint(ch))
     837              :    {
     838            0 :       m_deviceTarget = ch;
     839            0 :       updated = true;
     840              :    }
     841            0 :    else return;
     842              : 
     843              : 
     844            0 :    m_deviceSearching = true;
     845              : 
     846            0 :    if(updated)
     847              :    {
     848            0 :       std::lock_guard<std::mutex> lock(m_drawMutex);
     849            0 :       wprintw(w_interactWin, "%c", ch);
     850            0 :       wrefresh(w_interactWin);
     851            0 :    }
     852              : 
     853              :    //if(fpout) *fpout << "device searching: " << m_deviceTarget << std::endl;
     854            0 :    if(m_deviceTarget.size() == 0) return;
     855              : 
     856            0 :    auto it = knownElements.lower_bound(m_deviceTarget);
     857              : 
     858              :    //if(fpout) *fpout << "new row: " << it->second.tableRow << " " << m_startRow << " " << it->second.tableRow-m_startRow << "\n";
     859              : 
     860            0 :    if(it->second.tableRow == -1) return;
     861              : 
     862            0 :    m_startRow = it->second.tableRow;
     863              : 
     864            0 :    moveCurrent( 0, 1);
     865              : 
     866            0 :    redrawTable();
     867              : 
     868            0 :    return;
     869              : 
     870              : }
     871              : 
     872            0 : void cursesINDI::keyPressed( int ch )
     873              : {
     874              : 
     875              :    //If in first column, do device selection
     876            0 :    if(m_currX == 1 )
     877              :    {
     878            0 :       deviceSearch(ch);
     879            0 :       return;
     880              :    }
     881              : 
     882            0 :    if(m_deviceSearching)
     883              :    {
     884            0 :       wclear(w_interactWin);
     885            0 :       wrefresh(w_interactWin);
     886            0 :       m_deviceSearching = false;
     887              :    }
     888              : 
     889            0 :    switch(ch)
     890              :    {
     891            0 :       case 'e':
     892              :       {
     893            0 :          if(m_currY + m_startRow >= knownElements.size()) break;
     894            0 :          auto it = knownElements.begin();
     895            0 :          while(it != knownElements.end())
     896              :          {
     897            0 :             if( (size_t) it->second.tableRow == m_currY+m_startRow) break;
     898            0 :             ++it;
     899              :          }
     900              : 
     901            0 :          if(it == knownElements.end()) break;
     902              : 
     903              :          //Can't edit a switch
     904            0 :          if( knownProps[it->second.propKey].getType() != pcf::IndiProperty::Text && knownProps[it->second.propKey].getType() != pcf::IndiProperty::Number) break;
     905              : 
     906            0 :          cursStat(1);
     907              : 
     908              :          //mutex scope
     909              :          {
     910            0 :             std::lock_guard<std::mutex> lock(m_drawMutex);
     911            0 :             wclear(w_interactWin);
     912            0 :             wprintw(w_interactWin, "set %s.%s=", it->second.propKey.c_str(), it->second.name.c_str());
     913            0 :             wrefresh(w_interactWin);
     914            0 :          }
     915              : 
     916            0 :          bool escape = false;
     917            0 :          std::string newStr;
     918              :          int nch;
     919            0 :          while( (nch = wgetch(w_interactWin)) != '\n')
     920              :          {
     921            0 :             if(nch == ERR)
     922              :             {
     923            0 :                if( getQuitProcess())
     924              :                {
     925              :                   //If the IndiConnection has set 'quitProces' but no other shutdown
     926              :                   //has been issued then we record this as a lost connection.
     927            0 :                   if(!m_shutdown) m_connectionLost = true;
     928            0 :                   break;
     929              :                }
     930            0 :                else continue;
     931              :             }
     932            0 :             else if(nch == 3)
     933              :             {
     934            0 :                m_shutdown = true;
     935            0 :                break;
     936              :             }
     937              : 
     938            0 :             cursStat(1);
     939              : 
     940            0 :             if(nch == 27)
     941              :             {
     942            0 :                std::lock_guard<std::mutex> lock(m_drawMutex);
     943            0 :                wclear(w_interactWin);
     944            0 :                wrefresh(w_interactWin);
     945            0 :                escape = true;
     946            0 :                break;
     947            0 :             }
     948            0 :             if( nch == KEY_BACKSPACE )
     949              :             {
     950            0 :                if(newStr.size() > 0)
     951              :                {
     952            0 :                   std::lock_guard<std::mutex> lock(m_drawMutex);
     953            0 :                   newStr.erase(newStr.size()-1,1);
     954            0 :                   wprintw(w_interactWin, "\b \b");
     955            0 :                   wrefresh(w_interactWin);
     956            0 :                }
     957              :             }
     958            0 :             else if (std::isprint(nch))
     959              :             {
     960            0 :                std::lock_guard<std::mutex> lock(m_drawMutex);
     961            0 :                wprintw(w_interactWin, "%c", nch);
     962            0 :                wrefresh(w_interactWin);
     963              : 
     964            0 :                newStr += (char) nch;
     965            0 :             }
     966              :          }
     967            0 :          if(escape) break;
     968            0 :          if(m_shutdown) break;
     969              : 
     970              :          //mutex scope
     971              :          {
     972            0 :             std::lock_guard<std::mutex> lock(m_drawMutex);
     973            0 :             wclear(w_interactWin);
     974            0 :             wprintw(w_interactWin, "send %s.%s=%s? y/n [n]", it->second.propKey.c_str(), it->second.name.c_str(), newStr.c_str());
     975            0 :             wrefresh(w_interactWin);
     976            0 :          }
     977              : 
     978            0 :          nch = 0;
     979            0 :          while( (nch = wgetch(w_interactWin)) == ERR)
     980              :          {
     981              :          }
     982              : 
     983            0 :          if(nch == 3)
     984              :          {
     985            0 :             m_shutdown = true;
     986            0 :             break;
     987              :          }
     988              : 
     989            0 :          if(nch == 'y')
     990              :          {
     991            0 :             pcf::IndiProperty ipSend(knownProps[it->second.propKey].getType());
     992              : 
     993            0 :             ipSend.setDevice(knownProps[it->second.propKey].getDevice());
     994            0 :             ipSend.setName(knownProps[it->second.propKey].getName());
     995            0 :             ipSend.add(pcf::IndiElement(it->second.name));
     996              :             //if(fpout) *fpout << "newStr: " << newStr << std::endl;
     997              : 
     998            0 :             ipSend[it->second.name].setValue(newStr);
     999            0 :             sendNewProperty(ipSend);
    1000            0 :          }
    1001              : 
    1002              :          //mutex scope
    1003              :          {
    1004            0 :             std::lock_guard<std::mutex> lock(m_drawMutex);
    1005            0 :             wclear(w_interactWin);
    1006            0 :             wrefresh(w_interactWin);
    1007            0 :          }
    1008              : 
    1009            0 :          break;
    1010            0 :       } //case 'e'
    1011            0 :       case 't':
    1012              :       {
    1013            0 :          if(m_currY + m_startRow >= knownElements.size()) break;
    1014            0 :          auto it = knownElements.begin();
    1015            0 :          while(it != knownElements.end())
    1016              :          {
    1017            0 :             if( (size_t) it->second.tableRow == m_currY+m_startRow) break;
    1018            0 :             ++it;
    1019              :          }
    1020              : 
    1021            0 :          if(it == knownElements.end()) break;
    1022              : 
    1023            0 :          if( !knownProps[it->second.propKey].find(it->second.name)) break; //Just a check.
    1024              : 
    1025            0 :          if( knownProps[it->second.propKey].getType() != pcf::IndiProperty::Switch) break;
    1026              : 
    1027            0 :          std::string toggleString;
    1028              :          pcf::IndiElement::SwitchStateType toggleState;
    1029            0 :          if( knownProps[it->second.propKey][it->second.name].getSwitchState() == pcf::IndiElement::Off  )
    1030              :          {
    1031            0 :             toggleString = "On";
    1032            0 :             toggleState = pcf::IndiElement::On;
    1033              :          }
    1034            0 :          else if(knownProps[it->second.propKey][it->second.name].getSwitchState() == pcf::IndiElement::On)
    1035              :          {
    1036            0 :             toggleString = "Off";
    1037            0 :             toggleState = pcf::IndiElement::Off;
    1038              :          }
    1039            0 :          else break; //would happen fo state unknown
    1040              : 
    1041            0 :          cursStat(1);
    1042              : 
    1043              :          //mutex scope
    1044              :          {
    1045            0 :             std::lock_guard<std::mutex> lock(m_drawMutex);
    1046            0 :             wclear(w_interactWin);
    1047            0 :             wprintw(w_interactWin, "toggle %s.%s to %s?", it->second.propKey.c_str(), it->second.name.c_str(), toggleString.c_str());
    1048            0 :             wrefresh(w_interactWin);
    1049            0 :          }
    1050              : 
    1051            0 :          int nch = 0;
    1052            0 :          while( (nch = wgetch(w_interactWin)) == ERR)
    1053              :          {
    1054              :          }
    1055              : 
    1056            0 :          if(nch == 3)
    1057              :          {
    1058            0 :             m_shutdown = true;
    1059            0 :             break;
    1060              :          }
    1061              : 
    1062            0 :          if(nch == 'y')
    1063              :          {
    1064            0 :             pcf::IndiProperty ipSend(knownProps[it->second.propKey].getType());
    1065              : 
    1066            0 :             ipSend.setDevice(knownProps[it->second.propKey].getDevice());
    1067            0 :             ipSend.setName(knownProps[it->second.propKey].getName());
    1068            0 :             ipSend.add(pcf::IndiElement(it->second.name));
    1069            0 :             ipSend[it->second.name].setSwitchState(toggleState);
    1070            0 :             sendNewProperty(ipSend);
    1071            0 :          }
    1072              : 
    1073              :          //mutex scope
    1074              :          {
    1075            0 :             std::lock_guard<std::mutex> lock(m_drawMutex);
    1076            0 :             wclear(w_interactWin);
    1077            0 :             wrefresh(w_interactWin);
    1078            0 :          }
    1079              : 
    1080            0 :          break;
    1081            0 :       } //case 't'
    1082            0 :       case 'p':
    1083              :       {
    1084            0 :          if(m_currY + m_startRow >= knownElements.size()) break;
    1085            0 :          auto it = knownElements.begin();
    1086            0 :          while(it != knownElements.end())
    1087              :          {
    1088            0 :             if( (size_t) it->second.tableRow == m_currY+m_startRow) break;
    1089            0 :             ++it;
    1090              :          }
    1091              : 
    1092            0 :          if(it == knownElements.end()) break;
    1093              : 
    1094            0 :          if( !knownProps[it->second.propKey].find(it->second.name)) break; //Just a check.
    1095              : 
    1096            0 :          if( knownProps[it->second.propKey].getType() != pcf::IndiProperty::Switch) break;
    1097            0 :          cursStat(1);
    1098              : 
    1099              :          //mutex scope
    1100              :          {
    1101            0 :             std::lock_guard<std::mutex> lock(m_drawMutex);
    1102            0 :             wclear(w_interactWin);
    1103            0 :             wprintw(w_interactWin, "press switch %s.%s?", it->second.propKey.c_str(), it->second.name.c_str());
    1104            0 :             wrefresh(w_interactWin);
    1105            0 :          }
    1106              : 
    1107            0 :          int nch = 0;
    1108            0 :          while( (nch = wgetch(w_interactWin)) == ERR)
    1109              :          {
    1110              :          }
    1111              : 
    1112            0 :          if(nch == 3)
    1113              :          {
    1114            0 :             m_shutdown = true;
    1115            0 :             break;
    1116              :          }
    1117              : 
    1118            0 :          if(nch == 'y')
    1119              :          {
    1120            0 :             pcf::IndiProperty ipSend(knownProps[it->second.propKey].getType());
    1121              : 
    1122            0 :             ipSend.setDevice(knownProps[it->second.propKey].getDevice());
    1123            0 :             ipSend.setName(knownProps[it->second.propKey].getName());
    1124              : 
    1125              :             //Must add all elements
    1126            0 :             for(auto elit = knownProps[it->second.propKey].getElements().begin(); elit != knownProps[it->second.propKey].getElements().end(); ++elit)
    1127              :             {
    1128            0 :                ipSend.add(elit->second);
    1129            0 :                if( knownProps[it->second.propKey].getRule() != pcf::IndiProperty::AnyOfMany)
    1130              :                {
    1131            0 :                   ipSend[elit->first].setSwitchState(pcf::IndiElement::Off);
    1132              :                }
    1133              :             }
    1134              : 
    1135            0 :             ipSend[it->second.name].setSwitchState(pcf::IndiElement::On);
    1136            0 :             sendNewProperty(ipSend);
    1137            0 :          }
    1138              : 
    1139              :          //mutex scope
    1140              :          {
    1141            0 :             std::lock_guard<std::mutex> lock(m_drawMutex);
    1142            0 :             wclear(w_interactWin);
    1143            0 :             wrefresh(w_interactWin);
    1144            0 :          }
    1145              : 
    1146            0 :          break;
    1147              :       } //case 'p'
    1148            0 :       default:
    1149            0 :          return;//break;
    1150              :    }
    1151              : 
    1152            0 :    std::lock_guard<std::mutex> lock(m_drawMutex);
    1153            0 :    cursStat(0);
    1154            0 :    wrefresh(w_interactWin);
    1155              : 
    1156              : }
        

Generated by: LCOV version 2.0-1