Browse Source

Add multi-range timers

Neal Wilson 9 years ago
parent
commit
5973f49cc6
2 changed files with 244 additions and 6 deletions
  1. 139
    1
      config/Windows/productionroaster.xml
  2. 105
    5
      src/typica.w

+ 139
- 1
config/Windows/productionroaster.xml View File

67
 		var roasterIndex = roasterlist.currentIndex;
67
 		var roasterIndex = roasterlist.currentIndex;
68
 		var channels = new Array();
68
 		var channels = new Array();
69
 		var annotationButtons = new Array();
69
 		var annotationButtons = new Array();
70
+                var rangetimers = new Array();
70
 		var nidevices = new Array();
71
 		var nidevices = new Array();
71
 		var dataqsdkdevices = new Array();
72
 		var dataqsdkdevices = new Array();
72
 		var jsdevices = new Array();
73
 		var jsdevices = new Array();
614
 				}
615
 				}
615
                                 else if(driverReference.driver == "coolingtimer")
616
                                 else if(driverReference.driver == "coolingtimer")
616
                                 {
617
                                 {
617
-                                    var coolingTimer = new TimerDisplay;
618
+                                    var coolingTimer = new TimerDisplay();
618
                                     coolingTimer.setDisplayFormat("mm:ss");
619
                                     coolingTimer.setDisplayFormat("mm:ss");
619
                                     var decorator = new WidgetDecorator(coolingTimer, configModel.data(driverIndex, 0), 2);
620
                                     var decorator = new WidgetDecorator(coolingTimer, configModel.data(driverIndex, 0), 2);
620
                                     indicatorPanel.addWidget(decorator);
621
                                     indicatorPanel.addWidget(decorator);
625
                                     coolingTimer.setResetValue(timeValue);
626
                                     coolingTimer.setResetValue(timeValue);
626
                                     stop.clicked.connect(coolingTimer.startTimer);
627
                                     stop.clicked.connect(coolingTimer.startTimer);
627
                                 }
628
                                 }
629
+                                else if(driverReference.driver == "rangetimer")
630
+                                {
631
+                                    rangetimers.push(new TimerDisplay());
632
+                                    var rangeTimer = rangetimers[rangetimers.length - 1];
633
+                                    rangeTimer.setDisplayFormat("mm:ss");
634
+                                    var decorator = new WidgetDecorator(rangeTimer, configModel.data(driverIndex, 0), 2);
635
+                                    indicatorPanel.addWidget(decorator);
636
+                                    stop.clicked.connect(rangeTimer.stopTimer);
637
+                                    start.clicked.connect(rangeTimer.reset);
638
+                                    if(driverReference.starttrigger == "batch")
639
+                                    {
640
+                                        start.clicked.connect(rangeTimer.startTimer);
641
+                                    }
642
+                                    else if(driverReference.starttrigger == "manual")
643
+                                    {
644
+                                        var startButton = new AnnotationButton(driverReference.startbuttontext);
645
+                                        startButton.clicked.connect(rangeTimer.startTimer);
646
+                                        annotationPanel.addWidget(startButton);
647
+                                        tabControls.push(button);
648
+                                    }
649
+                                    else if(driverReference.starttrigger == "value")
650
+                                    {
651
+                                        var startTrigger = new ThresholdDetector();
652
+                                        startTrigger.setThreshold(Number(driverReference.startvalue));
653
+                                        var scope = new Object;
654
+                                        scope.invoke = function() {
655
+                                            if(timer.running && !(arguments.callee.rangeTimer.running))
656
+                                            {
657
+                                                arguments.callee.rangeTimer.startTimer();
658
+                                            }
659
+                                        };
660
+                                        scope.invoke.rangeTimer = rangeTimer;
661
+                                        startTrigger.timeForValue.connect(scope.invoke);
662
+                                        for(var j = 0; j < columnNames.length; j++)
663
+                                        {
664
+                                            if(columnNames[j] == driverReference.startcolumnname)
665
+                                            {
666
+                                                channels[j].newData.connect(startTrigger.newMeasurement);
667
+                                            }
668
+                                        }
669
+                                    }
670
+                                    stop.clicked.connect(rangeTimer.stopTimer)
671
+                                    if(driverReference.stoptrigger == "manual")
672
+                                    {
673
+                                        var stopButton = new AnnotationButton(driverReference.stopbuttontext);
674
+                                        stopButton.clicked.connect(rangeTimer.stopTimer);
675
+                                        annotationPanel.addWidget(stopButton);
676
+                                        tabControls.push(button);
677
+                                    }
678
+                                    else if(driverReference.stoptrigger == "value")
679
+                                    {
680
+                                        var stopTrigger = new ThresholdDetector();
681
+                                        stopTrigger.setThreshold(Number(driverReference.stopvalue));
682
+                                        stopTrigger.timeForValue.connect(rangeTimer.stopTimer);
683
+                                        for(var j = 0; j < columnNames.length; j++)
684
+                                        {
685
+                                            if(columnNames[j] == driverReference.stopcolumnname)
686
+                                            {
687
+                                                channels[j].newData.connect(stopTrigger.newMeasurement);
688
+                                            }
689
+                                        }
690
+                                    }
691
+                                    rangeTimer.reportingName = configModel.data(driverIndex, 0);
692
+                                    rangeTimer.report = function() {
693
+                                        return "" + this.reportingName + ": " + this.value;
694
+                                    };
695
+                                }
696
+                                else if(driverReference.driver = "multirangetimer")
697
+                                {
698
+                                    var rangeTimer = new TimerDisplay();
699
+                                    rangeTimer.setDisplayFormat("mm:ss");
700
+                                    var decorator = new WidgetDecorator(rangeTimer, configModel.data(driverIndex, 0), 2);
701
+                                    indicatorPanel.addWidget(decorator);
702
+                                    var rangenames = driverReference.rangenames;
703
+                                    var endtemps = driverReference.endtemps;
704
+                                    var names = rangenames.slice(2, rangenames.length - 2).split(", ");
705
+                                    rangeTimer.rangeNames = new Array();
706
+                                    for(var j = 0; j < names.length; j++)
707
+                                    {
708
+                                        var name = names[j].slice(1, names[j].length - 1);
709
+                                        rangeTimer.rangeNames.push(name);
710
+                                    }
711
+                                    rangeTimer.rangeEnds = new Array();
712
+                                    var ends = endtemps.slice(2, endtemps.length - 2).split(", ");
713
+                                    for(var j = 0; j < ends.length; j++)
714
+                                    {
715
+                                        rangeTimer.rangeEnds.push(Number(ends[j]));
716
+                                    }
717
+                                    rangeTimer.currentRange = 0;
718
+                                    rangeTimer.results = new Array();
719
+                                    rangeTimer.trigger = new ThresholdDetector();
720
+                                    for(var j = 0; j < columnNames.length; j++)
721
+                                    {
722
+                                        if(columnNames[j] == driverReference.trigger)
723
+                                        {
724
+                                            channels[j].newData.connect(rangeTimer.trigger.newMeasurement);
725
+                                        }
726
+                                    }
727
+                                    rangeTimer.startRanges = function() {
728
+                                        arguments.callee.timer.currentRange = 0;
729
+                                        arguments.callee.timer.results.splice(0, arguments.callee.timer.results.length);
730
+                                        arguments.callee.timer.trigger.setThreshold(arguments.callee.timer.rangeEnds[0]);
731
+                                    };
732
+                                    rangeTimer.startRanges.timer = rangeTimer;
733
+                                    start.clicked.connect(rangeTimer.startTimer);
734
+                                    start.clicked.connect(rangeTimer.startRanges);
735
+                                    rangeTimer.transition = function() {
736
+                                        arguments.callee.timer.stopTimer();
737
+                                        arguments.callee.timer.results.push(arguments.callee.timer.value);
738
+                                        arguments.callee.timer.reset();
739
+                                        arguments.callee.timer.startTimer();
740
+                                        arguments.callee.timer.currentRange++;
741
+                                        if(arguments.callee.timer.currentRange <= arguments.callee.timer.rangeEnds.length)
742
+                                        {
743
+                                            arguments.callee.timer.trigger.setThreshold(arguments.callee.timer.rangeEnds[arguments.callee.timer.currentRange]);
744
+                                        }
745
+                                    };
746
+                                    rangeTimer.transition.timer = rangeTimer;
747
+                                    rangeTimer.trigger.timeForValue.connect(rangeTimer.transition);
748
+                                    stop.clicked.connect(rangeTimer.transition);
749
+                                    stop.clicked.connect(rangeTimer.stopTimer);
750
+                                    rangeTimer.report = function() {
751
+                                        var retval = "";
752
+                                        for(var j = 0; j < arguments.callee.timer.results.length; j++)
753
+                                        {
754
+                                            retval += arguments.callee.timer.rangeNames[j] + ": " + arguments.callee.timer.results[j] + "\n";
755
+                                        }
756
+                                        return retval;
757
+                                    };
758
+                                    rangeTimer.report.timer = rangeTimer;
759
+                                    rangetimers.push(rangeTimer);
760
+                                }
628
 			}
761
 			}
629
 		}
762
 		}
630
 		for(var i = 1; i < tabControls.length; i++)
763
 		for(var i = 1; i < tabControls.length; i++)
818
 				log.addOutputAnnotationColumn(lc + channels.length - channelSkip);
951
 				log.addOutputAnnotationColumn(lc + channels.length - channelSkip);
819
                 var filename = log.saveTemporary();
952
                 var filename = log.saveTemporary();
820
                 currentBatchInfo.tempData = filename;
953
                 currentBatchInfo.tempData = filename;
954
+                var notes = findChildObject(currentBatchInfo, 'annotation');
955
+                for(var i = 0; i < rangetimers.length; i++)
956
+                {
957
+                    notes.append(rangetimers[i].report());
958
+                }
821
                 currentBatchInfo.raise();
959
                 currentBatchInfo.raise();
822
                 currentBatchInfo.activateWindow();
960
                 currentBatchInfo.activateWindow();
823
             }
961
             }

+ 105
- 5
src/typica.w View File

4155
     return QScriptValue();
4155
     return QScriptValue();
4156
 }
4156
 }
4157
 
4157
 
4158
+
4158
 @** Application Configuration.
4159
 @** Application Configuration.
4159
 
4160
 
4160
 \noindent While \pn{} is intended as a data logging application, the diversity
4161
 \noindent While \pn{} is intended as a data logging application, the diversity
19176
 model.
19177
 model.
19177
 
19178
 
19178
 @<Populate model column from list@>=
19179
 @<Populate model column from list@>=
19179
-for(int i = 0; i < itemList.size(); i++)
19180
+for(int j = 0; j < itemList.size(); j++)
19180
 {
19181
 {
19181
-    tablemodel->setData(tablemodel->index(i, column),
19182
-                   QVariant(itemList.at(i).toDouble()),
19182
+    tablemodel->setData(tablemodel->index(j, column),
19183
+                   QVariant(itemList.at(j).toDouble()),
19183
                    Qt::DisplayRole);
19184
                    Qt::DisplayRole);
19184
 }
19185
 }
19185
 
19186
 
19523
     }
19524
     }
19524
 }
19525
 }
19525
 
19526
 
19526
-
19527
-
19528
 @ The widget is registered with the configuration system.
19527
 @ The widget is registered with the configuration system.
19529
 @<Register device configuration widgets@>=
19528
 @<Register device configuration widgets@>=
19530
 app.registerDeviceConfigurationWidget("rangetimer",
19529
 app.registerDeviceConfigurationWidget("rangetimer",
19535
 @<Class implementations@>=
19534
 @<Class implementations@>=
19536
 @<RangeTimerConfWidget implementation@>
19535
 @<RangeTimerConfWidget implementation@>
19537
 
19536
 
19537
+@ The multirange timer is a little different. To keep configuration tractible,
19538
+this is slightly less general purpose than the range timer and only supports
19539
+automatic triggering on a single data series.
19540
+
19541
+@<Class declarations@>=
19542
+class MultiRangeTimerConfWidget : public BasicDeviceConfigurationWidget
19543
+{
19544
+    @[Q_OBJECT@]@/
19545
+    public:@/
19546
+        @[Q_INVOKABLE@]@, MultiRangeTimerConfWidget(DeviceTreeModel *model,
19547
+                                                    const QModelIndex &index);
19548
+    @[private slots@]:@/
19549
+        void updateColumnName(const QString &text);
19550
+        void updateRangeData();
19551
+    private:@/
19552
+        SaltModel *tablemodel;
19553
+};
19554
+
19555
+@ These limitations allow a rather small set of controls for configuration. A
19556
+line edit to specify the data series used for automatic triggering and a table
19557
+which specifies the name of the timed range and an ending temperature.
19558
+
19559
+@<MultiRangeTimerConfWidget implementation@>=
19560
+MultiRangeTimerConfWidget::MultiRangeTimerConfWidget(DeviceTreeModel *model,
19561
+                                                     const QModelIndex &index)
19562
+: BasicDeviceConfigurationWidget(model, index), tablemodel(new SaltModel(2))
19563
+{
19564
+    QFormLayout *layout = new QFormLayout;
19565
+    QLineEdit *trigger = new QLineEdit;
19566
+    layout->addRow(tr("Trigger column name:"), trigger);
19567
+    tablemodel->setHeaderData(0, Qt::Horizontal, "Range Name");
19568
+    tablemodel->setHeaderData(1, Qt::Horizontal, "End Temperature");
19569
+    QTableView *rangeTable = new QTableView;
19570
+    rangeTable->setModel(tablemodel);
19571
+    layout->addRow(tr("Range data:"), rangeTable);
19572
+    @<Get device configuration data for current node@>@;
19573
+    for(int i = 0; i < configData.size(); i++)
19574
+    {
19575
+        node = configData.at(i).toElement();
19576
+        if(node.attribute("name") == "trigger")
19577
+        {
19578
+            trigger->setText(node.attribute("value"));
19579
+        }
19580
+        else if(node.attribute("name") == "rangenames")
19581
+        {
19582
+            QString data = node.attribute("value");
19583
+            if(data.length() > 3)
19584
+            {
19585
+                data.chop(2);
19586
+                data = data.remove(0, 2);
19587
+            }
19588
+            QStringList itemList = data.split(", ");
19589
+            for(int j = 0; j < itemList.size(); j++)
19590
+            {
19591
+                QString item = itemList.at(j);
19592
+                item.chop(1);
19593
+                item = item.remove(0, 1);
19594
+                tablemodel->setData(tablemodel->index(j, 0),
19595
+                                    QVariant(item), Qt::DisplayRole);
19596
+            }
19597
+        }
19598
+        else if(node.attribute("name") == "endtemps")
19599
+        {
19600
+            @<Convert numeric array literal to list@>@;
19601
+            int column = 1;
19602
+            @<Populate model column from list@>@;
19603
+        }
19604
+    }
19605
+    updateColumnName(trigger->text());
19606
+    updateRangeData();
19607
+    connect(trigger, SIGNAL(textEdited(QString)), this, SLOT(updateColumnName(QString)));
19608
+    connect(tablemodel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(updateRangeData()));
19609
+    setLayout(layout);
19610
+}
19611
+
19612
+@ The update mechanisms are reasonably straightforward. Table updates just
19613
+refresh the entire table instead of attempting to be clever about updating only
19614
+the element that changed.
19615
+
19616
+@<MultiRangeTimerConfWidget implementation@>=
19617
+void MultiRangeTimerConfWidget::updateRangeData()
19618
+{
19619
+    updateAttribute("rangenames", tablemodel->quotedArrayLiteral(0, Qt::DisplayRole));
19620
+    updateAttribute("endtemps", tablemodel->arrayLiteral(1, Qt::DisplayRole));
19621
+}
19622
+
19623
+void MultiRangeTimerConfWidget::updateColumnName(const QString &text)
19624
+{
19625
+    updateAttribute("trigger", text);
19626
+}
19627
+
19628
+@ The widget is registered with the configuration system.
19629
+@<Register device configuration widgets@>=
19630
+app.registerDeviceConfigurationWidget("multirangetimer",
19631
+MultiRangeTimerConfWidget::staticMetaObject);
19632
+
19633
+@ The implementation is in the main source file.
19634
+
19635
+@<Class implementations@>=
19636
+@<MultiRangeTimerConfWidget implementation@>
19637
+
19538
 @* Profile Translation Configuration Widget.
19638
 @* Profile Translation Configuration Widget.
19539
 
19639
 
19540
 \noindent Configuring profile translation requires knowing which column to use
19640
 \noindent Configuring profile translation requires knowing which column to use

Loading…
Cancel
Save