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 : }
|