14 #include "tempcontrol.h"
15 #include "ui_tempcontrolform.h"
16 #include "interface.h"
18 #include "xnodeconnector.h"
22 XTempControl::XChannel::XChannel(
const char *name,
bool runtime,
23 Transaction &tr_list,
const shared_ptr<XThermometerList> &list) :
26 XThermometer> > (
"Thermometer", false, ref(tr_list), list)),
27 m_excitation(create<
XComboNode> (
"Excitation", false)),
28 m_thermometers(list) {}
30 XTempControl::Loop::Loop(
const char *name,
bool runtime, shared_ptr<XTempControl> tempctrl,
Transaction &tr,
31 unsigned int idx,
Transaction &tr_meas,
const shared_ptr<XMeasure> &meas) :
35 m_targetTemp(create<
XDoubleNode> (
"TargetTemp" , true,
"%.5g")),
36 m_manualPower(create<
XDoubleNode> (
"ManualPower", true,
"%.4g")),
40 m_heaterMode(create<
XComboNode> (
"HeaterMode", false, true)),
41 m_powerRange(create<
XComboNode> (
"PowerRange", false, true)),
42 m_powerMax(create<
XDoubleNode> (
"PowerMax", false,
"%.4g")),
43 m_powerMin(create<
XDoubleNode> (
"PowerMin", false,
"%.4g")),
44 m_heaterPower(create<
XDoubleNode> (
"HeaterPower", false,
"%.4g")),
45 m_sourceTemp(create<
XDoubleNode> (
"SourceTemp", false,
"%.5g")),
46 m_stabilized(create<
XDoubleNode> (
"Stabilized", true,
"%g")),
48 m_extDCSourceChannel(create<
XComboNode> (
"ExtDCSourceChannel", false, true)),
49 m_extIsPositive(create<
XBoolNode> (
"ExtIsPositive", false)) {
51 create<XItemNode<XChannelList, XChannel> >(
"CurrentChannel",
true, ref(tr),
52 tempctrl->m_channels);
55 m_lsnOnExtDeviceChanged = tr[ *m_extDevice].onValueChanged().connectWeakly(
56 shared_from_this(), &XTempControl::Loop::onExtDeviceChanged);
59 m_currentChannel->setUIEnabled(
false);
60 m_powerRange->setUIEnabled(
false);
61 m_heaterMode->setUIEnabled(
false);
62 m_prop->setUIEnabled(
false);
63 m_int->setUIEnabled(
false);
64 m_deriv->setUIEnabled(
false);
65 m_manualPower->setUIEnabled(
false);
66 m_powerMax->setUIEnabled(
true);
67 m_powerMin->setUIEnabled(
true);
68 m_targetTemp->setUIEnabled(
false);
70 m_extDevice->setUIEnabled(
true);
71 m_extDCSourceChannel->setUIEnabled(
true);
72 m_extIsPositive->setUIEnabled(
true);
74 tempctrl->m_form->m_toolBox->setItemText(m_idx,
getLabel());
77 XTempControl::Loop::start() {
78 auto tempctrl = m_tempctrl.lock();
79 if( !tempctrl)
return;
82 if(hasExtDevice(shot)) {
83 tr[ m_heaterMode].clear();
84 tr[ m_heaterMode].add(
"Off");
85 tr[ m_heaterMode].add(
"PID");
86 tr[ m_heaterMode].add(
"Man");
89 tr[ *m_powerRange].setUIEnabled(
true);
92 m_currentChannel->setUIEnabled(
true);
93 m_heaterMode->setUIEnabled(
true);
94 m_prop->setUIEnabled(
true);
95 m_int->setUIEnabled(
true);
96 m_deriv->setUIEnabled(
true);
97 m_manualPower->setUIEnabled(
true);
98 m_targetTemp->setUIEnabled(
true);
100 m_extDevice->setUIEnabled(
false);
101 m_extDCSourceChannel->setUIEnabled(
false);
102 m_extIsPositive->setUIEnabled(
false);
106 m_lasttime = XTime::now();
109 m_lsnOnPChanged = tr[ *m_prop].onValueChanged().connectWeakly(shared_from_this(), &XTempControl::Loop::onPChanged);
110 m_lsnOnIChanged = tr[ *m_int].onValueChanged().connectWeakly(shared_from_this(), &XTempControl::Loop::onIChanged);
111 m_lsnOnDChanged = tr[ *m_deriv].onValueChanged().connectWeakly(shared_from_this(), &XTempControl::Loop::onDChanged);
112 m_lsnOnTargetTempChanged = tr[ *m_targetTemp].onValueChanged().connectWeakly(shared_from_this(), &XTempControl::Loop::onTargetTempChanged);
113 m_lsnOnManualPowerChanged = tr[ *m_manualPower].onValueChanged().connectWeakly(shared_from_this(), &XTempControl::Loop::onManualPowerChanged);
114 m_lsnOnHeaterModeChanged = tr[ *m_heaterMode].onValueChanged().connectWeakly(shared_from_this(), &XTempControl::Loop::onHeaterModeChanged);
115 m_lsnOnPowerRangeChanged = tr[ *m_powerRange].onValueChanged().connectWeakly(shared_from_this(), &XTempControl::Loop::onPowerRangeChanged);
116 m_lsnOnCurrentChannelChanged
117 = tr[ *m_currentChannel].onValueChanged().connectWeakly(shared_from_this(), &XTempControl::Loop::onCurrentChannelChanged);
121 XTempControl::Loop::stop() {
122 m_currentChannel->setUIEnabled(
false);
123 m_powerRange->setUIEnabled(
false);
124 m_heaterMode->setUIEnabled(
false);
125 m_prop->setUIEnabled(
false);
126 m_int->setUIEnabled(
false);
127 m_deriv->setUIEnabled(
false);
128 m_manualPower->setUIEnabled(
false);
129 m_targetTemp->setUIEnabled(
false);
131 m_extDevice->setUIEnabled(
true);
132 m_extDCSourceChannel->setUIEnabled(
true);
133 m_extIsPositive->setUIEnabled(
true);
135 m_lsnOnPChanged.reset();
136 m_lsnOnIChanged.reset();
137 m_lsnOnDChanged.reset();
138 m_lsnOnTargetTempChanged.reset();
139 m_lsnOnManualPowerChanged.reset();
140 m_lsnOnPowerMaxChanged.reset();
141 m_lsnOnPowerMinChanged.reset();
142 m_lsnOnHeaterModeChanged.reset();
143 m_lsnOnPowerRangeChanged.reset();
144 m_lsnOnCurrentChannelChanged.reset();
147 XTempControl::Loop::update(
double temp) {
148 auto tempctrl = m_tempctrl.lock();
149 if( !tempctrl)
return;
153 double tau = shot[ *m_int] * 4.0;
156 XTime newtime = XTime::now();
157 double dt = newtime - m_lasttime;
158 m_lasttime = newtime;
159 double terr = temp - shot[ *m_targetTemp];
160 m_tempAvg = (m_tempAvg - temp) * exp( -dt / tau) + temp;
161 m_tempErrAvg = (m_tempErrAvg - terr * terr) * exp( -dt / tau) + terr * terr;
162 m_tempErrAvg = std::min(m_tempErrAvg, temp * temp * 0.04);
165 shared_ptr<XDCSource> dcsrc = shot[ *m_extDevice];
166 shared_ptr<XFlowControllerDriver> flowctrl = shot[ *m_extDevice];
167 if(dcsrc || flowctrl) {
168 double limit_min = shot[ *m_powerMin];
169 double limit_max = shot[ *m_powerMax];
170 if(shot[ *m_heaterMode].to_str() ==
"PID") {
171 power = pid(shot, newtime, temp);
173 if(shot[ *m_heaterMode].to_str() ==
"Man") {
174 power = shot[ *m_manualPower];
177 int ch = shot[ *m_extDCSourceChannel];
179 limit_max = std::min(limit_max, dcsrc->max(ch,
false));
180 power = (limit_max - limit_min) * sqrt(power) / 10.0 + limit_min;
181 dcsrc->changeValue(ch, power,
false);
185 limit_max = std::min(limit_max,
Snapshot( *flowctrl)[ *flowctrl].fullScale());
186 power = (limit_max - limit_min) * power / 100.0 + limit_min;
187 trans( *flowctrl->target()) = power;
191 power = tempctrl->getHeater(m_idx);
194 tr[ *m_sourceTemp] = temp;
195 tr[ *m_stabilized] = sqrt(m_tempErrAvg);
196 tr[ *m_heaterPower] = power;
198 tempctrl->m_form->m_toolBox->setItemText(m_idx,
XString(
getLabel() + formatString(
": %.5g K, %.3g%s", temp, power,
199 tempctrl->m_heaterPowerUnit(m_idx))));
202 double XTempControl::Loop::pid(
const Snapshot &shot,
XTime time,
double temp) {
203 double p = shot[ *m_prop];
204 double i = shot[ *m_int];
205 double d = shot[ *m_deriv];
207 double dt = temp - shot[ *m_targetTemp];
208 if(shot[ *m_extIsPositive])
212 if((i > 0) && (time - m_pidLastTime < i)) {
213 m_pidAccum += (time - m_pidLastTime) * dt;
214 dxdt = (temp - m_pidLastTemp) / (time - m_pidLastTime);
215 acc = m_pidAccum / i;
216 acc = -std::min(std::max( -acc * p, -2.0), 100.0) / p;
217 m_pidAccum = acc * i;
222 m_pidLastTime = time;
223 m_pidLastTemp = temp;
225 return -(dt + acc + dxdt * d) * p;
230 tr[ *m_extDCSourceChannel].clear();
231 shared_ptr<XDCSource> dcsrc = shot[ *m_extDevice];
234 auto strings(dcsrc->channel()->itemStrings(
Snapshot( *dcsrc)));
235 for(
auto it = strings.begin(); it != strings.end(); it++) {
236 tr[ *m_extDCSourceChannel].add(it->label);
242 auto tempctrl = m_tempctrl.lock();
243 if( !tempctrl)
return;
246 if( !hasExtDevice(shot))
247 tempctrl->onPChanged(m_idx, shot[ *m_prop]);
254 auto tempctrl = m_tempctrl.lock();
255 if( !tempctrl)
return;
258 if( !hasExtDevice(shot))
259 tempctrl->onIChanged(m_idx, shot[ *m_int]);
266 auto tempctrl = m_tempctrl.lock();
267 if( !tempctrl)
return;
270 if( !hasExtDevice(shot))
271 tempctrl->onDChanged(m_idx, shot[ *m_deriv]);
278 auto tempctrl = m_tempctrl.lock();
279 if( !tempctrl)
return;
282 if( !hasExtDevice(shot))
283 tempctrl->onTargetTempChanged(m_idx, shot[ *m_targetTemp]);
290 auto tempctrl = m_tempctrl.lock();
291 if( !tempctrl)
return;
294 if( !hasExtDevice(shot))
295 tempctrl->onManualPowerChanged(m_idx, shot[ *m_manualPower]);
302 auto tempctrl = m_tempctrl.lock();
303 if( !tempctrl)
return;
307 if( !hasExtDevice(shot))
308 tempctrl->onHeaterModeChanged(m_idx, shot[ *m_heaterMode]);
315 auto tempctrl = m_tempctrl.lock();
316 if( !tempctrl)
return;
319 if( !hasExtDevice(shot))
320 tempctrl->onPowerRangeChanged(m_idx, shot[ *m_powerMax]);
327 auto tempctrl = m_tempctrl.lock();
328 if( !tempctrl)
return;
331 if( !hasExtDevice(shot))
332 tempctrl->onPowerRangeChanged(m_idx, shot[ *m_powerMin]);
339 auto tempctrl = m_tempctrl.lock();
340 if( !tempctrl)
return;
343 if( !hasExtDevice(shot))
344 tempctrl->onPowerRangeChanged(m_idx, shot[ *m_powerRange]);
351 auto tempctrl = m_tempctrl.lock();
352 if( !tempctrl)
return;
355 shared_ptr<XChannel> ch(shot[ *m_currentChannel]);
358 tempctrl->onCurrentChannelChanged(m_idx, ch);
365 XTempControl::XTempControl(
const char *name,
bool runtime,
366 Transaction &tr_meas,
const shared_ptr<XMeasure> &meas) :
368 m_channels(create<XChannelList> (
"Channels", false)),
373 create<XItemNode<XChannelList, XChannel> >(tr,
"SetupChannel",
true, ref(tr), m_channels);
375 m_conSetupChannel = xqcon_create<XQComboBoxConnector> (m_setupChannel,
376 m_form->m_cmbSetupChannel,
Snapshot( *m_channels));
379 m_lsnOnSetupChannelChanged = tr[ *m_setupChannel].onValueChanged().connectWeakly(
380 shared_from_this(), &XTempControl::onSetupChannelChanged);
383 m_form->statusBar()->hide();
384 m_form->setWindowTitle(i18n(
"TempControl - ") +
getLabel());
389 m_form->showNormal();
397 uint16_t chno = reader.pop<uint16_t> ();
398 reader.pop<uint16_t> ();
399 float raw = reader.pop<
float> ();
400 float temp = reader.pop<
float> ();
403 if(chno >= m_entry_temps.size())
405 m_entry_temps[chno]->value(tr, temp);
406 m_entry_raws[chno]->value(tr, raw);
416 m_conThermometer.reset();
417 m_conExcitation.reset();
418 m_lsnOnExcitationChanged.reset();
419 shared_ptr<XChannel> channel = shot[ *m_setupChannel];
422 m_conThermometer = xqcon_create<XQComboBoxConnector> (
423 channel->thermometer(), m_form->m_cmbThermometer,
Snapshot( *channel->thermometers()));
424 m_conExcitation = xqcon_create<XQComboBoxConnector> (channel->excitation(),
425 m_form->m_cmbExcitation,
Snapshot( *channel->excitation()));
427 m_lsnOnExcitationChanged
428 = tr[ *channel->excitation()].onValueChanged().connectWeakly(
430 &XTempControl::onExcitationChangedInternal);
435 Transaction &tr_meas,
const shared_ptr<XMeasure> &meas,
436 bool multiread, std::initializer_list<XString> channel_names,
437 std::initializer_list<XString> excitations,
438 std::initializer_list<XString> loop_names) {
439 shared_ptr<XScalarEntryList> entries(meas->scalarEntries());
440 m_multiread = multiread;
443 for(
auto &&ch: channel_names) {
444 shared_ptr<XChannel> channel = m_channels->create<
XChannel> (
445 tr, ch.c_str(),
false, ref(tr_meas), meas->thermometers());
446 tr[ *channel->excitation()].add(excitations);
453 for(XNode::const_iterator it = list.begin(); it != list.end(); it++) {
454 shared_ptr<XChannel> channel =
455 dynamic_pointer_cast<
XChannel> (*it);
456 shared_ptr<XScalarEntry> entry_temp(
457 create<XScalarEntry>(
458 QString(
"Ch.%1").arg(channel->getName()).toLocal8Bit().data(),
459 false, dynamic_pointer_cast<
XDriver> (shared_from_this()),
"%.5g"));
460 shared_ptr<XScalarEntry> entry_raw(
461 create<XScalarEntry> (
462 QString(
"Ch.%1.raw").arg(
463 channel->getName()).toLocal8Bit().data(),
false,
464 dynamic_pointer_cast<
XDriver> (shared_from_this()),
"%.5g"));
465 m_entry_temps.push_back(entry_temp);
466 m_entry_raws.push_back(entry_raw);
467 entries->insert(tr_meas, entry_temp);
468 entries->insert(tr_meas, entry_raw);
473 shared_ptr<XScalarEntry> entry_temp(create<XScalarEntry> (
475 dynamic_pointer_cast<XDriver> (shared_from_this()),
"%.5g"));
476 shared_ptr<XScalarEntry> entry_raw(create<XScalarEntry> (
478 dynamic_pointer_cast<XDriver> (shared_from_this()),
"%.5g"));
479 m_entry_temps.push_back(entry_temp);
480 m_entry_raws.push_back(entry_raw);
481 entries->insert(tr_meas, entry_temp);
482 entries->insert(tr_meas, entry_raw);
485 unsigned int num_of_loops = 0;
486 for(
auto &&lp: loop_names) {
491 dynamic_pointer_cast<
XTempControl>(shared_from_this()), ref(tr), num_of_loops,
494 m_loops.push_back(p);
497 if(num_of_loops > 1) {
500 xqcon_create<XQComboBoxConnector> (lp->m_currentChannel,
501 m_form->m_cmbSourceChannel2,
Snapshot( *m_channels)),
502 xqcon_create<XQComboBoxConnector> (lp->m_powerRange,
503 m_form->m_cmbPowerRange2,
Snapshot( *lp->m_powerRange)),
504 xqcon_create<XQComboBoxConnector> (lp->m_heaterMode,
505 m_form->m_cmbHeaterMode2,
Snapshot( *lp->m_heaterMode)),
506 xqcon_create<XQLineEditConnector> (lp->m_prop, m_form->m_edP2),
507 xqcon_create<XQLineEditConnector> (lp->m_int, m_form->m_edI2),
508 xqcon_create<XQLineEditConnector> (lp->m_deriv, m_form->m_edD2),
509 xqcon_create<XQLineEditConnector> (lp->m_manualPower, m_form->m_edManHeater2),
510 xqcon_create<XQLineEditConnector> (lp->m_powerMax, m_form->m_edPowerMax2),
511 xqcon_create<XQLineEditConnector> (lp->m_powerMin, m_form->m_edPowerMin2),
512 xqcon_create<XQLineEditConnector> (lp->m_targetTemp, m_form->m_edTargetTemp2),
513 xqcon_create<XQLCDNumberConnector> (lp->m_heaterPower, m_form->m_lcdHeater2),
514 xqcon_create<XQLCDNumberConnector> (lp->m_sourceTemp, m_form->m_lcdSourceTemp2),
515 xqcon_create<XQComboBoxConnector> (lp->m_extDevice, m_form->m_cmbExtDevice2, ref(tr_meas)),
516 xqcon_create<XQComboBoxConnector> (
517 lp->m_extDCSourceChannel, m_form->m_cmbExtDCSrcCh2,
Snapshot( *lp->m_extDCSourceChannel)),
518 xqcon_create<XQToggleButtonConnector>( lp->m_extIsPositive, m_form->m_ckbExtIsPositive2)
523 m_form->m_toolBox->removeItem(1);
524 m_form->m_pageLoop2->hide();
529 xqcon_create<XQComboBoxConnector> (lp->m_currentChannel,
530 m_form->m_cmbSourceChannel,
Snapshot( *m_channels)),
531 xqcon_create<XQComboBoxConnector> (lp->m_powerRange,
532 m_form->m_cmbPowerRange,
Snapshot( *lp->m_powerRange)),
533 xqcon_create<XQComboBoxConnector> (lp->m_heaterMode,
534 m_form->m_cmbHeaterMode,
Snapshot( *lp->m_heaterMode)),
535 xqcon_create<XQLineEditConnector> (lp->m_prop, m_form->m_edP),
536 xqcon_create<XQLineEditConnector> (lp->m_int, m_form->m_edI),
537 xqcon_create<XQLineEditConnector> (lp->m_deriv, m_form->m_edD),
538 xqcon_create<XQLineEditConnector> (lp->m_manualPower, m_form->m_edManHeater),
539 xqcon_create<XQLineEditConnector> (lp->m_powerMax, m_form->m_edPowerMax),
540 xqcon_create<XQLineEditConnector> (lp->m_powerMin, m_form->m_edPowerMin),
541 xqcon_create<XQLineEditConnector> (lp->m_targetTemp, m_form->m_edTargetTemp),
542 xqcon_create<XQLCDNumberConnector> (lp->m_heaterPower, m_form->m_lcdHeater),
543 xqcon_create<XQLCDNumberConnector> (lp->m_sourceTemp, m_form->m_lcdSourceTemp),
544 xqcon_create<XQComboBoxConnector> (lp->m_extDevice, m_form->m_cmbExtDevice, ref(tr_meas)),
545 xqcon_create<XQComboBoxConnector> (
546 lp->m_extDCSourceChannel, m_form->m_cmbExtDCSrcCh,
Snapshot( *lp->m_extDCSourceChannel)),
547 xqcon_create<XQToggleButtonConnector>( lp->m_extIsPositive, m_form->m_ckbExtIsPositive)
551 m_form->m_toolBox->removeItem(0);
552 m_form->m_pageLoop1->hide();
558 for(
auto it = m_loops.begin(); it != m_loops.end(); ++it) {
562 while( !terminated) {
565 auto writer = std::make_shared<RawData>();
568 XTime time_awared = XTime::now();
571 if(shot.size(m_channels)) {
573 unsigned int idx = 0;
574 for(XNode::const_iterator it = list.begin(); it != list.end(); it++) {
575 shared_ptr<XChannel> ch = static_pointer_cast<XChannel>( *it);
576 bool src_found =
false;
577 for(
auto lit = m_loops.begin(); lit != m_loops.end(); ++lit) {
578 shared_ptr<XChannel> curch = shot[ ( *lit)->m_currentChannel];
582 if(m_multiread || src_found) {
583 shared_ptr<XThermometer> thermo = shot[ *ch->thermometer()];
585 temp = ( !thermo) ?
getTemp(ch) : thermo->getTemp(raw);
587 for(
auto lit = m_loops.begin(); lit != m_loops.end(); ++lit) {
588 shared_ptr<XChannel> curch = shot[ ( *lit)->m_currentChannel];
590 ( *lit)->update(temp);
592 writer->push((uint16_t) idx);
593 writer->push((uint16_t) 0);
594 writer->push(
float(raw));
595 writer->push(
float(temp));
609 trans( *m_setupChannel) = shared_ptr<XThermometer>();
611 for(
auto it = m_loops.begin(); it != m_loops.end(); ++it) {
620 shared_ptr<XChannel> ch;
624 for(XNode::const_iterator it = list.begin(); it != list.end(); it++) {
625 shared_ptr<XChannel> ch__ =
626 dynamic_pointer_cast<XChannel> ( *it);
627 if(ch__->excitation().get() == node)
633 int exc = shot[ *ch->excitation()];
636 onExcitationChanged(ch, exc);