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 : }
216 :
217 0 : cursesINDI::~cursesINDI()
218 : {
219 0 : if( w_interactWin ) delwin(w_interactWin);
220 0 : if( w_curvalWin ) delwin(w_curvalWin);
221 0 : if( w_attentionWin ) delwin(w_attentionWin);
222 0 : if( w_countWin) delwin(w_countWin);
223 0 : }
224 :
225 0 : void cursesINDI::handleDefProperty( const pcf::IndiProperty &ipRecv )
226 : {
227 0 : if(!ipRecv.hasValidDevice() && !ipRecv.hasValidName())
228 : {
229 0 : return;
230 : }
231 :
232 0 : std::pair<propMapIteratorT, bool> result;
233 :
234 0 : std::lock_guard<std::mutex> lock(m_drawMutex);
235 :
236 0 : result = knownProps.insert(propMapValueT( ipRecv.createUniqueKey(), ipRecv ));
237 :
238 :
239 0 : if(result.second == false)
240 : {
241 0 : result.first->second = ipRecv; //We already have it, so we're already registered
242 : }
243 : else
244 : {
245 0 : sendGetProperties(ipRecv); //Otherwise register for it
246 : }
247 :
248 0 : auto elIt = ipRecv.getElements().begin();
249 :
250 0 : while(elIt != ipRecv.getElements().end())
251 : {
252 0 : elementSpec es;
253 0 : es.propKey = ipRecv.createUniqueKey();
254 0 : es.device = ipRecv.getDevice();
255 0 : es.propertyName = ipRecv.getName();
256 0 : es.name = elIt->second.getName();
257 :
258 0 : std::string key = es.propKey + "." + es.name;
259 0 : auto elResult = knownElements.insert( elementMapValueT(key, es));
260 :
261 0 : if(elResult.second == true)
262 : {
263 : //If result is new insert, add to TUI table if filter requires
264 0 : ++m_redraw;
265 : }
266 : else
267 : {
268 : //Or just update the table.
269 : //Should check if this element actually changed....
270 0 : ++m_update;
271 : }
272 0 : ++elIt;
273 0 : }
274 :
275 0 : }
276 :
277 0 : void cursesINDI::handleDelProperty( const pcf::IndiProperty &ipRecv )
278 : {
279 : //if(fpout) *fpout << "got delete property" << std::endl;
280 :
281 0 : if(ipRecv.hasValidDevice())
282 : {
283 0 : if(!ipRecv.hasValidName())
284 : {
285 : //if(fpout) *fpout << "will delete: " << ipRecv.getDevice() << "\n";
286 :
287 0 : for(elementMapIteratorT elIt = knownElements.begin(); elIt != knownElements.end();)
288 : {
289 0 : if( elIt->second.device == ipRecv.getDevice()) elIt = knownElements.erase(elIt);
290 0 : else ++elIt;
291 : }
292 :
293 0 : for(propMapIteratorT pIt = knownProps.begin(); pIt != knownProps.end();)
294 : {
295 0 : if( pIt->first == ipRecv.createUniqueKey() ) pIt = knownProps.erase(pIt);
296 0 : else ++pIt;
297 : }
298 : }
299 : else
300 : {
301 : //if(fpout) *fpout << "will delete: " << ipRecv.createUniqueKey() << "\n";
302 :
303 0 : for(elementMapIteratorT elIt = knownElements.begin(); elIt != knownElements.end();)
304 : {
305 0 : if( elIt->second.propKey == ipRecv.createUniqueKey()) elIt = knownElements.erase(elIt);
306 0 : else ++elIt;
307 : }
308 :
309 0 : knownProps.erase(ipRecv.createUniqueKey());
310 :
311 : }
312 : }
313 :
314 0 : ++m_redraw;
315 :
316 0 : }
317 :
318 0 : void cursesINDI::handleMessage( const pcf::IndiProperty &ipRecv )
319 : {
320 : tm bdt; //broken down time
321 0 : time_t tt = ipRecv.getTimeStamp().getTimeVal().tv_sec;
322 0 : gmtime_r( &tt, &bdt);
323 :
324 : char tstr1[25];
325 0 : strftime(tstr1, sizeof(tstr1), "%H:%M:%S", &bdt);
326 : char tstr2[11];
327 0 : snprintf(tstr2, sizeof(tstr2), ".%06i", static_cast<int>(ipRecv.getTimeStamp().getTimeVal().tv_usec)); //casting in case we switch to int64_t
328 :
329 :
330 0 : std::string msg = ipRecv.getMessage();
331 :
332 0 : if(msg.size() > 4)
333 : {
334 0 : std::string prio = msg.substr(0,4);
335 0 : if(prio == "NOTE")
336 : {
337 0 : m_msgout << "\033[1m";
338 : }
339 0 : else if(prio == "WARN")
340 : {
341 0 : m_msgout << "\033[93m\033[1m";
342 : }
343 0 : else if(prio == "ERR ")
344 : {
345 0 : m_msgout << "\033[91m\033[1m";
346 : }
347 0 : else if(prio == "CRIT")
348 : {
349 0 : m_msgout << "\033[41m\033[1m";
350 : }
351 0 : else if(prio == "ALRT")
352 : {
353 0 : m_msgout << "\033[41m\033[1m";
354 : }
355 0 : else if(prio == "EMER")
356 : {
357 0 : m_msgout << "\033[41m\033[1m";
358 : }
359 0 : }
360 0 : m_msgout << std::string(tstr1) << std::string(tstr2) << " [" << ipRecv.getDevice() << "] " << msg;
361 :
362 0 : m_msgout << "\033[0m";
363 0 : m_msgout << std::endl;
364 :
365 :
366 0 : ++m_msgsPrinted;
367 0 : if(m_msgsPrinted > m_msgsMax)
368 : {
369 0 : m_msgout.close();
370 0 : m_msgout.open(m_msgFile);
371 0 : m_msgsPrinted = 0;
372 : }
373 0 : }
374 :
375 0 : void cursesINDI::handleSetProperty( const pcf::IndiProperty &ipRecv )
376 : {
377 0 : handleDefProperty(ipRecv);
378 0 : }
379 :
380 :
381 0 : void cursesINDI::execute()
382 : {
383 0 : processIndiRequests(false);
384 0 : }
385 :
386 0 : void cursesINDI::cursStat(int cs)
387 : {
388 0 : m_cursStat = cs;
389 0 : curs_set(m_cursStat);
390 :
391 0 : }
392 :
393 0 : int cursesINDI::cursStat()
394 : {
395 0 : return m_cursStat;
396 : }
397 :
398 0 : void cursesINDI::startUp()
399 : {
400 0 : if(w_interactWin == nullptr)
401 : {
402 0 : w_interactWin = newwin( 1, m_minWidth, m_yTop-3, m_xLeft);
403 : }
404 :
405 0 : if(w_curvalWin == nullptr)
406 : {
407 0 : w_curvalWin = newwin( 1, m_minWidth, m_yTop-2, m_xLeft);
408 : }
409 :
410 0 : if(w_attentionWin == nullptr)
411 : {
412 0 : w_attentionWin = newwin( 1, m_minWidth, m_yTop-4, m_xLeft);
413 : }
414 :
415 0 : keypad(w_interactWin, TRUE);
416 :
417 :
418 0 : m_shutdown = false;
419 0 : drawThreadStart();
420 0 : }
421 :
422 0 : void cursesINDI::shutDown()
423 : {
424 0 : if(getQuitProcess() && !m_shutdown) m_connectionLost = true;
425 :
426 0 : m_shutdown = true;
427 :
428 0 : quitProcess();
429 0 : deactivate();
430 :
431 0 : m_drawThread.join();
432 :
433 0 : m_cellContents.clear();
434 :
435 0 : if(w_interactWin) delwin(w_interactWin);
436 0 : w_interactWin = nullptr;
437 :
438 0 : if(w_countWin) delwin(w_countWin);
439 0 : w_countWin = nullptr;
440 :
441 0 : }
442 :
443 : inline
444 0 : void cursesINDI::_drawThreadStart( cursesINDI * c)
445 : {
446 0 : c->drawThreadExec();
447 0 : }
448 :
449 : inline
450 0 : int cursesINDI::drawThreadStart()
451 : {
452 : try
453 : {
454 0 : m_drawThread = std::thread( _drawThreadStart, this);
455 : }
456 0 : catch( const std::exception & e )
457 : {
458 0 : return -1;
459 0 : }
460 0 : catch( ... )
461 : {
462 0 : return -1;
463 0 : }
464 :
465 0 : if(!m_drawThread.joinable())
466 : {
467 0 : return -1;
468 : }
469 :
470 0 : return 0;
471 : }
472 :
473 : inline
474 0 : void cursesINDI::drawThreadExec()
475 : {
476 0 : while(!m_shutdown && !getQuitProcess())
477 : {
478 : ////if(fpout) *fpout << "draw thread . . ." << std::endl;
479 0 : if(m_redraw > 0)
480 : {
481 0 : redrawTable();
482 : }
483 :
484 0 : if(m_update > 0)
485 : {
486 0 : updateTable();
487 : }
488 :
489 0 : std::this_thread::sleep_for( std::chrono::duration<unsigned long, std::nano>(250000000));
490 : }
491 :
492 0 : if(getQuitProcess() && !m_shutdown)
493 : {
494 0 : m_connectionLost = true;
495 :
496 0 : m_cellContents.clear();
497 0 : redrawTable();
498 0 : m_shutdown = true;
499 : }
500 :
501 0 : }
502 :
503 0 : void cursesINDI::redrawTable()
504 : {
505 0 : std::lock_guard<std::mutex> lock(m_drawMutex);
506 :
507 0 : int start_redraw = m_redraw;
508 :
509 : //if(fpout) *fpout << "redrawTable: " << m_redraw << std::endl;
510 :
511 0 : m_cellContents.clear();
512 :
513 0 : bool fsmAlerts = false;
514 :
515 0 : std::set<std::string> alertDev;
516 :
517 0 : for( elementMapIteratorT es = knownElements.begin(); es != knownElements.end(); ++es)
518 : {
519 : //if(fpout) *fpout << knownProps[es->second.propKey].getName() << " " << knownProps[es->second.propKey].getState() << "\n";
520 :
521 0 : if(knownProps[es->second.propKey].getName() == "fsm" &&
522 0 : knownProps[es->second.propKey].getState() == pcf::IndiProperty::Alert)
523 : {
524 0 : fsmAlerts = true;
525 0 : alertDev.insert(knownProps[es->second.propKey].getDevice());
526 : }
527 0 : std::vector<std::string> s;
528 :
529 0 : s.resize( m_colFraction.size() );
530 :
531 0 : s[0] = std::to_string(m_cellContents.size()+1);
532 0 : s[1] = knownProps[es->second.propKey].getDevice();
533 0 : s[2] = displayProperty( knownProps[es->second.propKey] );
534 :
535 0 : s[3] = es->second.name;
536 :
537 0 : s[4] = displayValue( knownProps[es->second.propKey], es->second.name);
538 :
539 0 : m_cellContents.push_back(s);
540 0 : es->second.tableRow = m_cellContents.size()-1;
541 0 : }
542 :
543 0 : draw();
544 :
545 : //if(fpout) *fpout << "fsmAlerts: " << fsmAlerts << "\n";
546 :
547 : int cx, cy;
548 0 : getyx(w_interactWin, cy, cx);
549 0 : int cs = cursStat();
550 0 : cursStat(0);
551 :
552 0 : wclear(w_attentionWin);
553 0 : if(fsmAlerts)
554 : {
555 0 : std::string alrt = "!! FSM alert: " + *alertDev.begin();
556 0 : if(alertDev.size() > 1) alrt += " (+" + std::to_string(alertDev.size()-1) + ")";
557 :
558 0 : wprintw(w_attentionWin, "%s", alrt.c_str());
559 0 : }
560 0 : wrefresh(w_attentionWin);
561 :
562 0 : wmove(w_interactWin,cy,cx);
563 0 : cursStat(cs);
564 0 : wrefresh(w_interactWin);
565 :
566 0 : m_redraw -= start_redraw;
567 0 : if(m_redraw <0) m_redraw = 0;
568 :
569 0 : _moveCurrent(m_currY, m_currX);
570 0 : }
571 :
572 :
573 :
574 0 : void cursesINDI::updateTable()
575 : {
576 0 : if(m_redraw) return; //Pending redraw, so we skip it and let that take care of it.
577 :
578 0 : std::lock_guard<std::mutex> lock(m_drawMutex);
579 :
580 0 : int start_update = m_update;
581 :
582 : //if(fpout) *fpout << "updateTable: " << m_update << std::endl;
583 : int cx, cy;
584 :
585 0 : getyx(w_interactWin, cy, cx);
586 0 : int cs = cursStat();
587 :
588 0 : updateCurVal();
589 :
590 0 : bool fsmAlerts {false};
591 0 : std::set<std::string> alertDev;
592 :
593 0 : for(auto it = knownElements.begin(); it != knownElements.end(); ++it)
594 : {
595 : //if(fpout) *fpout << knownProps[it->second.propKey].getName() << " " << knownProps[it->second.propKey].getState() << "\n";
596 :
597 0 : if(knownProps[it->second.propKey].getName() == "fsm" &&
598 0 : knownProps[it->second.propKey].getState() == pcf::IndiProperty::Alert)
599 : {
600 0 : fsmAlerts = true;
601 0 : alertDev.insert(knownProps[it->second.propKey].getDevice());
602 : }
603 :
604 0 : if(it->second.tableRow == -1) continue;
605 :
606 0 : if(m_cellContents[it->second.tableRow][2] != displayProperty(knownProps[it->second.propKey]) )
607 : {
608 0 : m_cellContents[it->second.tableRow][2] = displayProperty(knownProps[it->second.propKey]) ;
609 :
610 0 : if(it->second.tableRow - m_startRow < (size_t) tabHeight()) //It's currently displayed
611 : {
612 0 : cursStat(0);
613 0 : wclear(m_gridWin[it->second.tableRow - m_startRow][2]);
614 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());
615 0 : wrefresh(m_gridWin[it->second.tableRow - m_startRow][2]);
616 0 : wmove(w_interactWin,cy,cx);
617 0 : cursStat(cs);
618 0 : wrefresh(w_interactWin);
619 : }
620 : }
621 :
622 0 : if(m_cellContents[it->second.tableRow][4] != displayValue(knownProps[it->second.propKey], it->second.name)) //.getValue())
623 : {
624 0 : m_cellContents[it->second.tableRow][4] = displayValue(knownProps[it->second.propKey], it->second.name); //knownProps[it->second.propKey][it->second.name].getValue();
625 :
626 0 : if(it->second.tableRow - m_startRow < (size_t) tabHeight()) //It's currently displayed
627 : {
628 0 : cursStat(0);
629 0 : wclear(m_gridWin[it->second.tableRow - m_startRow][4]);
630 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());
631 0 : wrefresh(m_gridWin[it->second.tableRow - m_startRow][4]);
632 0 : wmove(w_interactWin,cy,cx);
633 0 : cursStat(cs);
634 0 : wrefresh(w_interactWin);
635 : }
636 :
637 : };
638 : //updateContents( it->second.tableRow, 4, knownProps[it->second.propKey][it->second.name].getValue());
639 : }
640 :
641 0 : wattron(m_gridWin[m_currY][m_currX], A_REVERSE);
642 :
643 : //if(fpout) *fpout << "fsmAlerts: " << fsmAlerts << "\n";
644 :
645 0 : getyx(w_interactWin, cy, cx);
646 0 : cs = cursStat();
647 0 : cursStat(0);
648 0 : wclear(w_attentionWin);
649 0 : if(fsmAlerts)
650 : {
651 0 : std::string alrt = "!! FSM alert: " + *alertDev.begin();
652 0 : if(alertDev.size() > 1) alrt += " (+" + std::to_string(alertDev.size()-1) + ")";
653 :
654 0 : wprintw(w_attentionWin, "%s", alrt.c_str());
655 0 : }
656 0 : wrefresh(w_attentionWin);
657 0 : wmove(w_interactWin,cy,cx);
658 0 : cursStat(cs);
659 0 : wrefresh(w_interactWin);
660 :
661 : //print();
662 :
663 : // wmove(w_interactWin,cy,cx);
664 : // cursStat(cs);
665 : // wrefresh(w_interactWin);
666 :
667 0 : m_update -= start_update;
668 0 : if(m_update <0) m_update = 0;
669 0 : }
670 :
671 0 : void cursesINDI::updateCurVal( )
672 : {
673 : int cx, cy;
674 0 : getyx(w_interactWin, cy, cx);
675 0 : int cs = cursStat();
676 :
677 0 : cursStat(0);
678 0 : wclear(w_curvalWin);
679 :
680 0 : auto it = knownElements.begin();
681 0 : while(it != knownElements.end())
682 : {
683 0 : if( (size_t) it->second.tableRow == m_currY+m_startRow) break;
684 0 : ++it;
685 : }
686 :
687 0 : if(it == knownElements.end())
688 : {
689 0 : wrefresh(w_interactWin);
690 0 : cursStat(cs);
691 0 : return;
692 : }
693 :
694 0 : std::string cval = "> " + it->second.propKey + "." + it->second.name + " = ";
695 :
696 0 : cval += displayValue( knownProps[it->second.propKey], it->second.name);
697 :
698 0 : wprintw(w_curvalWin, "%s", cval.c_str());
699 0 : wrefresh(w_curvalWin);
700 :
701 0 : wmove(w_interactWin,cy,cx);
702 0 : cursStat(cs);
703 0 : wrefresh(w_interactWin);
704 :
705 0 : return;
706 0 : }
707 :
708 0 : void cursesINDI::moveCurrent( int nextY,
709 : int nextX
710 : )
711 : {
712 0 : std::lock_guard<std::mutex> lock(m_drawMutex);
713 :
714 0 : _moveCurrent(nextY, nextX);
715 0 : }
716 :
717 0 : void cursesINDI::_moveCurrent( int nextY,
718 : int nextX
719 : )
720 : {
721 0 : int currX = m_currX;
722 0 : int currY = m_currY;
723 :
724 0 : moveSelected(nextY, nextX);
725 :
726 : //if(fpout) *fpout << "moved: " << nextX << " " << nextY << " " << currX << "->" << m_currX << " " << currY << "->" << m_currY << std::endl;
727 :
728 0 : if(m_deviceSearching && nextX != 1)
729 : {
730 0 : wclear(w_interactWin);
731 0 : wrefresh(w_interactWin);
732 0 : m_deviceSearching = false;
733 : }
734 :
735 0 : updateCurVal();
736 :
737 0 : if(nextX == 1)
738 : {
739 0 : if( currX != nextX)
740 : {
741 0 : wclear(w_interactWin);
742 0 : wprintw(w_interactWin, "search: ");
743 0 : wrefresh(w_interactWin);
744 : }
745 : }
746 0 : else if(currY != nextY || currX == 1)
747 : {
748 0 : wclear(w_interactWin);
749 0 : if(m_currY + m_startRow >= knownElements.size())
750 : {
751 0 : wrefresh(w_interactWin);
752 0 : return;
753 : }
754 :
755 0 : auto it = knownElements.begin();
756 0 : while(it != knownElements.end())
757 : {
758 0 : if( (size_t) it->second.tableRow == m_currY+m_startRow) break;
759 0 : ++it;
760 : }
761 :
762 0 : if(it == knownElements.end())
763 : {
764 0 : wrefresh(w_interactWin);
765 0 : return;
766 : }
767 :
768 0 : if( knownProps[it->second.propKey].getPerm() != pcf::IndiProperty::ReadWrite)
769 : {
770 0 : wrefresh(w_interactWin);
771 0 : return;
772 : }
773 :
774 0 : if( knownProps[it->second.propKey].getType() == pcf::IndiProperty::Text)
775 : {
776 0 : wprintw(w_interactWin, "(e)dit this text");
777 : }
778 0 : else if( knownProps[it->second.propKey].getType() == pcf::IndiProperty::Number)
779 : {
780 0 : wprintw(w_interactWin, "(e)dit this number");
781 : }
782 0 : else if( knownProps[it->second.propKey].getType() == pcf::IndiProperty::Switch)
783 : {
784 0 : wprintw(w_interactWin, "(p)ress or (t)oggle this switch");
785 : }
786 :
787 :
788 0 : wrefresh(w_interactWin);
789 : }
790 :
791 : }
792 :
793 :
794 0 : void cursesINDI::deviceSearch( int ch )
795 : {
796 0 : bool updated = false;
797 0 : if( m_deviceSearching == true )
798 : {
799 0 : if( ch == KEY_BACKSPACE )
800 : {
801 0 : if(m_deviceTarget.size() > 0)
802 : {
803 0 : std::lock_guard<std::mutex> lock(m_drawMutex);
804 0 : m_deviceTarget.erase(m_deviceTarget.size()-1,1);
805 0 : wprintw(w_interactWin, "\b \b");
806 0 : wrefresh(w_interactWin);
807 0 : return;
808 0 : }
809 : }
810 0 : else if (std::isprint(ch))
811 : {
812 0 : m_deviceTarget += ch;
813 0 : updated = true;
814 : }
815 0 : else return;
816 : }
817 0 : else if (std::isprint(ch))
818 : {
819 0 : m_deviceTarget = ch;
820 0 : updated = true;
821 : }
822 0 : else return;
823 :
824 :
825 0 : m_deviceSearching = true;
826 :
827 0 : if(updated)
828 : {
829 0 : std::lock_guard<std::mutex> lock(m_drawMutex);
830 0 : wprintw(w_interactWin, "%c", ch);
831 0 : wrefresh(w_interactWin);
832 0 : }
833 :
834 : //if(fpout) *fpout << "device searching: " << m_deviceTarget << std::endl;
835 0 : if(m_deviceTarget.size() == 0) return;
836 :
837 0 : auto it = knownElements.lower_bound(m_deviceTarget);
838 :
839 : //if(fpout) *fpout << "new row: " << it->second.tableRow << " " << m_startRow << " " << it->second.tableRow-m_startRow << "\n";
840 :
841 0 : if(it->second.tableRow == -1) return;
842 :
843 0 : m_startRow = it->second.tableRow;
844 :
845 0 : moveCurrent( 0, 1);
846 :
847 0 : redrawTable();
848 :
849 0 : return;
850 :
851 : }
852 :
853 0 : void cursesINDI::keyPressed( int ch )
854 : {
855 :
856 : //If in first column, do device selection
857 0 : if(m_currX == 1 )
858 : {
859 0 : deviceSearch(ch);
860 0 : return;
861 : }
862 :
863 0 : if(m_deviceSearching)
864 : {
865 0 : wclear(w_interactWin);
866 0 : wrefresh(w_interactWin);
867 0 : m_deviceSearching = false;
868 : }
869 :
870 0 : switch(ch)
871 : {
872 0 : case 'e':
873 : {
874 0 : if(m_currY + m_startRow >= knownElements.size()) break;
875 0 : auto it = knownElements.begin();
876 0 : while(it != knownElements.end())
877 : {
878 0 : if( (size_t) it->second.tableRow == m_currY+m_startRow) break;
879 0 : ++it;
880 : }
881 :
882 0 : if(it == knownElements.end()) break;
883 :
884 : //Can't edit a switch
885 0 : if( knownProps[it->second.propKey].getType() != pcf::IndiProperty::Text && knownProps[it->second.propKey].getType() != pcf::IndiProperty::Number) break;
886 :
887 0 : cursStat(1);
888 :
889 : //mutex scope
890 : {
891 0 : std::lock_guard<std::mutex> lock(m_drawMutex);
892 0 : wclear(w_interactWin);
893 0 : wprintw(w_interactWin, "set %s.%s=", it->second.propKey.c_str(), it->second.name.c_str());
894 0 : wrefresh(w_interactWin);
895 0 : }
896 :
897 0 : bool escape = false;
898 0 : std::string newStr;
899 : int nch;
900 0 : while( (nch = wgetch(w_interactWin)) != '\n')
901 : {
902 0 : if(nch == ERR)
903 : {
904 0 : if( getQuitProcess())
905 : {
906 : //If the IndiConnection has set 'quitProces' but no other shutdown
907 : //has been issued then we record this as a lost connection.
908 0 : if(!m_shutdown) m_connectionLost = true;
909 0 : break;
910 : }
911 0 : else continue;
912 : }
913 :
914 0 : cursStat(1);
915 :
916 0 : if(nch == 27)
917 : {
918 0 : std::lock_guard<std::mutex> lock(m_drawMutex);
919 0 : wclear(w_interactWin);
920 0 : wrefresh(w_interactWin);
921 0 : escape = true;
922 0 : break;
923 0 : }
924 0 : if( nch == KEY_BACKSPACE )
925 : {
926 0 : if(newStr.size() > 0)
927 : {
928 0 : std::lock_guard<std::mutex> lock(m_drawMutex);
929 0 : newStr.erase(newStr.size()-1,1);
930 0 : wprintw(w_interactWin, "\b \b");
931 0 : wrefresh(w_interactWin);
932 0 : }
933 : }
934 0 : else if (std::isprint(nch))
935 : {
936 0 : std::lock_guard<std::mutex> lock(m_drawMutex);
937 0 : wprintw(w_interactWin, "%c", nch);
938 0 : wrefresh(w_interactWin);
939 :
940 0 : newStr += (char) nch;
941 0 : }
942 : }
943 0 : if(escape) break;
944 :
945 : //mutex scope
946 : {
947 0 : std::lock_guard<std::mutex> lock(m_drawMutex);
948 0 : wclear(w_interactWin);
949 0 : wprintw(w_interactWin, "send %s.%s=%s? y/n [n]", it->second.propKey.c_str(), it->second.name.c_str(), newStr.c_str());
950 0 : wrefresh(w_interactWin);
951 0 : }
952 :
953 0 : nch = 0;
954 0 : while( (nch = wgetch(w_interactWin)) == ERR)
955 : {
956 : }
957 :
958 0 : if(nch == 'y')
959 : {
960 0 : pcf::IndiProperty ipSend(knownProps[it->second.propKey].getType());
961 :
962 0 : ipSend.setDevice(knownProps[it->second.propKey].getDevice());
963 0 : ipSend.setName(knownProps[it->second.propKey].getName());
964 0 : ipSend.add(pcf::IndiElement(it->second.name));
965 : //if(fpout) *fpout << "newStr: " << newStr << std::endl;
966 :
967 0 : ipSend[it->second.name].setValue(newStr);
968 0 : sendNewProperty(ipSend);
969 0 : }
970 :
971 : //mutex scope
972 : {
973 0 : std::lock_guard<std::mutex> lock(m_drawMutex);
974 0 : wclear(w_interactWin);
975 0 : wrefresh(w_interactWin);
976 0 : }
977 :
978 0 : break;
979 0 : } //case 'e'
980 0 : case 't':
981 : {
982 0 : if(m_currY + m_startRow >= knownElements.size()) break;
983 0 : auto it = knownElements.begin();
984 0 : while(it != knownElements.end())
985 : {
986 0 : if( (size_t) it->second.tableRow == m_currY+m_startRow) break;
987 0 : ++it;
988 : }
989 :
990 0 : if(it == knownElements.end()) break;
991 :
992 0 : if( !knownProps[it->second.propKey].find(it->second.name)) break; //Just a check.
993 :
994 0 : if( knownProps[it->second.propKey].getType() != pcf::IndiProperty::Switch) break;
995 :
996 0 : std::string toggleString;
997 : pcf::IndiElement::SwitchStateType toggleState;
998 0 : if( knownProps[it->second.propKey][it->second.name].getSwitchState() == pcf::IndiElement::Off )
999 : {
1000 0 : toggleString = "On";
1001 0 : toggleState = pcf::IndiElement::On;
1002 : }
1003 0 : else if(knownProps[it->second.propKey][it->second.name].getSwitchState() == pcf::IndiElement::On)
1004 : {
1005 0 : toggleString = "Off";
1006 0 : toggleState = pcf::IndiElement::Off;
1007 : }
1008 0 : else break; //would happen fo state unknown
1009 :
1010 0 : cursStat(1);
1011 :
1012 : //mutex scope
1013 : {
1014 0 : std::lock_guard<std::mutex> lock(m_drawMutex);
1015 0 : wclear(w_interactWin);
1016 0 : wprintw(w_interactWin, "toggle %s.%s to %s?", it->second.propKey.c_str(), it->second.name.c_str(), toggleString.c_str());
1017 0 : wrefresh(w_interactWin);
1018 0 : }
1019 :
1020 0 : int nch = 0;
1021 0 : while( (nch = wgetch(w_interactWin)) == ERR)
1022 : {
1023 : }
1024 :
1025 0 : if(nch == 'y')
1026 : {
1027 0 : pcf::IndiProperty ipSend(knownProps[it->second.propKey].getType());
1028 :
1029 0 : ipSend.setDevice(knownProps[it->second.propKey].getDevice());
1030 0 : ipSend.setName(knownProps[it->second.propKey].getName());
1031 0 : ipSend.add(pcf::IndiElement(it->second.name));
1032 0 : ipSend[it->second.name].setSwitchState(toggleState);
1033 0 : sendNewProperty(ipSend);
1034 0 : }
1035 :
1036 : //mutex scope
1037 : {
1038 0 : std::lock_guard<std::mutex> lock(m_drawMutex);
1039 0 : wclear(w_interactWin);
1040 0 : wrefresh(w_interactWin);
1041 0 : }
1042 :
1043 0 : break;
1044 0 : } //case 't'
1045 0 : case 'p':
1046 : {
1047 0 : if(m_currY + m_startRow >= knownElements.size()) break;
1048 0 : auto it = knownElements.begin();
1049 0 : while(it != knownElements.end())
1050 : {
1051 0 : if( (size_t) it->second.tableRow == m_currY+m_startRow) break;
1052 0 : ++it;
1053 : }
1054 :
1055 0 : if(it == knownElements.end()) break;
1056 :
1057 0 : if( !knownProps[it->second.propKey].find(it->second.name)) break; //Just a check.
1058 :
1059 0 : if( knownProps[it->second.propKey].getType() != pcf::IndiProperty::Switch) break;
1060 0 : cursStat(1);
1061 :
1062 : //mutex scope
1063 : {
1064 0 : std::lock_guard<std::mutex> lock(m_drawMutex);
1065 0 : wclear(w_interactWin);
1066 0 : wprintw(w_interactWin, "press switch %s.%s?", it->second.propKey.c_str(), it->second.name.c_str());
1067 0 : wrefresh(w_interactWin);
1068 0 : }
1069 :
1070 0 : int nch = 0;
1071 0 : while( (nch = wgetch(w_interactWin)) == ERR)
1072 : {
1073 : }
1074 :
1075 0 : if(nch == 'y')
1076 : {
1077 0 : pcf::IndiProperty ipSend(knownProps[it->second.propKey].getType());
1078 :
1079 0 : ipSend.setDevice(knownProps[it->second.propKey].getDevice());
1080 0 : ipSend.setName(knownProps[it->second.propKey].getName());
1081 :
1082 : //Must add all elements
1083 0 : for(auto elit = knownProps[it->second.propKey].getElements().begin(); elit != knownProps[it->second.propKey].getElements().end(); ++elit)
1084 : {
1085 0 : ipSend.add(elit->second);
1086 0 : if( knownProps[it->second.propKey].getRule() != pcf::IndiProperty::AnyOfMany)
1087 : {
1088 0 : ipSend[elit->first].setSwitchState(pcf::IndiElement::Off);
1089 : }
1090 : }
1091 :
1092 0 : ipSend[it->second.name].setSwitchState(pcf::IndiElement::On);
1093 0 : sendNewProperty(ipSend);
1094 0 : }
1095 :
1096 : //mutex scope
1097 : {
1098 0 : std::lock_guard<std::mutex> lock(m_drawMutex);
1099 0 : wclear(w_interactWin);
1100 0 : wrefresh(w_interactWin);
1101 0 : }
1102 :
1103 0 : break;
1104 : } //case 'p'
1105 0 : default:
1106 0 : return;//break;
1107 : }
1108 :
1109 0 : std::lock_guard<std::mutex> lock(m_drawMutex);
1110 0 : cursStat(0);
1111 0 : wrefresh(w_interactWin);
1112 :
1113 : }
|