34static std::unique_ptr<DeltaT> deltat(
new DeltaT());
49 IUFillText(&InfoT[INFO_VERSION],
"INFO_VERSION",
"Version",
"NA");
55 IUFillSwitchVector(&ForceSP, ForceS, 2,
getDeviceName(),
"FORCE_CONTROL",
"Force",
MAIN_CONTROL_TAB,
IP_RW,
ISR_ATMOST1, 60,
59 IUFillNumber(&TemperatureN[TEMPERATURE_AMBIENT],
"TEMPERATURE_AMBIENT",
"Ambient (c)",
"%.2f", -50, 70., 0., 0.);
60 IUFillNumber(&TemperatureN[TEMPERATURE_SECONDARY],
"TEMPERATURE_SECONDARY",
"Secondary (c)",
"%.2f", -50, 70., 0., 0.);
61 IUFillNumber(&TemperatureN[TEMPERATURE_BACKPLATE],
"TEMPERATURE_BACKPLATE",
"Backplate (c)",
"%.2f", -50, 70., 0., 0.);
96 for (
auto &oneSP : HeaterControlSP)
99 for (
auto &oneNP : HeaterParamNP)
102 for (
auto &oneNP : HeaterMonitorNP)
112 for (
auto &oneSP : HeaterControlSP)
115 for (
auto &oneNP : HeaterParamNP)
118 for (
auto &oneNP : HeaterMonitorNP)
134 char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
141 cmd[5] = calculateCheckSum(
cmd, 6);
143 if (!sendCommand(
cmd, res, 6, 10))
146 const uint8_t *ures =
reinterpret_cast<const uint8_t *
>(res);
147 uint16_t bld = ures[7] << 8 | ures[8];
149 version = std::to_string(ures[5]) +
"." +
150 std::to_string(ures[6]) +
151 " (" + std::to_string(bld) +
")";
155 LOGF_INFO(
"Detected version %s", version.c_str());
165 return "PlaneWave DeltaT";
171const char *DeltaT::getHeaterName(
int index)
176 return "Primary Backplate Heater";
178 return "Secondary Mirror Heater";
180 return "Terietary Heater";
182 return "Uknkown heater";
194 if (!strcmp(ForceSP.
name, name))
211 for (uint8_t i = 0; i < HeaterControlSP.size(); i++)
213 if (!strcmp(HeaterControlSP[i]->name, name))
221 setHeaterEnabled(i,
false);
222 LOGF_INFO(
"%s is off.", getHeaterName(i));
226 setHeaterEnabled(i,
true);
227 LOGF_INFO(
"%s is on.", getHeaterName(i));
231 LOGF_INFO(
"%s automatic control is enabled. Temperature delta will kept at %.2f C", getHeaterName(i),
232 HeaterParamNP[i]->np[PARAM_CONTROL].value);
235 case HEATER_THRESHOLD:
236 LOGF_INFO(
"%s threshold control is enabled. When ambient temperature falls below %.2f C, heater would be turned on at %.f%% power.",
238 HeaterParamNP[i]->np[PARAM_THRESHOLD].value,
239 HeaterParamNP[i]->np[PARAM_DUTY].value);
243 HeaterControlSP[i]->s =
IPS_OK;
262 for (uint8_t i = 0; i < HeaterParamNP.size(); i++)
264 if (!strcmp(HeaterParamNP[i]->name, name))
267 HeaterParamNP[i]->s =
IPS_OK;
272 setHeaterEnabled(i,
true);
292 for (uint8_t i = 0 ; i < HeaterControlSP.size(); i++)
301 if (readTemperature())
304 bool aboveThreshold =
false;
305 for (
int i = 0; i < TemperatureNP.
nnp; i++)
307 if (std::fabs(TemperatureN[i].value - m_LastTemperature[i]) > TEMPERATURE_REPORT_THRESHOLD)
309 aboveThreshold =
true;
310 m_LastTemperature[i] = TemperatureN[i].value;
318 for (uint8_t i = 0 ; i < HeaterControlSP.size(); i++)
331 double surfaceTemperature = TemperatureN[i == 0 ? TEMPERATURE_BACKPLATE : TEMPERATURE_SECONDARY].value;
333 double targetTemperature = HeaterParamNP[i]->np[PARAM_CONTROL].value + TemperatureN[TEMPERATURE_AMBIENT].value;
335 double targetDuty = m_Controllers[i]->calculate(targetTemperature, surfaceTemperature);
337 double heaterDuty = std::max(0., std::min(100., targetDuty));
339 if (std::abs(heaterDuty - HeaterMonitorNP[i]->np[MONITOR_DUTY].value) > 0.001 ||
340 std::abs(HeaterParamNP[i]->np[PARAM_PERIOD].value - HeaterMonitorNP[i]->np[MONITOR_PERIOD].value) > 0)
341 setHeaterParam(i, HeaterParamNP[i]->np[PARAM_PERIOD].value, heaterDuty);
345 case HEATER_THRESHOLD:
349 if (std::fabs(TemperatureN[TEMPERATURE_AMBIENT].value - HeaterParamNP[i]->np[PARAM_THRESHOLD].value)
350 < TEMPERATURE_CONTROL_THRESHOLD)
354 if (HeaterMonitorNP[i]->s ==
IPS_IDLE
355 && (TemperatureN[TEMPERATURE_AMBIENT].value < HeaterParamNP[i]->np[PARAM_THRESHOLD].value))
356 setHeaterEnabled(i,
true);
357 else if (HeaterMonitorNP[i]->s ==
IPS_BUSY
358 && (TemperatureN[TEMPERATURE_AMBIENT].value > HeaterParamNP[i]->np[PARAM_THRESHOLD].value))
359 setHeaterEnabled(i,
false);
375 for (uint8_t i = 0; i < HeaterParamNP.size(); i++)
383bool DeltaT::readReport(uint8_t index)
385 char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
393 cmd[6] = calculateCheckSum(
cmd, 7);
395 if (!sendCommand(
cmd, res, 7, 19))
398 if (
static_cast<uint8_t
>(res[5]) != 0x80)
401 const uint8_t *ures =
reinterpret_cast<const uint8_t *
>(res);
404 report.StateUB = ures[6];
405 report.ModeUB = ures[7];
406 report.SetPointUW = ures[9] << 8 | ures[8];
407 report.TempHtrIdUB = ures[10];
408 report.TempHtrUW = ures[12] << 8 | ures[11];
409 report.TempAmbUW = ures[14] << 8 | ures[13];
410 report.PeriodUW = ures[16] << 8 | ures[15];
411 report.DutyCycleUB = ures[17];
425 double currentPeriod = HeaterMonitorNP[index]->np[MONITOR_PERIOD].value;
426 double currentDuty = HeaterMonitorNP[index]->np[MONITOR_DUTY].value;
427 IPState currentState = HeaterMonitorNP[index]->s;
429 HeaterMonitorNP[index]->np[MONITOR_PERIOD].value = report.PeriodUW / 10.0;
430 HeaterMonitorNP[index]->np[MONITOR_DUTY].value = report.DutyCycleUB;
433 bool paramChanged = std::fabs(currentPeriod - HeaterMonitorNP[index]->np[MONITOR_PERIOD].value) > 0.1 ||
434 std::fabs(
currentDuty - HeaterMonitorNP[index]->np[MONITOR_DUTY].value) > 0 ||
435 currentState != HeaterMonitorNP[index]->s;
438 return (paramChanged);
444bool DeltaT::initializeHeaters()
446 char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
453 cmd[5] = calculateCheckSum(
cmd, 6);
455 if (!sendCommand(
cmd, res, 6, 7))
458 uint8_t nHeaters = res[5];
460 LOGF_INFO(
"Detected %d heaters", nHeaters);
463 for (uint8_t i = 0; i < nHeaters; i++)
466 std::unique_ptr<PID> Controller;
467 Controller.reset(
new PID(1, 100, 0, 200, 0, 0.75));
468 m_Controllers.push_back(std::move(Controller));
470 std::unique_ptr<ISwitchVectorProperty> ControlSP;
472 std::unique_ptr<ISwitch[]> ControlS;
473 ControlS.reset(
new ISwitch[4]);
476 snprintf(switchName,
MAXINDINAME,
"HEATER_%d", i + 1);
477 snprintf(groupLabel,
MAXINDINAME,
"%s", getHeaterName(i));
485 HeaterControlSP.push_back(std::move(ControlSP));
486 HeaterControlS.push_back(std::move(ControlS));
490 for (uint8_t i = 0; i < nHeaters; i++)
494 std::unique_ptr<INumberVectorProperty> ControlNP;
496 std::unique_ptr<INumber[]> ControlN;
497 ControlN.reset(
new INumber[4]);
500 snprintf(numberName,
MAXINDINAME,
"PARAM_%d", i + 1);
501 snprintf(groupLabel,
MAXINDINAME,
"%s", getHeaterName(i));
502 IUFillNumber(&ControlN[PARAM_PERIOD],
"PARAM_PERIOD",
"Period",
"%.1f", 0.1, 60, 1, 1);
503 IUFillNumber(&ControlN[PARAM_DUTY],
"PARAM_DUTY",
"Duty",
"%.f", 1, 100, 5, 1);
504 IUFillNumber(&ControlN[PARAM_CONTROL],
"PARAM_CONTROL",
"ΔAmbient =",
"%.1f", 0, 100, 5, 2.5);
505 IUFillNumber(&ControlN[PARAM_THRESHOLD],
"PARAM_THRESHOLD",
"Ambient less",
"%.1f", -50, 50, 5, 2.5);
509 HeaterParamNP.push_back(std::move(ControlNP));
510 HeaterParamN.push_back(std::move(ControlN));
514 std::unique_ptr<INumberVectorProperty> MonitorNP;
516 std::unique_ptr<INumber[]> MonitorN;
517 MonitorN.reset(
new INumber[2]);
519 snprintf(numberName,
MAXINDINAME,
"MONITOR_%d", i + 1);
520 snprintf(groupLabel,
MAXINDINAME,
"%s", getHeaterName(i));
521 IUFillNumber(&MonitorN[MONITOR_PERIOD],
"MONITOR_PERIOD",
"Period",
"%.1f", 0.1, 60, 1, 1);
522 IUFillNumber(&MonitorN[MONITOR_DUTY],
"MONITOR_DUTY",
"Duty",
"%.f", 1, 100, 5, 1);
526 HeaterMonitorNP.push_back(std::move(MonitorNP));
527 HeaterMonitorN.push_back(std::move(MonitorN));
536bool DeltaT::setHeaterEnabled(uint8_t index,
bool enabled)
540 char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
548 cmd[6] = calculateCheckSum(
cmd, 7);
550 if (!sendCommand(
cmd, res, 7, 7))
554 return (
static_cast<uint8_t
>(res[5]) == 0x80);
558 return setHeaterParam(index, HeaterParamNP[index]->np[PARAM_PERIOD].value, HeaterParamNP[index]->np[PARAM_DUTY].value);
565bool DeltaT::setHeaterParam(uint8_t index,
double period,
double duty)
567 char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
569 uint16_t seconds = period * 10;
578 cmd[6] = seconds & 0xFF;
580 cmd[7] = (seconds >> 8) & 0xFF;
581 cmd[8] =
static_cast<uint8_t
>(duty);
582 cmd[9] = calculateCheckSum(
cmd, 10);
584 if (!sendCommand(
cmd, res, 10, 7))
588 return (
static_cast<uint8_t
>(res[5]) == 0x80);
594bool DeltaT::forceBoot()
596 char cmd[DRIVER_LEN] = {0};
602 cmd[5] = calculateCheckSum(
cmd, 6);
603 return sendCommand(
cmd,
nullptr, 6, 0);
609bool DeltaT::forceReset()
611 char cmd[DRIVER_LEN] = {0};
617 cmd[5] = calculateCheckSum(
cmd, 6);
618 return sendCommand(
cmd,
nullptr, 6, 0);
624bool DeltaT::readTemperature()
626 char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
628 for (uint8_t i = 0; i < 3; i++)
636 cmd[6] = calculateCheckSum(
cmd, 7);
638 if (!sendCommand(
cmd, res, 7, 8))
641 double newTemperature = calculateTemperature(res[5], res[6]);
643 if (std::abs(TemperatureN[i].value - newTemperature) > TEMPERATURE_CONTROL_THRESHOLD)
644 TemperatureN[i].value = newTemperature;
654double DeltaT::calculateTemperature(uint8_t lsb, uint8_t msb)
656 if (lsb == 0x7F && msb == 0x7F)
659 int raw_temperature = lsb << 8 | msb;
660 if (raw_temperature & 0x8000)
661 raw_temperature = raw_temperature - 0x10000;
663 return raw_temperature / 16.0;
669bool DeltaT::sendCommand(
const char *
cmd,
char * res, uint32_t cmd_len, uint32_t res_len)
671 int nbytes_written = 0, nbytes_read = 0, rc = 0;
673 for (
int j = 0; j < 3; j++)
675 char hex_cmd[DRIVER_LEN * 3] = {0};
676 hexDump(hex_cmd,
cmd, cmd_len);
684 LOGF_ERROR(
"Serial write error: %s.", errstr);
688 if (res ==
nullptr || res_len == 0)
692 rc =
tty_read(PortFD, res, res_len, DRIVER_TIMEOUT, &nbytes_read);
708 char hex_res[DRIVER_LEN * 3] = {0};
709 hexDump(hex_res, res, res_len);
713 if (res_len > 0 && calculateCheckSum(res, res_len) !=
static_cast<uint8_t
>(res[res_len - 1]))
725void DeltaT::hexDump(
char * buf,
const char * data, uint32_t size)
727 for (uint32_t i = 0; i < size; i++)
728 sprintf(buf + 3 * i,
"%02X ",
static_cast<uint8_t
>(data[i]));
731 buf[3 * size - 1] =
'\0';
737std::vector<std::string> DeltaT::split(
const std::string &input,
const std::string ®ex)
740 std::regex re(regex);
741 std::sregex_token_iterator
742 first{input.begin(), input.end(), re, -1},
744 return {first, last};
751std::string DeltaT::to_string(
const T a_value,
const int n)
753 std::ostringstream out;
755 out << std::fixed << a_value;
762uint8_t DeltaT::calculateCheckSum(
const char *
cmd, uint32_t len)
765 for (uint32_t i = 1; i < len - 1; i++)
767 return (-sum & 0xFF);
void registerHandshake(std::function< bool()> callback)
registerHandshake Register a handshake function to be called once the initial connection to the devic...
The Serial class manages connection with serial devices including Bluetooth. Serial communication is ...
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
const char * getDefaultName() override
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
const char * getDeviceName() const
virtual bool updateProperties()
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process the client newSwitch command.
void registerConnection(Connection::Interface *newConnection)
registerConnection Add new connection plugin to the existing connection pool. The connection type sha...
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
virtual bool initProperties()
Initialize properties initial state and value. The child class must implement this function.
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process the client newNumber command.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Implementations for common driver routines.
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
const char * IUFindOnSwitchName(ISState *states, char *names[], int n)
Returns the name of the first ON switch it finds in the supplied arguments.
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
#define LOGF_INFO(fmt,...)
#define LOGF_DEBUG(fmt,...)
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
#define LOGF_ERROR(fmt,...)
Number vector property descriptor.
Switch vector property descriptor.