source: trunk/FACT++/gui/FactGui.h @ 10397

Last change on this file since 10397 was 10397, checked in by tbretz, 9 years ago
Some cosmetics.
File size: 18.4 KB
Line 
1#ifndef FACT_FactGui
2#define FACT_FactGui
3
4#include "MainWindow.h"
5
6#include <iomanip>
7
8#include <boost/bind.hpp>
9
10#include <QtGui/QStandardItemModel>
11
12#include "CheckBoxDelegate.h"
13
14#include "src/Converter.h"
15#include "src/HeadersFTM.h"
16#include "src/DimNetwork.h"
17
18using namespace std;
19
20class FactGui : public MainWindow, public DimNetwork
21{
22private:
23    class FunctionEvent : public QEvent
24    {
25    public:
26        boost::function<void(const QEvent &)> fFunction;
27
28        FunctionEvent(const boost::function<void(const QEvent &)> &f)
29            : QEvent((QEvent::Type)QEvent::registerEventType()),
30            fFunction(f) { }
31
32        bool Exec() { fFunction(*this); return true; }
33    };
34
35    DimInfo fDimDNS;
36    map<string, DimInfo*> fServices;
37
38    // ======================================================================
39
40    QStandardItem *AddServiceItem(const std::string &server, const std::string &service, bool iscmd)
41    {
42        QListView *servers     = iscmd ? fDimCmdServers     : fDimSvcServers;
43        QListView *services    = iscmd ? fDimCmdCommands    : fDimSvcServices;
44        QListView *description = iscmd ? fDimCmdDescription : fDimSvcDescription;
45
46        QStandardItemModel *m = dynamic_cast<QStandardItemModel*>(servers->model());
47        if (!m)
48        {
49            m = new QStandardItemModel(this);
50            servers->setModel(m);
51            services->setModel(m);
52            description->setModel(m);
53        }
54
55        QList<QStandardItem*> l = m->findItems(server.c_str());
56
57        if (l.size()>1)
58        {
59            cout << "hae" << endl;
60            return 0;
61        }
62
63        QStandardItem *col = l.size()==0 ? NULL : l[0];
64
65        if (!col)
66        {
67            col = new QStandardItem(server.c_str());
68            m->appendRow(col);
69
70            if (!services->rootIndex().isValid())
71            {
72                services->setRootIndex(col->index());
73                servers->setCurrentIndex(col->index());
74            }
75        }
76
77        QStandardItem *item = 0;
78        for (int i=0; i<col->rowCount(); i++)
79        {
80            QStandardItem *coli = col->child(i);
81            if (coli->text().toStdString()==service)
82                return coli;
83        }
84
85        item = new QStandardItem(service.c_str());
86        col->appendRow(item);
87
88        if (!description->rootIndex().isValid())
89        {
90            description->setRootIndex(item->index());
91            services->setCurrentIndex(item->index());
92        }
93
94        if (!iscmd)
95            item->setCheckable(true);
96
97        return item;
98    }
99
100    void AddDescription(QStandardItem *item, const vector<Description> &vec)
101    {
102        if (!item)
103            return;
104        if (vec.size()==0)
105            return;
106
107        item->setToolTip(vec[0].comment.c_str());
108
109        const string str = Description::GetHtmlDescription(vec);
110
111        QStandardItem *desc = new QStandardItem(str.c_str());
112        desc->setSelectable(false);
113        item->setChild(0, 0, desc);
114    }
115
116    // ======================================================================
117
118    void handleAddServer(const std::string &server)
119    {
120        const State s = GetState(server, GetCurrentState(server));
121        handleStateChanged(Time(), server, s);
122    }
123
124    void handleRemoveServer(const string &server)
125    {
126        handleStateOffline(server);
127        handleRemoveAllServices(server);
128    }
129
130    void handleRemoveAllServers()
131    {
132        QStandardItemModel *m = 0;
133        if ((m=dynamic_cast<QStandardItemModel*>(fDimCmdServers->model())))
134            m->removeRows(0, m->rowCount());
135
136        if ((m = dynamic_cast<QStandardItemModel*>(fDimSvcServers->model())))
137            m->removeRows(0, m->rowCount());
138    }
139
140    void handleAddService(const std::string &server, const std::string &service, const std::string &/*fmt*/, bool iscmd)
141    {
142        QStandardItem *item = AddServiceItem(server, service, iscmd);
143        const vector<Description> v = GetDescription(server, service);
144        AddDescription(item, v);
145    }
146
147    void handleRemoveService(const std::string &server, const std::string &service, bool iscmd)
148    {
149        QListView *servers = iscmd ? fDimCmdServers : fDimSvcServers;
150
151        QStandardItemModel *m = dynamic_cast<QStandardItemModel*>(servers->model());
152        if (!m)
153            return;
154
155        QList<QStandardItem*> l = m->findItems(server.c_str());
156        if (l.size()!=1)
157            return;
158
159        for (int i=0; i<l[0]->rowCount(); i++)
160        {
161            QStandardItem *row = l[0]->child(i);
162            if (row->text().toStdString()==service)
163            {
164                l[0]->removeRow(row->index().row());
165                return;
166            }
167        }
168    }
169
170    void handleRemoveAllServices(const std::string &server)
171    {
172        QStandardItemModel *m = 0;
173        if ((m=dynamic_cast<QStandardItemModel*>(fDimCmdServers->model())))
174        {
175            QList<QStandardItem*> l = m->findItems(server.c_str());
176            if (l.size()==1)
177                m->removeRow(l[0]->index().row());
178        }
179
180        if ((m = dynamic_cast<QStandardItemModel*>(fDimSvcServers->model())))
181        {
182            QList<QStandardItem*> l = m->findItems(server.c_str());
183            if (l.size()==1)
184                m->removeRow(l[0]->index().row());
185        }
186    }
187
188    void handleAddDescription(const std::string &server, const std::string &service, const vector<Description> &vec)
189    {
190        const bool iscmd = IsCommand(server, service)==true;
191
192        QStandardItem *item = AddServiceItem(server, service, iscmd);
193        AddDescription(item, vec);
194    }
195
196    void handleStateChanged(const Time &/*time*/, const std::string &server,
197                            const State &s)
198    {
199        // FIXME: Prefix tooltip with time
200        if (server=="FTM_CONTROL")
201        {
202            fStatusFTMLabel->setText(s.name.c_str());
203            fStatusFTMLabel->setToolTip(s.comment.c_str());
204
205            if (s.index<FTM::kDisconnected) // No Dim connection
206            {
207                fStatusFTMLed_1->setEnabled(false);
208                fStatusFTMLed_2->setEnabled(false);
209                fStatusFTMLed_3->setEnabled(false);
210            }
211            if (s.index==FTM::kDisconnected) // Dim connection / FTM disconnected
212            {
213                fStatusFTMLed_1->setEnabled(true);
214                fStatusFTMLed_2->setEnabled(false);
215                fStatusFTMLed_3->setEnabled(false);
216            }
217            if (s.index==FTM::kConnected) // Dim connection / FTM connected
218            {
219                fStatusFTMLed_1->setEnabled(false);
220                fStatusFTMLed_2->setEnabled(true);
221                fStatusFTMLed_3->setEnabled(false);
222            }
223        }
224
225        if (server=="FAD_CONTROL")
226        {
227            fStatusFADLabel->setText(s.name.c_str());
228            fStatusFADLabel->setToolTip(s.comment.c_str());
229
230            if (s.index<FTM::kDisconnected) // No Dim connection
231            {
232                fStatusFADLed_1->setEnabled(false);
233                fStatusFADLed_2->setEnabled(false);
234                fStatusFADLed_3->setEnabled(false);
235            }
236            if (s.index==FTM::kDisconnected) // Dim connection / FTM disconnected
237            {
238                fStatusFADLed_1->setEnabled(true);
239                fStatusFADLed_2->setEnabled(false);
240                fStatusFADLed_3->setEnabled(false);
241            }
242            if (s.index==FTM::kConnected) // Dim connection / FTM connected
243            {
244                fStatusFADLed_1->setEnabled(false);
245                fStatusFADLed_2->setEnabled(true);
246                fStatusFADLed_3->setEnabled(false);
247            }
248        }
249
250        if (server=="DATA_LOGGER")
251        {
252            fStatusLoggerLabel->setText(s.name.c_str());
253            fStatusLoggerLabel->setToolTip(s.comment.c_str());
254
255            if (s.index<-1 || s.index>=0x100) // Error
256            {
257                fStatusLoggerLed_1->setEnabled(false);
258                fStatusLoggerLed_2->setEnabled(false);
259                fStatusLoggerLed_3->setEnabled(false);
260            }
261            if (s.index<=30)   // Waiting
262            {
263                fStatusLoggerLed_1->setEnabled(false);
264                fStatusLoggerLed_2->setEnabled(true);
265                fStatusLoggerLed_3->setEnabled(false);
266            }
267            if (s.index==40)   // Logging
268            {
269                fStatusLoggerLed_1->setEnabled(false);
270                fStatusLoggerLed_2->setEnabled(false);
271                fStatusLoggerLed_3->setEnabled(true);
272            }
273        }
274
275        if (server=="CHAT")
276        {
277            fStatusChatLabel->setText(s.name.c_str());
278
279            if (s.index==FTM::kConnected) // Dim connection / FTM connected
280                fStatusChatLed_3->setEnabled(true);
281            else
282                fStatusChatLed_3->setEnabled(false);
283        }
284    }
285
286    void handleStateOffline(const string &server)
287    {
288        handleStateChanged(Time(), server, State(-2, "Offline", "No connection via DIM."));
289    }
290
291    void handleWrite(const Time &time, const string &text, int qos)
292    {
293        stringstream out;
294
295        if (text.substr(0, 6)=="CHAT: ")
296        {
297            out << "<font size='-1' color='navy'>[<B>";
298            out << Time::fmt("%H:%M:%S") << time << "</B>]</FONT>  ";
299            out << text.substr(6);
300            fChatText->append(out.str().c_str());
301            return;
302        }
303
304
305        out << "<font color='";
306
307        switch (qos)
308        {
309        case kMessage: out << "black";   break;
310        case kInfo:    out << "green";   break;
311        case kWarn:    out << "#FF6600"; break;
312        case kError:   out << "maroon";  break;
313        case kFatal:   out << "maroon";  break;
314        case kDebug:   out << "navy";    break;
315        default:       out << "navy";    break;
316        }
317        out << "'>" << time.GetAsStr() << " - " << text << "</font>";
318
319        fLogText->append(out.str().c_str());
320
321        if (qos>=kWarn)
322            fTextEdit->append(out.str().c_str());
323    }
324
325    void handleDimService(const string &txt)
326    {
327        fDimSvcText->append(txt.c_str());
328    }
329
330    void handleDimDNS(int version)
331    {
332        ostringstream str;
333        str << "DIM V" << version/100 << 'r' << version%100;
334
335        fStatusDNSLed_3->setEnabled(version!=0);
336        fStatusDNSLabel->setText(version==0?"Offline":str.str().c_str());
337        fStatusDNSLabel->setToolTip(version==0?"No connection to DIM DNS.":"Connection to DIM DNS established.");
338    }
339
340    // ======================================================================
341
342    void AddServer(const std::string &s)
343    {
344        DimNetwork::AddServer(s);
345
346        QApplication::postEvent(this,
347           new FunctionEvent(boost::bind(&FactGui::handleAddServer, this, s)));
348    }
349
350    void RemoveServer(const std::string &s)
351    {
352        UnsubscribeServer(s);
353
354        DimNetwork::RemoveServer(s);
355
356        QApplication::postEvent(this,
357           new FunctionEvent(boost::bind(&FactGui::handleRemoveServer, this, s)));
358    }
359
360    void RemoveAllServers()
361    {
362        UnsubscribeAllServers();
363
364        vector<string> v = GetServerList();
365        for (vector<string>::iterator i=v.begin(); i<v.end(); i++)
366            QApplication::postEvent(this,
367                                    new FunctionEvent(boost::bind(&FactGui::handleStateOffline, this, *i)));
368
369        DimNetwork::RemoveAllServers();
370
371        QApplication::postEvent(this,
372           new FunctionEvent(boost::bind(&FactGui::handleRemoveAllServers, this)));
373    }
374
375    void AddService(const std::string &server, const std::string &service, const std::string &fmt, bool iscmd)
376    {
377        QApplication::postEvent(this,
378           new FunctionEvent(boost::bind(&FactGui::handleAddService, this, server, service, fmt, iscmd)));
379    }
380
381    void RemoveService(const std::string &server, const std::string &service, bool iscmd)
382    {
383        if (fServices.find(server+'/'+service)!=fServices.end())
384            UnsubscribeService(server+'/'+service);
385
386        QApplication::postEvent(this,
387           new FunctionEvent(boost::bind(&FactGui::handleRemoveService, this, server, service, iscmd)));
388    }
389
390    void RemoveAllServices(const std::string &server)
391    {
392        UnsubscribeServer(server);
393
394        QApplication::postEvent(this,
395           new FunctionEvent(boost::bind(&FactGui::handleRemoveAllServices, this, server)));
396    }
397
398    void AddDescription(const std::string &server, const std::string &service, const vector<Description> &vec)
399    {
400        QApplication::postEvent(this,
401           new FunctionEvent(boost::bind(&FactGui::handleAddDescription, this, server, service, vec)));
402    }
403
404
405    void IndicateStateChange(const Time &time, const std::string &server)
406    {
407        const State s = GetState(server, GetCurrentState(server));
408
409        QApplication::postEvent(this,
410           new FunctionEvent(boost::bind(&FactGui::handleStateChanged, this, time, server, s)));
411    }
412
413    int Write(const Time &time, const string &txt, int qos)
414    {
415        QApplication::postEvent(this,
416           new FunctionEvent(boost::bind(&FactGui::handleWrite, this, time, txt, qos)));
417
418        return 0;
419    }
420
421    void infoHandler(DimInfo &info)
422    {
423        const string fmt = string("DIS_DNS/SERVER_INFO")==info.getName() ? "C" : info.getFormat();
424
425        stringstream dummy;
426        const Converter conv(dummy, fmt, false);
427
428        const Time tm(info.getTimestamp(), info.getTimestampMillisecs());
429
430        stringstream out;
431        out << Time::fmt("%H:%M:%S.%f") << tm << "   <B>" << info.getName() << "</B> - ";
432
433        bool iserr = false;
434        if (!conv)
435        {
436            out << "Compilation of format string '" << fmt << "' failed!";
437            iserr = true;
438        }
439        else
440        {
441            try
442            {
443                const string dat = conv.GetString(info.getData(), info.getSize());
444                out << dat;
445            }
446            catch (const runtime_error &e)
447            {
448                out << "Conversion to string failed!<pre>" << e.what() << "</pre>";
449                iserr = true;
450            }
451        }
452
453        // srand(hash<string>()(string(info.getName())));
454        // int bg = rand()&0xffffff;
455
456        int bg = hash<string>()(string(info.getName()));
457
458        int r = bg&0x1f0000;
459        int g = bg&0x001f00;
460        int b = bg&0x00001f;
461
462        bg = ~(b|g|r)&0xffffff;
463
464        if (iserr)
465            bg = 0xffffff;
466
467        stringstream bgcol;
468        bgcol << hex << setfill('0') << setw(6) << bg;
469
470        const string col = iserr ? "red" : "black";
471        const string str = "<table width='100%' bgcolor=#"+bgcol.str()+"><tr><td><font color='"+col+"'>"+out.str()+"</font></td></tr></table>";
472
473        QApplication::postEvent(this,
474                                new FunctionEvent(boost::bind(&FactGui::handleDimService, this, str)));
475    }
476
477    void infoHandler()
478    {
479        if (getInfo()==&fDimDNS)
480        {
481            QApplication::postEvent(this,
482                                    new FunctionEvent(boost::bind(&FactGui::handleDimDNS, this, getInfo()->getInt())));
483            return;
484        }
485
486        for (map<string,DimInfo*>::iterator i=fServices.begin(); i!=fServices.end(); i++)
487            if (i->second==getInfo())
488            {
489                infoHandler(*i->second);
490                return;
491            }
492
493        DimNetwork::infoHandler();
494    }
495
496    // ======================================================================
497
498
499    void SubscribeService(const string &service)
500    {
501        if (fServices.find(service)!=fServices.end())
502        {
503            cout << "ERROR - We are already subscribed to " << service << endl;
504            return;
505        }
506
507        fServices[service] = new DimStampedInfo(service.c_str(), (void*)NULL, 0, this);
508    }
509
510    void UnsubscribeService(const string &service)
511    {
512        const map<string,DimInfo*>::iterator i=fServices.find(service);
513
514        if (i==fServices.end())
515        {
516            cout << "ERROR - We are not subscribed to " << service << endl;
517            return;
518        }
519
520        delete i->second;
521
522        fServices.erase(i);
523    }
524
525    void UnsubscribeServer(const string &server)
526    {
527        for (map<string,DimInfo*>::iterator i=fServices.begin();
528             i!=fServices.end(); i++)
529            if (i->first.substr(0, server.length()+1)==server+'/')
530            {
531                delete i->second;
532                fServices.erase(i);
533            }
534    }
535
536    void UnsubscribeAllServers()
537    {
538        for (map<string,DimInfo*>::iterator i=fServices.begin();
539             i!=fServices.end(); i++)
540            delete i->second;
541
542        fServices.clear();
543    }
544
545    // ======================================================================
546
547    bool event(QEvent *evt)
548    {
549        if (dynamic_cast<FunctionEvent*>(evt))
550            return static_cast<FunctionEvent*>(evt)->Exec();
551
552        if (dynamic_cast<CheckBoxEvent*>(evt))
553        {
554            const QStandardItem &item = static_cast<CheckBoxEvent*>(evt)->item;
555            const QStandardItem *par  = item.parent();
556            if (par)
557            {
558                const QString server  = par->text();
559                const QString service = item.text();
560
561                const string s = (server+'/'+service).toStdString();
562
563                if (item.checkState()==Qt::Checked)
564                    SubscribeService(s);
565                else
566                    UnsubscribeService(s);
567            }
568        }
569
570        return MainWindow::event(evt); // unrecognized
571    }
572
573    void on_fDimCmdSend_clicked(bool)
574    {
575        const QString server    = fDimCmdServers->currentIndex().data().toString();
576        const QString command   = fDimCmdCommands->currentIndex().data().toString();
577        const QString arguments = fDimCmdLineEdit->displayText();
578
579        // FIXME: Sending a command exactly when the info Handler changes
580        //        the list it might lead to confusion.
581        try
582        {
583            SendDimCommand(server.toStdString(), command.toStdString()+" "+arguments.toStdString());
584            fTextEdit->append("<font color='green'>Command '"+server+'/'+command+"' successfully emitted.</font>");
585            fDimCmdLineEdit->clear();
586        }
587        catch (const runtime_error &e)
588        {
589            stringstream txt;
590            txt << e.what();
591
592            string buffer;
593            while (getline(txt, buffer, '\n'))
594                fTextEdit->append(("<font color='red'><pre>"+buffer+"</pre></font>").c_str());
595        }
596    }
597
598public:
599    FactGui() : fDimDNS("DIS_DNS/VERSION_NUMBER", 1, int(0), this)
600    {
601        DimClient::sendCommand("CHAT/MSG", "GUI online.");
602        // + MessageDimRX
603    }
604    ~FactGui()
605    {
606        UnsubscribeAllServers();
607    }
608};
609
610#endif
Note: See TracBrowser for help on using the repository browser.