Bladeren bron

ValueAnnotation class and configuration.

Neal Wilson 11 jaren geleden
bovenliggende
commit
a081ef8b32
2 gewijzigde bestanden met toevoegingen van 238 en 11 verwijderingen
  1. 13
    11
      src/typica.w
  2. 225
    0
      src/valueannotation.w

+ 13
- 11
src/typica.w Bestand weergeven

@@ -18183,7 +18183,7 @@ class LinearSplineInterpolationConfWidget : public BasicDeviceConfigurationWidge
18183 18183
 		void updateDestinationColumn(const QString &dest);
18184 18184
 		void updateKnots();
18185 18185
 	private:@/
18186
-		SaltModel *knotmodel;
18186
+		SaltModel *model;
18187 18187
 };
18188 18188
 
18189 18189
 @ This is configured by specifying a source column name, a destination column
@@ -18192,17 +18192,17 @@ the mapping data, we store each column of the table in its own attribute.
18192 18192
 
18193 18193
 @<LinearSplineInterpolationConfWidget implementation@>=
18194 18194
 LinearSplineInterpolationConfWidget::LinearSplineInterpolationConfWidget(DeviceTreeModel *model, const QModelIndex &index)
18195
-: BasicDeviceConfigurationWidget(model, index), knotmodel(new SaltModel(2))
18195
+: BasicDeviceConfigurationWidget(model, index), model(new SaltModel(2))
18196 18196
 {
18197 18197
 	QFormLayout *layout = new QFormLayout;
18198 18198
 	QLineEdit *source = new QLineEdit;
18199 18199
 	layout->addRow(tr("Source column name:"), source);
18200 18200
 	QLineEdit *destination = new QLineEdit;
18201 18201
 	layout->addRow(tr("Destination column name:"), destination);
18202
-	knotmodel->setHeaderData(0, Qt::Horizontal, "Input");
18203
-	knotmodel->setHeaderData(1, Qt::Horizontal, "Output");
18202
+	model->setHeaderData(0, Qt::Horizontal, "Input");
18203
+	model->setHeaderData(1, Qt::Horizontal, "Output");
18204 18204
 	QTableView *mappingTable = new QTableView;
18205
-	mappingTable->setModel(knotmodel);
18205
+	mappingTable->setModel(model);
18206 18206
 	NumericDelegate *delegate = new NumericDelegate;
18207 18207
 	mappingTable->setItemDelegate(delegate);
18208 18208
 	layout->addRow(tr("Mapping data:"), mappingTable);
@@ -18237,7 +18237,7 @@ LinearSplineInterpolationConfWidget::LinearSplineInterpolationConfWidget(DeviceT
18237 18237
 	updateKnots();
18238 18238
 	connect(source, SIGNAL(textEdited(QString)), this, SLOT(updateSourceColumn(QString)));
18239 18239
 	connect(destination, SIGNAL(textEdited(QString)), this, SLOT(updateDestinationColumn(QString)));
18240
-	connect(knotmodel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(updateKnots()));
18240
+	connect(model, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(updateKnots()));
18241 18241
 	setLayout(layout);
18242 18242
 }
18243 18243
 
@@ -18260,9 +18260,9 @@ model.
18260 18260
 @<Populate model column from list@>=
18261 18261
 for(int i = 0; i < itemList.size(); i++)
18262 18262
 {
18263
-	knotmodel->setData(knotmodel->index(i, column),
18264
-	                   QVariant(itemList.at(i).toDouble()),
18265
-                       Qt::DisplayRole);
18263
+	model->setData(model->index(i, column),
18264
+	               QVariant(itemList.at(i).toDouble()),
18265
+                   Qt::DisplayRole);
18266 18266
 }
18267 18267
 
18268 18268
 @ When data in the table is changed we simply overwrite any previously saved
@@ -18271,8 +18271,8 @@ data with the current data.
18271 18271
 @<LinearSplineInterpolationConfWidget implementation@>=
18272 18272
 void LinearSplineInterpolationConfWidget::updateKnots()
18273 18273
 {
18274
-	updateAttribute("sourcevalues", knotmodel->arrayLiteral(0, Qt::DisplayRole));
18275
-	updateAttribute("destinationvalues", knotmodel->arrayLiteral(1, Qt::DisplayRole));
18274
+	updateAttribute("sourcevalues", model->arrayLiteral(0, Qt::DisplayRole));
18275
+	updateAttribute("destinationvalues", model->arrayLiteral(1, Qt::DisplayRole));
18276 18276
 }
18277 18277
 
18278 18278
 void LinearSplineInterpolationConfWidget::updateSourceColumn(const QString &source)
@@ -18385,6 +18385,8 @@ app.registerDeviceConfigurationWidget("translation", TranslationConfWidget::stat
18385 18385
 
18386 18386
 @i scales.w
18387 18387
 
18388
+@i valueannotation.w
18389
+
18388 18390
 @** Local changes.
18389 18391
 
18390 18392
 \noindent This is the end of \pn{} as distributed by its author. It is expected

+ 225
- 0
src/valueannotation.w Bestand weergeven

@@ -0,0 +1,225 @@
1
+@** Annotations for Values.
2
+
3
+\noindent In circumstances where a control setting is logged but this setting
4
+changes infrequently and has a small number of possible values, it is sometimes
5
+useful to not log the control values throughout the roast but rather simply add
6
+an annotation when the control value changes. It will commonly be desirable to
7
+also provide an annotation representing the current state at the start of every
8
+batch.
9
+
10
+To support this feature, there must be a configuration widget which can be used
11
+to identify which data series should be monitored and what annotations should
12
+be produced for what values.
13
+
14
+@<Class declrations@>=
15
+class ValueAnnotationConfWidget : public BasicDeviceConfigurationWidget
16
+{
17
+	Q_OBJECT
18
+	public:
19
+		Q_INVOKABLE ValueAnnotationConfWidget(DeviceTreeModel *model,
20
+                                              const QModelIndex &index);
21
+	private slots:
22
+		void updateSourceColumn(const QString &source);
23
+		void updateAnnotations();
24
+		void updateStart(bool noteOnStart);
25
+	private:
26
+		SaltModel *model;
27
+};
28
+
29
+@ The constructor sets up the configuration interface requesting a source
30
+column name, if an annotation should be emitted at the start of the batch, and
31
+what annotations should be produced for what values.
32
+
33
+@<ValueAnnotationConfWidget implementation@>=
34
+ValueAnnotationConfWidget::ValueAnnotationConfWidget(DeviceTreeModel *model,
35
+                                                     const QModelIndex &index)
36
+: BasicDeviceConfigurationWidget(model, index),
37
+  model(new SaltModel(2))
38
+{
39
+	QFormLayout *layout = new QFormLayout;
40
+	QLineEdit *source = new QLineEdit;
41
+	layout->addRow(tr("Source column name:"), source);
42
+	QCheckBox *noteOnStart = new QCheckBox(tr("Produce Start State Annotation"));
43
+	noteOnStart->setChecked(true);
44
+	layout->addRow(noteOnStart);
45
+	model->setHeaderData(0, Qt::Horizontal, "Value");
46
+	model->setHeaderData(1, Qt::Horizontal, "Annotation");
47
+	QTableView *annotationTable = new QTableView;
48
+	annotationTable->setModel(model);
49
+	NumericDelegate *delegate = new NumericDelegate;
50
+	annotationTable->setItemDelegateForColumn(0, delegate);
51
+	layout->addRow(tr("Annotations for values:"), annotationTable);
52
+	@<Get device configuration data for current node@>=
53
+	for(int i = 0; i < configData.size(); i++)
54
+	{
55
+		node = configData.at(i).toElement();
56
+		if(node.attribute("name") == "source")
57
+		{
58
+			source->setText(node.attribute("value"));
59
+		}
60
+		else if(node.attribute("name") == "emitOnStart")
61
+		{
62
+			noteOnStart->setChecked(node.attribute("value") == "true" ? true : false);
63
+		}
64
+		else if(node.attribute("name") == "measuredValues")
65
+		{
66
+			@<Convert numeric array literal to list@>@;
67
+			int column = 0;
68
+			@<Populate model column from list@>@;
69
+		}
70
+		else if(node.attribute("name") == "annotations")
71
+		{
72
+			@<Convert numeric array literal to list@>@;
73
+			int column = 1;
74
+			@<Populate model column from list@>@;
75
+		}
76
+	}
77
+	updateSourceColumn(source->text());
78
+	updateStart(noteOnStart->isChecked());
79
+	updateAnnotations();
80
+	connect(source, SIGNAL(textEdited(QString)), this, SLOT(updateSourceColumn(QString)));
81
+	connect(noteOnStart, SIGNAL(toggled(bool)), this, SLOT(updateStart(bool)));
82
+	connect(model, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(updateAnnotations()));
83
+	setLayout(layout);
84
+}
85
+
86
+@ To update the table data, the measued values and annotations are saved in
87
+separate lists.
88
+
89
+@<ValueAnnotationConfWidget implementation@>=
90
+void ValueAnnotationConfWidget::updateAnnotations()
91
+{
92
+	updateAttribute("measuredValues", model->arrayLiteral(0, Qt::DisplayRole));
93
+	updateAttribute("annotations", model->arrayLiteral(1, Qt::DisplayRole));
94
+}
95
+
96
+@ The other settings are updated based on values passed through the parameter
97
+to the update method.
98
+
99
+@<ValueAnnotationConfWidget implementation@>=
100
+void ValueAnnotationConfWidget::updateSourceColumn(const QString &source)
101
+{
102
+	updateAttribute("source", source);
103
+}
104
+
105
+void ValueAnnotationConfWidget::updateStart(bool noteOnStart)
106
+{
107
+	updateAttribute("emitOnStart", noteOnStart);
108
+}
109
+
110
+@ The widget is registered with the configuration system.
111
+
112
+@<Register device configuration widgets@>=
113
+app.registerDeviceConfigurationWidget("valueannotation",
114
+	ValueAnnotationConfWidget::staticMetaObjet);
115
+
116
+@ While it is possible to implement this feature with |ThresholdDetector|
117
+objects, the code to handle these would be difficult to understand and there
118
+would be excessive overhead in moving measurements through all of these.
119
+Instead, we create a new class that watches for any sort of measurement
120
+change and produces the annotation signals directly.
121
+
122
+As measured values are represented as a |double|, a small value should be
123
+provided such that comparisons are not against the value directly but instead
124
+are against the value plus or minus this other small value.
125
+
126
+Method names have been chosen to be compatible with the |AnnotationButton|
127
+class.
128
+
129
+@<Class declarations@>=
130
+class ValueAnnotation : public QObject
131
+{
132
+	Q_OBJECT
133
+	public:
134
+		ValueAnnotation();
135
+		Q_INVOKABLE void setAnnotation(double value, const QString &annotation);
136
+	public slots:
137
+		void newMeasurement(Measurement measure);
138
+		void annotate();
139
+		void setAnnotationColumn(int column);
140
+		void setTemperatureColumn(int column);
141
+		void setTolerance(double epsilon);
142
+	signals:
143
+		void annotation(QString annotation, int tempcolumn, int notecolumn);
144
+	private:
145
+		int lastIndex;
146
+		int annotationColumn;
147
+		int measurementColumn;
148
+		QList<double> values;
149
+		QStringList annotations;
150
+		double tolerance;
151
+}
152
+
153
+@ Most of the work of this class happens in the |newMeasurement| method. This
154
+compares the latest measurement with every value that has an associated
155
+annotation. If the value is near enough to a value in the list, the index of
156
+that value is compared with the index of the previous annotation (if any) and
157
+if the indices are different, the appropriate annotation is emitted.
158
+
159
+@<ValueAnnotation implementation@>=
160
+void ValueAnnotation::newMeasurement(Measurement measure)
161
+{
162
+	for(int i = 0; i < values.size(); i++)
163
+	{
164
+		if(measure.temperature() > values.at(i) - tolerance &&
165
+		   measure.temperature() < values.at(i) + tolerance)
166
+		{
167
+			if(i != lastIndex)
168
+			{
169
+				lastIndex = i;
170
+				emit annotation(annotations.at(i), measurementColumn, annotationColumn);
171
+			}
172
+		}
173
+	}
174
+}
175
+
176
+@ Another method is used to emit an annotation matching the current state at
177
+the start of a batch if that is desired. This will not produce any output if
178
+no state has yet been matched.
179
+
180
+@<ValueAnnotation implementation@>=
181
+void ValueAnnotation::annotate()
182
+{
183
+	if(lastIndex > -1)
184
+	{
185
+		emit annotation(annotations.at(lastIndex), measurementColumn, annotationColumn);
186
+	}
187
+}
188
+
189
+@ Values and annotations are added to separate lists with new mappings always
190
+appended. Entries are never removed from these lists.
191
+
192
+@<ValueAnnotation implementation@>=
193
+void ValueAnnotation::setAnnotation(double value, const QString &annotation)
194
+{
195
+	values.append(value);
196
+	annotations.append(annotation);
197
+}
198
+
199
+@ The remaining setter methods are trivial similarly trivial.
200
+
201
+@<ValueAnnotation implementation@>=
202
+void ValueAnnotation::setAnnotationColumn(int column)
203
+{
204
+	annotationColumn = column;
205
+}
206
+
207
+void ValueAnnotation::setTemperatureColumn(int column)
208
+{
209
+	measurementColumn = column;
210
+}
211
+
212
+void ValueAnnotation::setTolerance(double epsilon)
213
+{
214
+	tolerance = epsilon;
215
+}
216
+
217
+@ This just leaves a trivial constructor.
218
+
219
+@<ValueAnnotation implementation@>=
220
+ValueAnnotation::ValueAnnotation() : QObject(),
221
+	lastIndex(-1), annotationColumn(2), measurementColumn(1), tolerance(0.05)
222
+{
223
+	/* Nothing needs to be done here. */
224
+}
225
+

Laden…
Annuleren
Opslaan