Browse Source

Graph rate of temperature change

Neal Wilson 11 years ago
parent
commit
a7012b4200
5 changed files with 208 additions and 2 deletions
  1. 32
    0
      config/Windows/productionroaster.xml
  2. 162
    0
      src/graphsettings.w
  3. 5
    1
      src/rate.w
  4. 4
    0
      src/resources.qrc
  5. 5
    1
      src/settings.w

+ 32
- 0
config/Windows/productionroaster.xml View File

69
 		var temperatureDisplays = new Array();
69
 		var temperatureDisplays = new Array();
70
 		var columnNames = new Array();
70
 		var columnNames = new Array();
71
 		var modbusdevices = new Array();
71
 		var modbusdevices = new Array();
72
+		var rateCalculators = new Array();
73
+		var rateadapters = new Array();
74
+		var rateoffsets = new Array();
75
+		var ratezeros = new Array();
72
 		var indicatorPanel = findChildObject(this, 'indicators');
76
 		var indicatorPanel = findChildObject(this, 'indicators');
73
 		var annotationPanel = findChildObject(this, 'controlpanel');
77
 		var annotationPanel = findChildObject(this, 'controlpanel');
74
 		var log = findChildObject(this, 'log');
78
 		var log = findChildObject(this, 'log');
421
 							indicatorPanel.addWidget(decorator);
425
 							indicatorPanel.addWidget(decorator);
422
 							rate.measurement.connect(indicator.setValue);
426
 							rate.measurement.connect(indicator.setValue);
423
 							temperatureDisplays.push(indicator);
427
 							temperatureDisplays.push(indicator);
428
+							rateCalculators.push(rate);
424
 						}
429
 						}
425
 					}
430
 					}
426
 				}
431
 				}
486
 			emitter.measurement.connect(log.newMeasurement);
491
 			emitter.measurement.connect(log.newMeasurement);
487
 			emitter.measurement.connect(graph.newMeasurement);
492
 			emitter.measurement.connect(graph.newMeasurement);
488
 		}
493
 		}
494
+		for(var i = channels.length; i < channels.length + rateCalculators.length; i++) {
495
+			var offset - new MeasurementTimeOffset(epoch);
496
+			rateoffsets.push(offset);
497
+			rateCalculators[i - channels.length].measurement.connect(offset.newMeasurement);
498
+			var adapter = new MeasurementAdapter(i + 1);
499
+			rateadapters.push(adapter);
500
+			offset.measurement.connect(adapter.newMeasurement);
501
+			var emitter = new ZeroEmitter(i + 1);
502
+			ratezeros.push(emitter);
503
+			rateCalculators[i - channels.length].measurement.connect(emitter.newMeasurement);
504
+			emitter.measurement.connect(graph.newMeasurement);
505
+		}
489
         start.clicked.connect(function() {
506
         start.clicked.connect(function() {
490
 			start.enabled = false;
507
 			start.enabled = false;
491
 			hasTranslated = false;
508
 			hasTranslated = false;
498
 				adapters[i].measurement.connect(log.newMeasurement);
515
 				adapters[i].measurement.connect(log.newMeasurement);
499
 				adapters[i].measurement.connect(graph.newMeasurement);
516
 				adapters[i].measurement.connect(graph.newMeasurement);
500
 			}
517
 			}
518
+			for(var i = 0; i < rateadapters.length; i++)
519
+			{
520
+				rateoffsets[i].setZeroTime(epoch);
521
+				ratezeros[i].emitZero();
522
+				rateadapters[i].measurement.connect(graph.newMeasurement);
523
+			}
501
 			timer.startTimer();
524
 			timer.startTimer();
502
             if(typeof(currentBatchInfo) == 'undefined') { } else {
525
             if(typeof(currentBatchInfo) == 'undefined') { } else {
503
                 query = new QSqlQuery();
526
                 query = new QSqlQuery();
545
 				adapters[i].measurement.disconnect(log.newMeasurement);
568
 				adapters[i].measurement.disconnect(log.newMeasurement);
546
 				adapters[i].measurement.disconnect(graph.newMeasurement);
569
 				adapters[i].measurement.disconnect(graph.newMeasurement);
547
 			}
570
 			}
571
+			for(var i = 0; i < rateadapters.length; i++)
572
+			{
573
+				rateadapters[i].measurement.disconnect(graph.newMeasurement);
574
+			}
548
             if(typeof(currentBatchInfo) == 'undefined') { } else {
575
             if(typeof(currentBatchInfo) == 'undefined') { } else {
549
                 lc = Number(QSettings.value("liveColumn"));
576
                 lc = Number(QSettings.value("liveColumn"));
550
                 var duration = log.lastTime(lc);
577
                 var duration = log.lastTime(lc);
686
 				zeroemitters[i].setColumn(c + i + 1);
713
 				zeroemitters[i].setColumn(c + i + 1);
687
 				log.setHeaderData(c + i + 1, columnNames[i]);
714
 				log.setHeaderData(c + i + 1, columnNames[i]);
688
 			}
715
 			}
716
+			for(var i = 0; i < rateadapters.length; i++)
717
+			{
718
+				rateadapters[i].setColumn(c + i + 1 + adapters.length);
719
+				ratezeros[i].setColumn(c + i + 1 + adapters.length);
720
+			}
689
 			log.setHeaderData(c + columnNames.length + 1, "Note");
721
 			log.setHeaderData(c + columnNames.length + 1, "Note");
690
             stop.setTemperatureColumn(c + 1);
722
             stop.setTemperatureColumn(c + 1);
691
 			stop.setAnnotationColumn(c + columnNames.length + 1);
723
 			stop.setAnnotationColumn(c + columnNames.length + 1);

+ 162
- 0
src/graphsettings.w View File

1
+@* Graph widget configuration.
2
+
3
+\noindent There are many features of the graph in Typica which ought to be
4
+configurable. Most of these cannot be configured until changes are made to the
5
+graph widget. The original motivation for the class comes from a need to
6
+configure secondary axes.
7
+
8
+@<Class declarations@>=
9
+class GraphSettingsWidget : public QWidget
10
+{
11
+	Q_OBJECT
12
+	public:
13
+		GraphSettingsWidget();
14
+};
15
+
16
+@ At present there are three types of data that the graph might present. This
17
+will likely be expanded as support for additional unit types are added.
18
+
19
+Different measurement categories are presently organized into different tabs.
20
+This will potentially need to change later as more features are added and the
21
+tab set becomes excessively large but for now this is the best that I can
22
+think of. If anybody with UI design experience would like to propose something
23
+better I would be glad to consider it.
24
+
25
+@<GraphSettingsWidget implementation@>=
26
+GraphSettingsWidget::GraphSettingsWidget() : QWidget(NULL)
27
+{
28
+	QTabWidget *graphCategories = new QTabWidget;
29
+	GraphSettingsRelativeTab *relative = new GraphSettingsRelativeTab;
30
+	graphCategories->addTab(relative, tr("Relative Temperatures"));
31
+	QVBoxLayout *layout = new QVBoxLayout;
32
+	layout->addWidget(graphCategories);
33
+	setLayout(layout);
34
+}
35
+
36
+@ Relative temperature measurements are different from the absolute temperature
37
+measurements that fall on the primary axis in that it is likely that negative
38
+values will be presented. These may be important and should not be hidden off
39
+the bottom of the graph, but the most important values will be in a relatively
40
+small range of positive values. The particulars will depend on the settings
41
+used to construct the relative values and the style of coffee roasting. It is
42
+also possible to disable the graphing of relative temperature measurements.
43
+
44
+@<Class declarations@>=
45
+class GraphSettingsRelativeTab : public QWidget
46
+{
47
+	Q_OBJECT
48
+	public:
49
+		GraphSettingsRelativeTab();
50
+	public slots:
51
+		void updateEnableSetting(bool enable);
52
+		void updateColorSetting(const QString &color);
53
+		void updateAxisSetting(const QString &gridList);
54
+		void updateUnit(int unit);
55
+		void showColorPicker();
56
+	private:
57
+		QLineEdit *colorEdit;
58
+};
59
+
60
+@ The constructor sets up the interface and restores any previous values from
61
+settings.
62
+
63
+@<GraphSettingsWidget implementation@>=
64
+GraphSettingsRelativeTab::GraphSettingsRelativeTab() : QWidget(NULL),
65
+	colorEdit(new QLineEdit)
66
+{
67
+	QSettings settings;
68
+	QVBoxLayout *layout = new QVBoxLayout;
69
+	QCheckBox *enable = new QCheckBox(tr("Graph relative temperatures"));
70
+	enable->setChecked(settings.value("settings/graph/relative/enable", true).toBool());
71
+	updateEnableSetting(enable->isChecked());
72
+	connect(enable, SIGNAL(toggled(bool)), this, SLOT(updateEnableSetting(bool)));
73
+	layout->addWidget(enable);
74
+	QHBoxLayout *colorLayout = new QHBoxLayout;
75
+	QLabel *colorLabel = new QLabel(tr("Axis color:"));
76
+	colorEdit->setText(settings.value("settings/graph/relative/color", "#000000").toString());
77
+	updateColorSetting(colorEdit->text());
78
+	connect(colorEdit, SIGNAL(textChanged(QString)), this, SLOT(updateColorSetting(QString)));
79
+	QToolButton *colorPickerButton = new QToolButton();
80
+	colorPickerButton->setIcon(QIcon::fromTheme("applications-graphics"));
81
+	connect(colorPickerButton, SIGNAL(clicked()), this, SLOT(showColorPicker()));
82
+	colorLayout->addWidget(colorLabel);
83
+	colorLayout->addWidget(colorEdit);
84
+	colorLayout->addWidget(colorPickerButton);
85
+	colorLayout->addStretch();
86
+	layout->addLayout(colorLayout);
87
+	QHBoxLayout *unitLayout = new QHBoxLayout;
88
+	QLabel *unitLabel = new QLabel(tr("Unit"));
89
+	QComboBox *unitSelector = new QComboBox;
90
+	unitSelector->addItem(tr("Fahrenheit"));
91
+	unitSelector->addItem(tr("Celsius"));
92
+	unitSelector->setCurrentIndex(settings.value("settings/graph/relative/unit", 0).toInt());
93
+	updateUnit(unitSelector->currentIndex());
94
+	connect(unitSelector, SIGNAL(currentIndexChanged(int)), this, SLOT(updateUnit(int)));
95
+	unitLayout->addWidget(unitLabel);
96
+	unitLayout->addWidget(unitSelector);
97
+	unitLayout->addStretch();
98
+	layout->addLayout(unitLayout);
99
+	QHBoxLayout *axisLayout = new QHBoxLayout;
100
+	QLabel *axisLabel = new QLabel(tr("Grid line positions (comma separated):"));
101
+	QLineEdit *axisEdit = new QLineEdit;
102
+	axisEdit->setText(settings.value("settings/graph/relative/grid", "-300, -100, -10, 0, 10, 30, 50").toString());
103
+	updateAxisSetting(axisEdit->text());
104
+	connect(axisEdit, SIGNAL(textChanged(QString)), this, SLOT(updateAxisSetting(QString)));
105
+	axisLayout->addWidget(axisLabel);
106
+	axisLayout->addWidget(axisEdit);
107
+	layout->addLayout(axisLayout);
108
+	layout->addStretch();
109
+	setLayout(layout);
110
+}
111
+
112
+@ A set of methods updates the settings as they are adjusted.
113
+
114
+@<GraphSettingsWidget implementation@>=
115
+void GraphSettingsRelativeTab::updateEnableSetting(bool enabled)
116
+{
117
+	QSettings settings;
118
+	settings.setValue("settings/graph/relative/enable", enabled);
119
+}
120
+
121
+void GraphSettingsRelativeTab::updateColorSetting(const QString &color)
122
+{
123
+	QSettings settings;
124
+	settings.setValue("settings/graph/relative/color", color);
125
+}
126
+
127
+void GraphSettingsRelativeTab::updateAxisSetting(const QString &gridList)
128
+{
129
+	QSettings settings;
130
+	QString settingValue;
131
+	QStringList points = gridList.split(QRegExp("[\\s,]+"), QString::SkipEmptyParts);
132
+	QStringList numbers;
133
+	foreach(QString text, points)
134
+	{
135
+		bool okay = false;
136
+		text.toDouble(&okay);
137
+		if(okay)
138
+		{
139
+			numbers.append(text);
140
+		}
141
+	}
142
+	numbers.removeDuplicates();
143
+	settings.setValue("settings/graph/relative/grid", numbers.join(","));
144
+}
145
+
146
+void GraphSettingsRelativeTab::updateUnit(int unit)
147
+{
148
+	QSettings settings;
149
+	settings.setValue("settings/graph/relative/unit", unit);
150
+}
151
+
152
+@ When selecting a color, it is possible to either type the color into the line
153
+edit directly or use a color picker to select the color graphically. A tool
154
+button displays a color picker and pushes the selected color into the line edit
155
+which in turn updates the setting.
156
+
157
+@<GraphSettingsWidget implementation@>=
158
+void GraphSettingsRelativeTab::showColorPicker()
159
+{
160
+	QColor color = QColorDialog::getColor(QColor(colorEdit->text()), this);
161
+	colorEdit->setText(color.name());
162
+}

+ 5
- 1
src/rate.w View File

89
 instead of on the data we wish we had should result in better stability in the
89
 instead of on the data we wish we had should result in better stability in the
90
 derived series.
90
 derived series.
91
 
91
 
92
+The measurement will carry the fact that it is a relative measurement.
93
+
92
 @<Calculate rate of change@>=
94
 @<Calculate rate of change@>=
93
 double mdiff = cache.back().temperature() - cache.front().temperature();
95
 double mdiff = cache.back().temperature() - cache.front().temperature();
94
 double tdiff = cache.front().time().msecsTo(cache.back().time()) / 1000.0;
96
 double tdiff = cache.front().time().msecsTo(cache.back().time()) / 1000.0;
95
 double dps = mdiff / tdiff;
97
 double dps = mdiff / tdiff;
96
 double scale = dps * st;
98
 double scale = dps * st;
97
-emit measurement(Measurement(scale, cache.back().time(), cache.back().scale()));
99
+Measurement value(scale, cache.back().time(), cache.back().scale());
100
+value.insert("relative", true);
101
+emit measurement(value);
98
 
102
 
99
 @ The rest of the class implementation is trivial.
103
 @ The rest of the class implementation is trivial.
100
 
104
 

+ 4
- 0
src/resources.qrc View File

19
         <file>resources/fonts/texgyrepagella-bolditalic.otf</file>
19
         <file>resources/fonts/texgyrepagella-bolditalic.otf</file>
20
         <file>resources/fonts/texgyrepagella-italic.otf</file>
20
         <file>resources/fonts/texgyrepagella-italic.otf</file>
21
         <file>resources/fonts/texgyrepagella-regular.otf</file>
21
         <file>resources/fonts/texgyrepagella-regular.otf</file>
22
+        <file>resources/icons/tango/16x16/categories/applications-graphics.png</file>
23
+        <file>resources/icons/tango/22x22/categories/applications-graphics.png</file>
24
+        <file>resources/icons/tango/32x32/categories/applications-graphics.png</file>
25
+        <file>resources/icons/tango/scalable/categories/applications-graphics.svg</file>
22
     </qresource>
26
     </qresource>
23
 </RCC>
27
 </RCC>

+ 5
- 1
src/settings.w View File

26
 	QTabWidget *settingsTab = new QTabWidget;
26
 	QTabWidget *settingsTab = new QTabWidget;
27
 	DeviceConfigurationWindow *deviceSettings = new DeviceConfigurationWindow;
27
 	DeviceConfigurationWindow *deviceSettings = new DeviceConfigurationWindow;
28
 	settingsTab->addTab(deviceSettings, tr("Roasters"));
28
 	settingsTab->addTab(deviceSettings, tr("Roasters"));
29
+	GraphSettingsWidget *graphSettings = new GraphSettingsWidget;
30
+	settingsTab->addTab(graphSettings, tr("Graph"));
29
 	setCentralWidget(settingsTab);
31
 	setCentralWidget(settingsTab);
30
 }
32
 }
31
 
33
 
49
 @<Set up the scripting engine@>=
51
 @<Set up the scripting engine@>=
50
 constructor = engine->newFunction(constructSettingsWindow);
52
 constructor = engine->newFunction(constructSettingsWindow);
51
 value = engine->newQMetaObject(&DeviceConfigurationWindow::staticMetaObject, constructor);
53
 value = engine->newQMetaObject(&DeviceConfigurationWindow::staticMetaObject, constructor);
52
-engine->globalObject().setProperty("SettingsWindow", value);
54
+engine->globalObject().setProperty("SettingsWindow", value);
55
+
56
+@i graphsettings.w

Loading…
Cancel
Save