Changeset 11088


Ignore:
Timestamp:
06/21/11 12:52:38 (13 years ago)
Author:
ogrimm
Message:
Updates to Edd. History server now periodically saves histories to disk.
Location:
fact
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • fact/Evidence/GUI.cc

    r10984 r11088  
    2020// History chooser function (opens plot for numeric data, TextHist for all other)
    2121//
    22 QWidget *OpenHistory(char *Service, int Index) {
     22void OpenHistory(char *Service, int Index) {
    2323
    2424  QString Format;
    2525  DimBrowser Browser;
    2626  class EvidenceHistory *Hist = Handler->GetHistory(Service);
     27  char *Name, *Fmt;
    2728
    2829  // Check if history service available
    2930  if (Hist == NULL || Hist->GetFormat() == NULL) {
    3031        QMessageBox::warning(NULL, "Edd Message", QString("Could not retrieve history for service ") + Service ,QMessageBox::Ok);
    31 
    32         // If service currently available, take its format
    33         char *Name, *Fmt;
    34 
    35         Browser.getServices(Service);
    36         if (Browser.getNextService(Name, Fmt) != 0) Format = QString(Fmt);
    37         else {
    38           Handler->DropHistory(Service);
    39           return NULL;
    40         }
    41   }
    42 
    43   if (Format.isEmpty()) Format = Hist->GetFormat();
     32  }
     33  else Format = Hist->GetFormat();
     34 
     35  // If service currently available, take its format
     36  Browser.getServices(Service);
     37  if (Browser.getNextService(Name, Fmt) != 0) Format = QString(Fmt);
     38
    4439  Handler->DropHistory(Service);
    4540 
    46   if (Format.size() == 1 && Format[0] != 'C') return new EddPlot(Service, Index);
    47   else return new EddText(Service);
    48  
    49  
     41  // Open new window   
     42  QMainWindow *M = new QMainWindow;
     43  M->setCentralWidget(new QWidget(M));
     44  M->setStatusBar(new QStatusBar(M));
     45  M->setAttribute(Qt::WA_DeleteOnClose);
     46 
     47  QWidget *W;
     48  if (Format.size() == 1 && Format[0] != 'C') W = new EddPlot(Service, Index);
     49  else W = new EddText(Service);
     50
     51  QGridLayout *Layout = new QGridLayout(M->centralWidget());
     52  Layout->addWidget(W, 0, 0);
     53  Layout->addWidget(new EddLineDisplay(Service, Index), 1, 0);
     54  M->resize(300,350);
     55  M->show();
    5056}
    5157
     
    183189//
    184190void EddLineDisplay::contextMenuEvent(QContextMenuEvent *Event) {
    185 
     191 
    186192  Menu->exec(Event->globalPos());
    187193}
     
    190196void EddLineDisplay::MenuOpenHistory() {
    191197 
    192   LastHist = OpenHistory(ServiceName.toAscii().data(), Index);
    193   if (LastHist != NULL) LastHist->show();
     198  OpenHistory(ServiceName.toAscii().data(), Index);
    194199}
    195200
     
    217222
    218223  setToolTip("Send command "+Name); 
     224  setStatusTip(QString("Command %1").arg(Name));
     225
    219226  connect(this, SIGNAL(returnPressed()), SLOT(SendCommand()));
     227}
     228
     229// Get command format and returns NULL (not empty) QString if not available
     230QString EddCommand::GetFormat() {
     231
     232  char *ItsName, *Format;
     233  DimBrowser Browser;
     234
     235  Browser.getServices(Name.toAscii().data());
     236 
     237  if (Browser.getNextService(ItsName, Format) != DimCOMMAND) return QString();   
     238  else return Format;
    220239}
    221240
     
    223242void EddCommand::SendCommand() {
    224243
    225   DimClient::sendCommand(Name.toAscii().data(), text().toAscii().data());
     244  QByteArray Data;
     245
     246  // Ignore empty commands 
     247  if (text().isEmpty()) return;
     248
     249  // Check if command available and retrieve format
     250  QString Format = GetFormat();
     251
     252  if (Format.isNull()) {
     253        QMessageBox::warning(this, "Edd Message", "Command " + Name + " currently unavailable", QMessageBox::Ok);
     254        return;   
     255  }
     256
     257  // Command has no argument
     258  if (Format.isEmpty()) {
     259        DimClient::sendCommand(Name.toAscii().data(), NULL, 0);
     260        return;
     261  }
     262 
     263  // Command has string as argument
     264  if (Format == "C") {
     265        DimClient::sendCommand(Name.toAscii().data(), text().toAscii().data());
     266        return;
     267  }
     268
     269  // Command has structure format, interpret data as hex
     270  if (Format.size() > 1) {
     271        Data = QByteArray::fromHex(text().toAscii());
     272  }
     273  else {
     274        QDataStream Stream(&Data, QIODevice::WriteOnly);
     275        std::vector<std::string> Values = EvidenceServer::Tokenize(text().toStdString());
     276
     277        for (unsigned int i=0; i<Values.size(); i++) {
     278          switch (Format[0].toAscii()) {
     279      case 'I':
     280      case 'L': Stream << (int) atoi(Values[i].c_str());                        break;
     281      case 'S': Stream << (short) atoi(Values[i].c_str());                      break;
     282      case 'F': Stream << (float) atof(Values[i].c_str());                      break;
     283      case 'D': Stream << (double) atof(Values[i].c_str());                     break;
     284      case 'X': Stream << (long long) (atof(Values[i].c_str()));        break;
     285          }     
     286        }
     287  }
     288 
     289  DimClient::sendCommand(Name.toAscii().data(), Data.data(), Data.size());
    226290  clear();
    227291}
    228292
     293// Opening context menu
     294void EddCommand::contextMenuEvent(QContextMenuEvent *Event) {
     295
     296  QMenu *Menu = createStandardContextMenu();
     297  Menu->addSeparator();
     298  Menu->addAction("Command help", this, SLOT(MenuCommandHelp()));
     299  Menu->exec(Event->globalPos());
     300  delete Menu;
     301}
     302
     303// Help text
     304void EddCommand::MenuCommandHelp() {
     305
     306  QString Format = GetFormat();
     307  QString Text = Name;
     308 
     309  if (Format.isNull()) {
     310        Text += " is currently unavailable";
     311  }
     312  else  Text += " has format '"+Format+"'";
     313
     314  Text +=       "\n\nCommand arguments will be transmitted as string for format 'C',\n"
     315                        "interpreted as an array of numbers for all other single character formats\n"
     316                        "and as bytes in hexadecimal encoding for all more complex formats";
     317
     318  QMessageBox::about(this, "Edd - Command help", Text);
     319}
    229320
    230321//////////////////////////////////
     
    253344  Menu->removeAction(Action);
    254345  Menu->insertAction(Menu->actions().value(1), Action);
    255  
     346
     347  Action = Menu->addAction("Show last hour", this, SLOT(MenuShowLastHour()));
     348  Menu->insertAction(Menu->actions().value(8), Action);
     349  Action = Menu->addAction("Show last day", this, SLOT(MenuShowLastDay()));
     350  Menu->insertAction(Menu->actions().value(8), Action); 
     351   
    256352  // Set timer to regularly update plot if new data available
    257353  SingleShot = new QTimer(this);
     
    378474  AddService(E.left(E.lastIndexOf(' ')), E.right(E.size()-E.lastIndexOf(' ')).toInt());
    379475}
     476
     477// Show last hour/last day
     478void EddPlot::MenuShowLastHour() {
     479
     480  setAxisScale(QwtPlot::xBottom, time(NULL)-60*60, time(NULL)+60);
     481  setAxisAutoScale(QwtPlot::yLeft);
     482  replot();
     483}
     484
     485void EddPlot::MenuShowLastDay() {
     486
     487  setAxisScale(QwtPlot::xBottom, time(NULL)-24*3600, time(NULL)+3600);
     488  setAxisAutoScale(QwtPlot::yLeft);
     489  replot();
     490}
     491
    380492
    381493// Remove list entry
     
    783895void EddBasePlot::MenuPlotHelp() {
    784896
    785   QMessageBox::about(this, "Edd - Plot navigation",
     897  QMessageBox::about(this, "Edd - Plot help",
    786898    "Zoom\tMouse wheel\n"
    787899        "\tKeys m and shift-m\n"
     
    793905        "ESC cancels selection\n"
    794906    "Cursor keys move mouse\n\n"
    795         "Statistics are calculated over the current x axis extend");
     907        "Statistics are calculated over the current x axis extend\n\n"
     908        "Items can be added to history plot by drag&drop from\n"
     909        "DIM service displays or from other plots legends");
    796910}
    797911
     
    880994  Mutex = new QMutex(QMutex::Recursive);
    881995  Volume = 0;
     996  Period = 10;
    882997
    883998  // Timer to calculate data rates
    884999  QTimer *Timer = new QTimer(this);
    8851000  Timer->connect(Timer, SIGNAL(timeout()), this, SLOT(UpdateStatistics()));
    886   Timer->start(10000);
     1001  Timer->start(Period*1000);
    8871002
    8881003  // Connect to DIM handler
     
    9171032void EddDim::MakeSubscriptions() {
    9181033
    919   if (WaitingList.isEmpty()) return;
    920 
    9211034  // Lock before accessing list
    9221035  QMutexLocker Locker(Mutex);
     1036
     1037  if (WaitingList.isEmpty()) return;
    9231038
    9241039  QString Name = WaitingList.first();
     
    10011116  }
    10021117 
    1003   float Rate = Volume/1024.0/10;
     1118  float Rate = Volume/1024.0/Period;
    10041119  Volume = 0;
    10051120  YEP("Edd/Rate_kBSec", time(NULL), QByteArray::number(Rate), "F", QString::number(Rate));
     
    10341149  }
    10351150
    1036   // Update statistics only for actual Dim services
    1037   if (!Name.startsWith("Edd/")) Volume += Data.size();
    1038  
    10391151  // Emit signal to all widgets
    10401152  YEP(Name, Time, Data, Format, Text);
     
    10471159  if (!EvidenceServer::ServiceOK(getInfo())) INT(getInfo()->getName(), -1);
    10481160  else {
     1161    // Signal to EddDim::Update()
    10491162        INT(getInfo()->getName(), getInfo()->getTimestamp(), QByteArray((char *) getInfo()->getData(),
    10501163                getInfo()->getSize()), getInfo()->getFormat(), QString::fromStdString(EvidenceServer::ToString(getInfo()->getFormat(), getInfo()->getData(), getInfo()->getSize())));
     1164        // No mutex protection for Volume (otherwise must be in Update())
     1165        Volume += getInfo()->getSize();
    10511166  }
    10521167}
     
    10641179  M->setStatusBar(new QStatusBar(M));
    10651180  M->setWindowTitle(WindowName);
    1066 
    10671181  L = new QGridLayout(M->centralWidget());
    10681182
    1069   connect(this, SIGNAL(pressed()), SLOT(Toggle()));
    1070 }
    1071 
    1072 // Show or hide window
    1073 void EddWindow::Toggle() {
    1074 
    1075   if (M->isVisible()) M->hide();
    1076   else {
    1077         M->show();
    1078         M->raise();
    1079   }
     1183  connect(this, SIGNAL(pressed()), M, SLOT(show()));
     1184  connect(this, SIGNAL(pressed()), M, SLOT(raise()));
    10801185}
    10811186
  • fact/Evidence/GUI.h

    r10984 r11088  
    3131const QColor EddPlotBackgroundColor(Qt::yellow);
    3232
    33 QWidget *OpenHistory(char *, int);
     33void OpenHistory(char *, int);
    3434bool SetStatus(QWidget *, QString, int, QString, int = -1);
    35 
    3635
    3736// Base class for Edd plot
     
    131130
    132131        QString Name;
    133        
     132
     133        QString GetFormat();
     134
    134135  public:
    135136    EddCommand(QString, QWidget * = NULL);
     
    137138  private slots:
    138139        void SendCommand();     
     140    void contextMenuEvent(QContextMenuEvent *);
     141        void MenuCommandHelp();   
    139142};
    140143
     
    192195        void LegendClicked(QwtPlotItem *);
    193196    void MenuPasteService();
     197        void MenuShowLastHour();
     198        void MenuShowLastDay();
    194199};
    195200
     
    238243    QMap<QString, struct HistItem> HistoryList;
    239244
     245        unsigned int Period;
    240246        long long Volume;
    241247
     
    273279        QGridLayout *Layout();
    274280        QMainWindow *Window();
    275   private slots:
    276         void Toggle();
    277281};
    278282
  • fact/Evidence/History.cc

    r10996 r11088  
    3131const string DEFAULT_MAX_SIZE_KB = "2000";
    3232const string DEFAULT_NUM_ENTRIES = "1000";      // Number of entries in each history buffer
     33const double MIN_SAVE_PERDIOD = 0.5;                    // Minimum period between saving history buffers in hours
    3334
    3435//
     
    4445          double MinAbsChange;
    4546          string Format;
     47          time_t LastSave;
    4648        };
    4749        map<string, struct Item> Map;
     
    5557        void AddService(string, const char *);
    5658        void RemoveService(string);
     59        void SaveHistory(string);
    5760        off_t FileSize(FILE *);
    5861        FILE *OpenFile(string, const char *);
     
    7376  GetConfig("maxsize_kb", DEFAULT_MAX_SIZE_KB);
    7477  GetConfig("numentries", DEFAULT_NUM_ENTRIES);
     78  GetConfig("saveperiod", "1");
    7579  GetConfig("exclude", "");
    7680
     
    9599  // Check if service available
    96100  if (!ServiceOK(I)) return;
    97 
     101 
    98102  // ====== Part A: Handle service subscriptions ===
    99103 
     
    143147  if (Map.count(Service) == 0 || I->getSize()==0 || I->getTimestamp()<=0) return;
    144148
     149  // Save history buffers periodically (in case of program crash)
     150  if (time(NULL)-Map[Service].LastSave > max(atof(GetConfig("saveperiod").c_str()),MIN_SAVE_PERDIOD)*3600) {
     151    Map[Service].LastSave = time(NULL);
     152        SaveHistory(Service);
     153  }
     154
    145155  // Resize buffer if necessary
    146156  int NEntries = atoi(GetConfig("numentries").c_str());
     
    266276  Map[Name].Format = Format;
    267277  Map[Name].MinAbsChange = 0.0;
     278  Map[Name].LastSave = 0.0;
    268279 
    269280  // Set minimum required change if given in configuratrion
     
    319330
    320331  // Save history buffer
     332  SaveHistory(Name);
     333
     334  Map.erase(Name);
     335}
     336
     337
     338//
     339// Save history buffer to file
     340//
     341void History::SaveHistory(string Name) {
     342
    321343  FILE *File = OpenFile(Name, "wb");
     344
    322345  if (File != NULL) {
    323346    fwrite(&Map[Name].Next, sizeof(Map[Name].Next), 1, File);                                   // Next pointer
     
    327350        // If error, try to delete (possibly erroneous) file
    328351        if (ferror(File) != 0) {
    329           if (remove(Name.c_str()) == -1) Message(WARN, "Error writing history file '%s' in RemoveService(), could also not delete file", Name.c_str());
    330           else Message(WARN, "Error writing history file '%s' in RemoveService(), deleted file", Name.c_str());
    331         }
    332     if (fclose(File) != 0) Message(WARN, "Error closing history file '%s' in RemoveService()", Name.c_str());;
    333   }
    334 
    335   Map.erase(Name);
    336 }
     352          if (remove(Name.c_str()) == -1) Message(WARN, "Error writing history file '%s', could also not delete file", Name.c_str());
     353          else Message(WARN, "Error writing history file '%s', deleted file", Name.c_str());
     354        }
     355    if (fclose(File) != 0) Message(WARN, "Error closing history file '%s'", Name.c_str());
     356  }
     357  else Message(WARN, "Could not open history file '%s' for writing", Name.c_str());;
     358}
     359
    337360
    338361//
  • fact/Evidence/readme.txt

    r10996 r11088  
    5151                        Signal handler calls abort() if invoked three times or more.
    525210/6/2011       Services can be excluded from History buffer (mainly for large event data services)
     5320/6/2011       History buffers periodically saved to file (in case program crash or computer reboot)
  • fact/tools/Edd/Edd.cc

    r10984 r11088  
    901901  // Menu bar
    902902  QMenu* Menu = menuBar()->addMenu("&Menu");
    903   Menu->addAction("New history plot", this, SLOT(MenuNewHistory()));
     903  Menu->addAction("Open history plot", this, SLOT(MenuNewHistory()));
     904  Menu->addAction("Send command", this, SLOT(MenuCommand()));
    904905  Menu->addAction("Raw data browser", this, SLOT(MenuRawDataBrowser()));
    905906  Menu->addSeparator();
     
    939940}
    940941
    941 // Open request for new history plot
     942// Open new history plot
    942943void GUI::MenuNewHistory() {
    943944
     
    956957  // Open dialog and open history window
    957958  QString Result = QInputDialog::getItem(this, "Edd Request",
    958     "Enter DIM service name", List, 0, true, &OK);
     959    "Enter or choose DIM service name (add index separated by colon)", List, 0, true, &OK);
     960
     961  // Check if cancelled or empty data
     962  List = Result.trimmed().split(":", QString::SkipEmptyParts);
     963  if (!OK || List.isEmpty()) return;
     964
     965  if (List.size() == 1) OpenHistory(List[0].toAscii().data(), 0);
     966  else OpenHistory(List[0].toAscii().data(), atoi(List[1].toAscii().data()));
     967}
     968
     969// Send command selected from list
     970void GUI::MenuCommand() {
     971
     972  QStringList List;
     973  char *Name, *Format;
     974  int Type;
     975  bool OK;
     976
     977  // Find all DIM commands and sort
     978  getServices("*");
     979  while ((Type = getNextService(Name, Format)) != 0) {
     980    if (Type==DimCOMMAND) List.append(Name);
     981  }
     982  List.sort();
     983
     984  // Open dialog and open window for entering command
     985  QString Result = QInputDialog::getItem(this, "Edd Request",
     986    "Enter or choose DIM command name", List, 0, true, &OK);
    959987  if (OK && !Result.isEmpty()) {
    960988    Result = Result.trimmed();
    961     QWidget *Hist = OpenHistory(Result.toAscii().data(), 0);
    962     if (Hist != NULL) Hist->show();
     989       
     990        QMainWindow *M = new QMainWindow;
     991        M->setCentralWidget(new QWidget(M));
     992        M->setStatusBar(new QStatusBar(M));
     993        M->setAttribute(Qt::WA_DeleteOnClose);
     994        QWidget *W = new EddCommand(Result.toAscii().data());
     995        QFormLayout *FormLayout = new QFormLayout(M->centralWidget());
     996        FormLayout->setRowWrapPolicy(QFormLayout::WrapAllRows);
     997        FormLayout->addRow("Enter argument for " + Result, W);
     998        M->show();
    963999  }
    9641000}
  • fact/tools/Edd/Edd.h

    r10904 r11088  
    142142    void MenuAbout();
    143143    void MenuNewHistory();
     144    void MenuCommand();
    144145    void MenuRawDataBrowser();
    145146        void DetachTab(int, bool=false);
Note: See TracChangeset for help on using the changeset viewer.