Browse Source

Merge branch 'scale' into development

Neal Wilson 11 years ago
parent
commit
dfd0e43383
6 changed files with 791 additions and 5 deletions
  1. 41
    0
      config/Windows/newbatch.xml
  2. 12
    0
      config/Windows/productionroaster.xml
  3. 6
    2
      src/Typica.pro
  4. 498
    0
      src/scales.w
  5. 18
    1
      src/typica.w
  6. 216
    2
      src/units.w

+ 41
- 0
config/Windows/newbatch.xml View File

@@ -2,6 +2,7 @@
2 2
     <menu name="Batch">
3 3
         <item id="new" shortcut="Ctrl+N">New Batch…</item>
4 4
     </menu>
5
+	<layout type="horizontal">
5 6
     <layout type="vertical">
6 7
         <layout type="horizontal">
7 8
 			<label>Machine:</label>
@@ -53,6 +54,12 @@
53 54
             <button name="Save log as target profile" type="check" id="target" />
54 55
         </layout>
55 56
     </layout>
57
+	<layout type="vertical">
58
+		<label>Connected Scales</label>
59
+		<layout type="vertical" id="scales" />
60
+		<stretch />
61
+	</layout>
62
+	</layout>
56 63
     <program>
57 64
         <![CDATA[
58 65
 			var unitBox = findChildObject(this, 'unit');
@@ -77,6 +84,40 @@
77 84
             var roasted = findChildObject(this, 'roasted');
78 85
             var roastwt = findChildObject(this, 'roast');
79 86
             roastwt.maximumWidth = 80;
87
+			var scalesLayout = findChildObject(this, 'scales');
88
+			scalesLayout.spacing = 10;
89
+			if(navigationwindow.loggingWindow.scales.length > 0) {
90
+				for(var i = 0; i < navigationwindow.loggingWindow.scales.length; i++) {
91
+					var scale = navigationwindow.loggingWindow.scales[i];
92
+					var label = new DragLabel();
93
+					var weighButton = new QPushButton();
94
+					weighButton.text = "Weigh";
95
+					weighButton.clicked.connect(scale.weigh);
96
+					label.updateMeasurement = function(m, u) {
97
+						switch(unitBox.currentIndex) {
98
+							case 0:
99
+								this.text = Units.convertWeight(m, u, Units.Gram).toFixed(1);
100
+								break;
101
+							case 1:
102
+								this.text = Units.convertWeight(m, u, Units.Kilogram).toFixed(4);
103
+								break;
104
+							case 2:
105
+								this.text = Units.convertWeight(m, u, Units.Ounce).toFixed(3);
106
+								break;
107
+							case 3:
108
+								this.text = Units.convertWeight(m, u, Units.Pound).toFixed(4);
109
+								break;
110
+						}
111
+					};
112
+					scalesLayout.addWidget(label);
113
+					scalesLayout.addWidget(weighButton);
114
+					scale.newMeasurement.connect(function(m, u) {
115
+						label.updateMeasurement(m, u);
116
+					});
117
+					scale.weigh();
118
+					unitBox['currentIndexChanged(int)'].connect(scale.weigh);
119
+				}
120
+			}
80 121
 			model.dataChanged.connect(function() {
81 122
 				green.text = table.columnSum(1, 0);
82 123
 				table.resizeColumnToContents(0);

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

@@ -75,6 +75,7 @@
75 75
 		var rateoffsets = new Array();
76 76
 		var ratezeros = new Array();
77 77
 		var channelType = new Array();
78
+		window.scales = new Array();
78 79
 		var indicatorPanel = findChildObject(this, 'indicators');
79 80
 		var annotationPanel = findChildObject(this, 'controlpanel');
80 81
 		var log = findChildObject(this, 'log');
@@ -488,6 +489,17 @@
488 489
 						}
489 490
 					}
490 491
 				}
492
+				else if(driverReference.driver == "scale")
493
+				{
494
+					var scale = new SerialScale(driverReference.port);
495
+					scale.setDataBits(8);
496
+					scale.setBaudRate(driverReference.baudrate);
497
+					scale.setParity(driverReference.parity);
498
+					scale.setStopBits(driverReference.stopbits);
499
+					scale.setFlowControl(driverReference.flowcontrol);
500
+					scale.open(3);
501
+					window.scales.push(scale);
502
+				}
491 503
 			}
492 504
 		}
493 505
 		for(var i = 1; i < tabControls.length; i++)

+ 6
- 2
src/Typica.pro View File

@@ -21,13 +21,17 @@ HEADERS += moc_typica.cpp \
21 21
     abouttypica.h \
22 22
     units.h \
23 23
     webview.h \
24
-    webelement.h
24
+    webelement.h \
25
+    scale.h \
26
+    draglabel.h
25 27
 SOURCES += typica.cpp \
26 28
     helpmenu.cpp \
27 29
     abouttypica.cpp \
28 30
     units.cpp \
29 31
     webview.cpp \
30
-    webelement.cpp
32
+    webelement.cpp \
33
+    scale.cpp \
34
+    draglabel.cpp
31 35
 
32 36
 RESOURCES += \
33 37
     resources.qrc

+ 498
- 0
src/scales.w View File

@@ -0,0 +1,498 @@
1
+@** Collecting Measurements from Scales.
2
+
3
+\noindent When a computer connected scale is available, it can be useful to
4
+eliminate manual transcription from the data entry for batches. In general it
5
+is difficult to determine where a measurement should go automatically, but
6
+there are a number of situations where the ability to drag a measurement from a
7
+label and drop it to whatever input widget is appropriate would be a useful
8
+operation to support.
9
+
10
+To support this, we need to subclass |QLabel| to allow it to initiate a drag
11
+and drop operation.
12
+
13
+@(draglabel.h@>=
14
+#ifndef TypicaDragLabelInclude
15
+#define TypicaDragLabelInclude
16
+
17
+#include <QLabel>
18
+
19
+class DragLabel : public QLabel
20
+{
21
+	Q_OBJECT
22
+	public:
23
+		explicit DragLabel(const QString &labelText, QWidget *parent = NULL);
24
+	protected:
25
+		void mousePressEvent(QMouseEvent *event);
26
+};
27
+
28
+#endif
29
+
30
+@ The font size of the label is increased by default to make it easier to
31
+manipulate on a touch screen. Otherwise, there is little to do in this class.
32
+
33
+@(draglabel.cpp@>=
34
+#include "draglabel.h"
35
+
36
+#include <QDrag>
37
+#include <QMouseEvent>
38
+
39
+DragLabel::DragLabel(const QString &labelText, QWidget *parent) :
40
+	QLabel(labelText, parent)
41
+{
42
+	QFont labelFont = font();
43
+	labelFont.setPointSize(14);
44
+	setFont(labelFont);
45
+}
46
+
47
+void DragLabel::mousePressEvent(QMouseEvent *event)
48
+{
49
+	if(event->button() == Qt::LeftButton)
50
+	{
51
+		QDrag *drag = new QDrag(this);
52
+		QMimeData *mimeData = new QMimeData;
53
+		mimeData->setText(text());
54
+		drag->setMimeData(mimeData);
55
+		drag->exec();
56
+	}
57
+}
58
+
59
+@ We require the ability to create these labels from the host environment.
60
+First we include the appropriate header.
61
+
62
+@<Header files to include@>=
63
+#include "draglabel.h"
64
+
65
+@ Next, a pair of function prototypes.
66
+
67
+@<Function prototypes for scripting@>=
68
+QScriptValue constructDragLabel(QScriptContext *context, QScriptEngine *engine);
69
+void setDragLabelProperties(QScriptValue value, QScriptEngine *engine);
70
+
71
+@ These are made known to the host environment as usual.
72
+
73
+@<Set up the scripting engine@>=
74
+constructor = engine->newFunction(constructDragLabel);
75
+value = engine->newQMetaObject(&DragLabel::staticMetaObject, constructor);
76
+engine->globalObject().setProperty("DragLabel", value);
77
+
78
+@ The implementation is trivial.
79
+
80
+@<Functions for scripting@>=
81
+QScriptValue constructDragLabel(QScriptContext *context, QScriptEngine *engine)
82
+{
83
+	QScriptValue object;
84
+	QString labelText = "";
85
+	if(context->argumentCount() == 1)
86
+	{
87
+		labelText = argument<QString>(0, context);
88
+	}
89
+	object = engine->newQObject(new DragLabel(labelText));
90
+	setDragLabelProperties(object, engine);
91
+	return object;
92
+}
93
+
94
+void setDragLabelProperties(QScriptValue value, QScriptEngine *engine)
95
+{
96
+	setQLabelProperties(value, engine);
97
+}
98
+
99
+@ An object is also required to communicate with a scale. This is responsible
100
+for setting up a connection over a serial port, sending commands out to the
101
+scale, buffering and interpreting the response, and signaling new measurements.
102
+
103
+@(scale.h@>=
104
+#ifndef TypicaScaleInclude
105
+#define TypicaScaleInclude
106
+
107
+#include "3rdparty/qextserialport/src/qextserialport.h"
108
+#include "units.h"
109
+
110
+class SerialScale : public QextSerialPort
111
+{
112
+	Q_OBJECT
113
+	public:
114
+		SerialScale(const QString &port);
115
+	public slots:
116
+		void tare();
117
+		void weigh();
118
+	signals:
119
+		void newMeasurement(double weight, Units::Unit unit);
120
+	private slots:
121
+		void dataAvailable();
122
+	private:
123
+		QByteArray responseBuffer;
124
+};
125
+
126
+#endif
127
+
128
+@ The constructor tells the port that this should be event driven and connects
129
+a signal to buffer data..
130
+
131
+@(scale.cpp@>=
132
+#include "scale.h"
133
+#include <QStringList>
134
+
135
+SerialScale::SerialScale(const QString &port) :
136
+	QextSerialPort(port, QextSerialPort::EventDriven)
137
+{
138
+	connect(this, SIGNAL(readyRead()), this, SLOT(dataAvailable()));
139
+}
140
+
141
+@ The |dataAvailable| method handles buffering incoming data and processing
142
+responses when they have come in. Serial port communications are likely to be
143
+very slow in comparison to everything else so it is likely that only one
144
+character will come in at a time.
145
+
146
+Note that this currently only understands single line output and a limited
147
+selection of units.
148
+
149
+@(scale.cpp@>=
150
+void SerialScale::dataAvailable()
151
+{
152
+	responseBuffer.append(readAll());
153
+	if(responseBuffer.contains("\x0D"))
154
+	{
155
+		if(responseBuffer.contains("!"))
156
+		{
157
+			responseBuffer.clear();
158
+		}
159
+		else
160
+		{
161
+			@<Process weight measurement@>@;
162
+			responseBuffer.clear();
163
+		}
164
+	}
165
+}
166
+
167
+@ Each line of data consists of an optional sign character possibly followed
168
+by a space followed by characters representing a number followed by a
169
+space followed by characters indicating a unit. This may be preceeded and
170
+followed by a variable amount of white space. To process a new measurement, we
171
+must remove the excess white space, split the number from the unit, prepend the
172
+sign to the number if it is present, convert the string representing the number
173
+to a numeric type, and determine which unit the measurement is in.
174
+
175
+\medskip
176
+
177
+\settabs 8 \columns
178
+\+&&&{\tt |"lb"|}&|Units::Pound|\cr
179
+\+&&&{\tt |"kg"|}&|Units::Kilogram|\cr
180
+\+&&&{\tt |"g"|}&|Units::Gram|\cr
181
+\+&&&{\tt |"oz"|}&|Units::Ounce|\cr
182
+
183
+\smallskip
184
+
185
+\centerline{Table \secno: Unit Strings and Representative Unit Enumeration}
186
+
187
+\medskip
188
+
189
+@<Process weight measurement@>=
190
+QStringList responseParts = QString(responseBuffer.simplified()).split(' ');
191
+if(responseParts.size() > 2)
192
+{
193
+	responseParts.removeFirst();
194
+	responseParts.replace(0, QString("-%1").arg(responseParts[0]));
195
+}
196
+double weight = responseParts[0].toDouble();
197
+Units::Unit unit = Units::Unitless;
198
+if(responseParts[1] == "lb")
199
+{
200
+	unit = Units::Pound;
201
+}
202
+else if(responseParts[1] == "kg")
203
+{
204
+	unit = Units::Kilogram;
205
+}
206
+else if(responseParts[1] == "g")
207
+{
208
+	unit = Units::Gram;
209
+}
210
+else if(responseParts[1] == "oz")
211
+{
212
+	unit = Units::Ounce;
213
+}
214
+emit newMeasurement(weight, unit);
215
+
216
+@ Two methods are used to send commands to the scale. I am unsure of how well
217
+standardized remote key operation of scales are. The class may need to be
218
+extended to support more devices.
219
+
220
+@(scale.cpp@>=
221
+void SerialScale::tare()
222
+{
223
+	write("!KT\x0D");
224
+}
225
+
226
+void SerialScale::weigh()
227
+{
228
+	write("!KP\x0D");
229
+}
230
+
231
+@ This must be available to the host environment.
232
+
233
+@<Function prototypes for scripting@>=
234
+QScriptValue constructSerialScale(QScriptContext *context, QScriptEngine *engine);
235
+void setSerialScaleProperties(QScriptValue value, QScriptEngine *engine);
236
+
237
+@ These functions are made known to the scripting engine in the usual way.
238
+
239
+@<Set up the scripting engine@>=
240
+constructor = engine->newFunction(constructSerialScale);
241
+value = engine->newQMetaObject(&SerialScale::staticMetaObject, constructor);
242
+engine->globalObject().setProperty("SerialScale", value);
243
+
244
+@ If we are to set up the serial ports from the host environment, a few
245
+enumerated types must be made known to the meta-object system.
246
+
247
+@<Class declarations@>=
248
+Q_DECLARE_METATYPE(BaudRateType)
249
+Q_DECLARE_METATYPE(DataBitsType)
250
+Q_DECLARE_METATYPE(ParityType)
251
+Q_DECLARE_METATYPE(StopBitsType)
252
+Q_DECLARE_METATYPE(FlowType)
253
+
254
+@ For each of these, a pair of functions converts values to script values and
255
+back. This is a very annoying aspect of the version of QextSerialPort currently
256
+used by \pn{}.
257
+
258
+@<Function prototypes for scripting@>=
259
+QScriptValue BaudRateType_toScriptValue(QScriptEngine *engine, const BaudRateType &value);
260
+void BaudRateType_fromScriptValue(const QScriptValue &sv, BaudRateType &value);
261
+QScriptValue DataBitsType_toScriptValue(QScriptEngine *engine, const DataBitsType &value);
262
+void DataBitsType_fromScriptValue(const QScriptValue &sv, DataBitsType &value);
263
+QScriptValue ParityType_toScriptValue(QScriptEngine *engine, const ParityType &value);
264
+void ParityType_fromScriptValue(const QScriptValue &sv, ParityType &value);
265
+QScriptValue StopBitsType_toScriptValue(QScriptEngine *engine, const StopBitsType &value);
266
+void StopBitsType_fromScriptValue(const QScriptValue &sv, StopBitsType &value);
267
+QScriptValue FlowType_toScriptValue(QScriptEngine *engine, const FlowType &value);
268
+void FlowType_fromScriptValue(const QScriptValue &sv, FlowType &value);
269
+
270
+@ These are implemented thusly.
271
+
272
+@<Functions for scripting@>=
273
+QScriptValue BaudRateType_toScriptValue(QScriptEngine *engine, const BaudRateType &value)
274
+{
275
+	return engine->newVariant(QVariant((int)(value)));
276
+}
277
+
278
+void BaudRateType_fromScriptValue(const QScriptValue &sv, BaudRateType &value)
279
+{
280
+	value = (BaudRateType)(sv.toVariant().toInt());
281
+}
282
+
283
+QScriptValue DataBitsType_toScriptValue(QScriptEngine *engine, const DataBitsType &value)
284
+{
285
+	return engine->newVariant(QVariant((int)(value)));
286
+}
287
+
288
+void DataBitsType_fromScriptValue(const QScriptValue &sv, DataBitsType &value)
289
+{
290
+	value = (DataBitsType)(sv.toVariant().toInt());
291
+}
292
+
293
+QScriptValue ParityType_toScriptValue(QScriptEngine *engine, const ParityType &value)
294
+{
295
+	return engine->newVariant(QVariant((int)(value)));
296
+}
297
+
298
+void ParityType_fromScriptValue(const QScriptValue &sv, ParityType &value)
299
+{
300
+	value = (ParityType)(sv.toVariant().toInt());
301
+}
302
+
303
+QScriptValue StopBitsType_toScriptValue(QScriptEngine *engine, const StopBitsType &value)
304
+{
305
+	return engine->newVariant(QVariant((int)(value)));
306
+}
307
+
308
+void StopBitsType_fromScriptValue(const QScriptValue &sv, StopBitsType &value)
309
+{
310
+	value = (StopBitsType)(sv.toVariant().toInt());
311
+}
312
+
313
+QScriptValue FlowType_toScriptValue(QScriptEngine *engine, const FlowType &value)
314
+{
315
+	return engine->newVariant(QVariant((int)(value)));
316
+}
317
+
318
+void FlowType_fromScriptValue(const QScriptValue &sv, FlowType &value)
319
+{
320
+	value = (FlowType)(sv.toVariant().toInt());
321
+}
322
+
323
+@ These conversion functions are then registered.
324
+
325
+@<Set up the scripting engine@>=
326
+qScriptRegisterMetaType(engine, BaudRateType_toScriptValue, BaudRateType_fromScriptValue);
327
+qScriptRegisterMetaType(engine, DataBitsType_toScriptValue, DataBitsType_fromScriptValue);
328
+qScriptRegisterMetaType(engine, ParityType_toScriptValue, ParityType_fromScriptValue);
329
+qScriptRegisterMetaType(engine, StopBitsType_toScriptValue, StopBitsType_fromScriptValue);
330
+qScriptRegisterMetaType(engine, FlowType_toScriptValue, FlowType_fromScriptValue);
331
+
332
+@ In order to make this class available to the host environment, we must also
333
+include the appropriate header file.
334
+
335
+@<Header files to include@>=
336
+#include "scale.h"
337
+
338
+@ Most of the properties of interest should be added automatically, however
339
+there are non-slot methods in |QIODevice| that we require.
340
+
341
+@<Functions for scripting@>=
342
+void setSerialScaleProperties(QScriptValue value, QScriptEngine *engine)
343
+{
344
+	setQIODeviceProperties(value, engine);
345
+}
346
+
347
+@ The script constructor should seem familiar.
348
+
349
+@<Functions for scripting@>=
350
+QScriptValue constructSerialScale(QScriptContext *context, QScriptEngine *engine)
351
+{
352
+	QScriptValue object;
353
+	if(context->argumentCount() == 1)
354
+	{
355
+		object = engine->newQObject(new SerialScale(argument<QString>(0, context)));
356
+		setSerialScaleProperties(object, engine);
357
+	}
358
+	else
359
+	{
360
+		context->throwError("Incorrect number of arguments passed to "
361
+		                    "SerialScale. The constructor takes one string "
362
+		                    "as an argument specifying a port name.");
363
+	}
364
+	return object;
365
+}
366
+
367
+@ In order to allow configuration of scales from within \pn{}, a configuration
368
+widget must be provided.
369
+
370
+@<Class declarations@>=
371
+class SerialScaleConfWidget : public BasicDeviceConfigurationWidget
372
+{
373
+	Q_OBJECT
374
+	public:
375
+		Q_INVOKABLE SerialScaleConfWidget(DeviceTreeModel *model,
376
+		                                  const QModelIndex &index);
377
+	private slots:
378
+		void updatePort(const QString &newPort);
379
+		void updateBaudRate(const QString &rate);
380
+		void updateParity(int index);
381
+		void updateFlowControl(int index);
382
+		void updateStopBits(int index);
383
+	private:
384
+		PortSelector *port;
385
+		BaudSelector *baud;
386
+		ParitySelector *parity;
387
+		FlowSelector *flow;
388
+		StopSelector *stop;
389
+};
390
+
391
+@ This is very similar to other configuration widgets.
392
+
393
+@<SerialScaleConfWidget implementation@>=
394
+SerialScaleConfWidget::SerialScaleConfWidget(DeviceTreeModel *model,
395
+                                             const QModelIndex &index)
396
+: BasicDeviceConfigurationWidget(model, index),
397
+  port(new PortSelector), baud(new BaudSelector), parity(new ParitySelector),
398
+  flow(new FlowSelector), stop(new StopSelector)
399
+{
400
+	QFormLayout *layout = new QFormLayout;
401
+	layout->addRow(tr("Port:"), port);
402
+	connect(port, SIGNAL(currentIndexChanged(QString)),
403
+	        this, SLOT(updatePort(QString)));
404
+	connect(port, SIGNAL(editTextChanged(QString)),
405
+	        this, SLOT(updatePort(QString)));
406
+	layout->addRow(tr("Baud:"), baud);
407
+	connect(baud, SIGNAL(currentIndexChanged(QString)),
408
+	        this, SLOT(updateBaudRate(QString)));
409
+	layout->addRow(tr("Parity:"), parity);
410
+	connect(parity, SIGNAL(currentIndexChanged(int)),
411
+	        this, SLOT(updateParity(int)));
412
+	layout->addRow(tr("Flow Control:"), flow);
413
+	connect(flow, SIGNAL(currentIndexChanged(int)),
414
+	        this, SLOT(updateFlowControl(int)));
415
+	layout->addRow(tr("Stop Bits:"), stop);
416
+	connect(stop, SIGNAL(currentIndexChanged(int)),
417
+	        this, SLOT(updateStopBits(int)));
418
+	@<Get device configuration data for current node@>@;
419
+	for(int i = 0; i < configData.size(); i++)
420
+	{
421
+		node = configData.at(i).toElement();
422
+		if(node.attribute("name") == "port")
423
+		{
424
+			int j = port->findText(node.attribute("value"));
425
+			if(j >= 0)
426
+			{
427
+				port->setCurrentIndex(j);
428
+			}
429
+			else
430
+			{
431
+				port->insertItem(0, node.attribute("value"));
432
+				port->setCurrentIndex(0);
433
+			}
434
+		}
435
+		else if(node.attribute("name") == "baudrate")
436
+		{
437
+			baud->setCurrentIndex(baud->findText(node.attribute("value")));
438
+		}
439
+		else if(node.attribute("name") == "parity")
440
+		{
441
+			parity->setCurrentIndex(parity->findData(node.attribute("value")));
442
+		}
443
+		else if(node.attribute("name") == "flowcontrol")
444
+		{
445
+			flow->setCurrentIndex(flow->findData(node.attribute("value")));
446
+		}
447
+		else if(node.attribute("name") == "stopbits")
448
+		{
449
+			stop->setCurrentIndex(stop->findData(node.attribute("value")));
450
+		}
451
+	}
452
+	updatePort(port->currentText());
453
+	updateBaudRate(baud->currentText());
454
+	updateParity(parity->currentIndex());
455
+	updateFlowControl(flow->currentIndex());
456
+	updateStopBits(stop->currentIndex());
457
+	setLayout(layout);
458
+}
459
+
460
+@ Update methods are the same as were used in |ModbusRtuPortConfWidget|.
461
+
462
+@<SerialScaleConfWidget implementation@>=
463
+void SerialScaleConfWidget::updatePort(const QString &newPort)
464
+{
465
+	updateAttribute("port", newPort);
466
+}
467
+
468
+void SerialScaleConfWidget::updateBaudRate(const QString &rate)
469
+{
470
+	updateAttribute("baudrate", rate);
471
+}
472
+
473
+void SerialScaleConfWidget::updateParity(int index)
474
+{
475
+	updateAttribute("parity", parity->itemData(index).toString());
476
+}
477
+
478
+void SerialScaleConfWidget::updateFlowControl(int index)
479
+{
480
+	updateAttribute("flowcontrol", flow->itemData(index).toString());
481
+}
482
+
483
+void SerialScaleConfWidget::updateStopBits(int index)
484
+{
485
+	updateAttribute("stopbits", stop->itemData(index).toString());
486
+}
487
+
488
+@ The configuration widget is registered with the configuration system.
489
+
490
+@<Register device configuration widgets@>=
491
+app.registerDeviceConfigurationWidget("scale", SerialScaleConfWidget::staticMetaObject);
492
+
493
+@ A |NodeInserter| is also added.
494
+
495
+@<Register top level device configuration nodes@>=
496
+inserter = new NodeInserter(tr("Serial Scale"), tr("Scale"), "scale", NULL);
497
+topLevelNodeInserters.append(inserter);
498
+

+ 18
- 1
src/typica.w View File

@@ -839,6 +839,7 @@ generated file empty.
839 839
 @<SettingsWindow implementation@>@/
840 840
 @<GraphSettingsWidget implementation@>@/
841 841
 @<DataqSdkDeviceConfWidget implementation@>@/
842
+@<SerialScaleConfWidget implementation@>@/
842 843
 
843 844
 @ A few headers are required for various parts of \pn{}. These allow the use of
844 845
 various Qt modules.
@@ -976,6 +977,16 @@ template<> QModelIndex argument(int arg, QScriptContext *context)
976 977
 	return qscriptvalue_cast<QModelIndex>(context->argument(arg));
977 978
 }
978 979
 
980
+template<> double argument(int arg, QScriptContext *context)
981
+{
982
+	return (double)(context->argument(arg).toNumber());
983
+}
984
+
985
+template<> Units::Unit argument(int arg, QScriptContext *context)
986
+{
987
+	return (Units::Unit)(context->argument(arg).toInt32());
988
+}
989
+
979 990
 @ The scripting engine is informed of a number of classes defined elsewhere in
980 991
 the program. Code related to scripting these classes is grouped with the code
981 992
 implementing the classes. Additionally, there are several classes from Qt which
@@ -12119,7 +12130,7 @@ Qt::ItemFlags SaltModel::flags(const QModelIndex &index) const
12119 12130
 	@<Check that the SaltModel index is valid@>@;
12120 12131
 	if(valid)
12121 12132
 	{
12122
-		return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
12133
+		return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDropEnabled;
12123 12134
 	}
12124 12135
 	return 0;
12125 12136
 }
@@ -15782,7 +15793,11 @@ PortSelector::PortSelector(QWidget *parent) : QComboBox(parent),
15782 15793
 	QextPortInfo port;
15783 15794
 	foreach(port, ports)
15784 15795
 	{
15796
+#ifdef Q_OS_WIN32
15785 15797
 		addItem(port.portName);
15798
+#else
15799
+		addItem(port.physName);
15800
+#endif
15786 15801
 	}
15787 15802
 	lister->setUpNotifications();
15788 15803
 	connect(lister, SIGNAL(deviceDiscovered(QextPortInfo)),
@@ -18368,6 +18383,8 @@ app.registerDeviceConfigurationWidget("translation", TranslationConfWidget::stat
18368 18383
 
18369 18384
 @i dataqsdk.w
18370 18385
 
18386
+@i scales.w
18387
+
18371 18388
 @** Local changes.
18372 18389
 
18373 18390
 \noindent This is the end of \pn{} as distributed by its author. It is expected

+ 216
- 2
src/units.w View File

@@ -22,11 +22,17 @@ class Units: public QObject
22 22
 			Fahrenheit = 10144,
23 23
 			Celsius = 10143,
24 24
 			Kelvin = 10325,
25
-			Rankine = 10145
25
+			Rankine = 10145,
26
+			Pound = 15876,
27
+			Kilogram = 15877,
28
+			Ounce = 1,
29
+			Gram = 2
26 30
 		};
27 31
 		static double convertTemperature(double value, Unit fromUnit, Unit toUnit);
28 32
 		static double convertRelativeTemperature(double value, Unit fromUnit, Unit toUnit);
29 33
 		static bool isTemperatureUnit(Unit unit);
34
+		static double convertWeight(double value, Unit fromUnit, Unit toUnit);
35
+		static bool isWeightUnit(Unit unit);
30 36
 };
31 37
 
32 38
 #endif
@@ -175,6 +181,9 @@ double Units::convertRelativeTemperature(double value, Unit fromUnit, Unit toUni
175 181
 				case Rankine:
176 182
 					return value;
177 183
 					break;
184
+				default:
185
+					return 0;
186
+					break;
178 187
 			}
179 188
 			break;
180 189
 		case Celsius:
@@ -192,6 +201,9 @@ double Units::convertRelativeTemperature(double value, Unit fromUnit, Unit toUni
192 201
 				case Rankine:
193 202
 					return value * (9.0 / 5.0);
194 203
 					break;
204
+				default:
205
+					return 0;
206
+					break;
195 207
 			}
196 208
 			break;
197 209
 		case Kelvin:
@@ -209,6 +221,9 @@ double Units::convertRelativeTemperature(double value, Unit fromUnit, Unit toUni
209 221
 				case Rankine:
210 222
 					return value * (9.0 / 5.0);
211 223
 					break;
224
+				default:
225
+					return 0;
226
+					break;
212 227
 			}
213 228
 			break;
214 229
 		case Rankine:
@@ -226,6 +241,9 @@ double Units::convertRelativeTemperature(double value, Unit fromUnit, Unit toUni
226 241
 				case Rankine:
227 242
 					return value;
228 243
 					break;
244
+				default:
245
+					return 0;
246
+					break;
229 247
 			}
230 248
 			break;
231 249
 		default:
@@ -235,11 +253,207 @@ double Units::convertRelativeTemperature(double value, Unit fromUnit, Unit toUni
235 253
 	return 0;
236 254
 }
237 255
 
256
+@ Units of force are handled similarly to units of temperature.
257
+
258
+@(units.cpp@>=
259
+double Units::convertWeight(double value, Unit fromUnit, Unit toUnit)
260
+{
261
+	if(isWeightUnit(fromUnit) && isWeightUnit(toUnit))
262
+	{
263
+		switch(fromUnit)
264
+		{
265
+			case Pound:
266
+				switch(toUnit)
267
+				{
268
+					case Pound:
269
+						return value;
270
+						break;
271
+					case Kilogram:
272
+						return value / 2.2;
273
+						break;
274
+					case Ounce:
275
+						return value * 16.0;
276
+						break;
277
+					case Gram:
278
+						return value / 0.0022;
279
+						break;
280
+					default:
281
+						return 0;
282
+						break;
283
+				}
284
+				break;
285
+			case Kilogram:
286
+				switch(toUnit)
287
+				{
288
+					case Pound:
289
+						return value * 2.2;
290
+						break;
291
+					case Kilogram:
292
+						return value;
293
+						break;
294
+					case Ounce:
295
+						return value * 35.2;
296
+						break;
297
+					case Gram:
298
+						return value * 1000.0;
299
+						break;
300
+					default:
301
+						return 0;
302
+						break;
303
+				}
304
+				break;
305
+			case Ounce:
306
+				switch(toUnit)
307
+				{
308
+					case Pound:
309
+						return value / 16.0;
310
+						break;
311
+					case Kilogram:
312
+						return value / 35.2;
313
+						break;
314
+					case Ounce:
315
+						return value;
316
+						break;
317
+					case Gram:
318
+						return value / 0.0352;
319
+						break;
320
+					default:
321
+						return 0;
322
+						break;
323
+				}
324
+				break;
325
+			case Gram:
326
+				switch(toUnit)
327
+				{
328
+					case Pound:
329
+						return value * 0.0022;
330
+						break;
331
+					case Kilogram:
332
+						return value / 1000.0;
333
+						break;
334
+					case Ounce:
335
+						return value * 0.0352;
336
+						break;
337
+					case Gram:
338
+						return value;
339
+						break;
340
+					default:
341
+						return 0;
342
+						break;
343
+				}
344
+				break;
345
+			default:
346
+				return 0;
347
+				break;
348
+		}
349
+	}
350
+	return 0;
351
+}
352
+
353
+bool Units::isWeightUnit(Unit unit)
354
+{
355
+	if(unit == Pound ||
356
+	   unit == Kilogram ||
357
+	   unit == Ounce ||
358
+	   unit == Gram)
359
+	{
360
+		return true;
361
+	}
362
+	return false;
363
+}
364
+
238 365
 @ This class is exposed to the host environment. Note the lack of constructor.
239 366
 We do not wish to create any instances, just have access to the |Unit|
240
-enumeration.
367
+enumeration and conversion methods.
368
+
369
+Unfortunately, marking a static method |Q_INVOKABLE| will not work as per Qt
370
+bug #18840. There seems to be no intention to correct this deficiency so
371
+instead of having something that just works, we must resort to the following
372
+hackery.
373
+
374
+@<Function prototypes for scripting@>=
375
+QScriptValue Units_convertTemperature(QScriptContext *context, QScriptEngine *engine);
376
+QScriptValue Units_convertRelativeTemperature(QScriptContext *context,
377
+                                              QScriptEngine *engine);
378
+QScriptValue Units_isTemperatureUnit(QScriptContext *context, QScriptEngine *engine);
379
+QScriptValue Units_convertWeight(QScriptContext *context, QScriptEngine *engine);
380
+QScriptValue Units_isWeightUnit(QScriptContext *context, QScriptEngine *engine);
381
+
382
+@ These functions are added as properties for the host environment.
241 383
 
242 384
 @<Set up the scripting engine@>=
243 385
 value = engine->newQMetaObject(&Units::staticMetaObject);
386
+value.setProperty("convertTemperature", engine->newFunction(Units_convertTemperature));
387
+value.setProperty("convertRelativeTemperature",
388
+                  engine->newFunction(Units_convertRelativeTemperature));
389
+value.setProperty("isTemperatureUnit", engine->newFunction(Units_isTemperatureUnit));
390
+value.setProperty("convertWeight", engine->newFunction(Units_convertWeight));
391
+value.setProperty("isWeightUnit", engine->newFunction(Units_isWeightUnit));
244 392
 engine->globalObject().setProperty("Units", value);
245 393
 
394
+@ The implementation of these functions is trivial.
395
+
396
+@<Functions for scripting@>=
397
+QScriptValue Units_convertTemperature(QScriptContext *context, QScriptEngine *engine)
398
+{
399
+	return QScriptValue(Units::convertTemperature(argument<double>(0, context),
400
+	                                              argument<Units::Unit>(1, context),
401
+	                                              argument<Units::Unit>(2, context)));
402
+}
403
+
404
+QScriptValue Units_convertRelativeTemperature(QScriptContext *context,
405
+                                              QScriptEngine *engine)
406
+{
407
+	return QScriptValue(Units::convertRelativeTemperature(
408
+	                         argument<double>(0, context),
409
+	                         argument<Units::Unit>(1, context),
410
+	                         argument<Units::Unit>(2, context)));
411
+}
412
+
413
+QScriptValue Units_isTemperatureUnit(QScriptContext *context, QScriptEngine *engine)
414
+{
415
+	return QScriptValue(Units::isTemperatureUnit(argument<Units::Unit>(0, context)));
416
+}
417
+
418
+QScriptValue Units_convertWeight(QScriptContext *context, QScriptEngine *engine)
419
+{
420
+	return QScriptValue(Units::convertWeight(argument<double>(0, context),
421
+	                                         argument<Units::Unit>(1, context),
422
+	                                         argument<Units::Unit>(2, context)));
423
+}
424
+
425
+QScriptValue Units_isWeightUnit(QScriptContext *context, QScriptEngine *engine)
426
+{
427
+	return QScriptValue(Units::isWeightUnit(argument<Units::Unit>(0, context)));
428
+}
429
+
430
+@ To pass unit data in some circumstances, the inner enumeration must be
431
+registered as a meta-type with script conversions.
432
+
433
+@<Class declarations@>=
434
+Q_DECLARE_METATYPE(Units::Unit)
435
+
436
+@ A pair of conversion methods is required.
437
+
438
+@<Function prototypes for scripting@>=
439
+QScriptValue Unit_toScriptValue(QScriptEngine *engine, const Units::Unit &value);
440
+void Unit_fromScriptValue(const QScriptValue &sv, Units::Unit &value);
441
+
442
+@ These are implemented thusly.
443
+
444
+@<Functions for scripting@>=
445
+QScriptValue Unit_toScriptValue(QScriptEngine *engine, const Units::Unit &value)
446
+{
447
+	return engine->newVariant(QVariant(value));
448
+}
449
+
450
+void Unit_fromScriptValue(const QScriptValue &sv, Units::Unit &value)
451
+{
452
+	value = sv.toVariant().value<Units::Unit>();
453
+}
454
+
455
+@ These conversion functions are registered.
456
+
457
+@<Set up the scripting engine@>=
458
+qScriptRegisterMetaType(engine, Unit_toScriptValue, Unit_fromScriptValue);
459
+

Loading…
Cancel
Save