32 #include <sys/ioctl.h>
34 static 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 uint16_t bld = res[7] << 8 | res[8];
154 LOGF_INFO(
"Detected version %s", version.c_str());
164 return "PlaneWave DeltaT";
170 const char *DeltaT::getHeaterName(
int index)
175 return "Primary Backplate Heater";
177 return "Secondary Mirror Heater";
179 return "Terietary Heater";
181 return "Uknkown heater";
193 if (!strcmp(ForceSP.
name, name))
210 for (uint8_t i = 0; i < HeaterControlSP.size(); i++)
212 if (!strcmp(HeaterControlSP[i]->name, name))
220 setHeaterEnabled(i,
false);
221 LOGF_INFO(
"%s is off.", getHeaterName(i));
225 setHeaterEnabled(i,
true);
226 LOGF_INFO(
"%s is on.", getHeaterName(i));
230 LOGF_INFO(
"%s automatic control is enabled. Temperature delta will kept at %.2f C", getHeaterName(i),
231 HeaterParamNP[i]->np[PARAM_CONTROL].value);
234 case HEATER_THRESHOLD:
235 LOGF_INFO(
"%s threshold control is enabled. When ambient temperature falls below %.2f C, heater would be turned on at %.f%% power.",
237 HeaterParamNP[i]->np[PARAM_THRESHOLD].value,
238 HeaterParamNP[i]->np[PARAM_DUTY].value);
242 HeaterControlSP[i]->s =
IPS_OK;
261 for (uint8_t i = 0; i < HeaterParamNP.size(); i++)
263 if (!strcmp(HeaterParamNP[i]->name, name))
266 HeaterParamNP[i]->s =
IPS_OK;
271 setHeaterEnabled(i,
true);
291 for (uint8_t i = 0 ; i < HeaterControlSP.size(); i++)
300 if (readTemperature())
303 bool aboveThreshold =
false;
304 for (
int i = 0; i < TemperatureNP.
nnp; i++)
306 if (std::fabs(TemperatureN[i].value - m_LastTemperature[i]) > TEMPERATURE_REPORT_THRESHOLD)
308 aboveThreshold =
true;
309 m_LastTemperature[i] = TemperatureN[i].value;
317 for (uint8_t i = 0 ; i < HeaterControlSP.size(); i++)
330 double surfaceTemperature = TemperatureN[i == 0 ? TEMPERATURE_BACKPLATE : TEMPERATURE_SECONDARY].value;
332 double targetTemperature = HeaterParamNP[i]->np[PARAM_CONTROL].value + TemperatureN[TEMPERATURE_AMBIENT].value;
334 double targetDuty = m_Controllers[i]->calculate(targetTemperature, surfaceTemperature);
338 if (std::abs(heaterDuty - HeaterMonitorNP[i]->np[MONITOR_DUTY].value) > 0.001 ||
339 std::abs(HeaterParamNP[i]->np[PARAM_PERIOD].value - HeaterMonitorNP[i]->np[MONITOR_PERIOD].value) > 0)
340 setHeaterParam(i, HeaterParamNP[i]->np[PARAM_PERIOD].value, heaterDuty);
344 case HEATER_THRESHOLD:
348 if (std::fabs(TemperatureN[TEMPERATURE_AMBIENT].value - HeaterParamNP[i]->np[PARAM_THRESHOLD].value)
349 < TEMPERATURE_CONTROL_THRESHOLD)
353 if (HeaterMonitorNP[i]->s ==
IPS_IDLE
354 && (TemperatureN[TEMPERATURE_AMBIENT].value < HeaterParamNP[i]->np[PARAM_THRESHOLD].value))
355 setHeaterEnabled(i,
true);
356 else if (HeaterMonitorNP[i]->s ==
IPS_BUSY
357 && (TemperatureN[TEMPERATURE_AMBIENT].value > HeaterParamNP[i]->np[PARAM_THRESHOLD].value))
358 setHeaterEnabled(i,
false);
374 for (uint8_t i = 0; i < HeaterParamNP.size(); i++)
382 bool DeltaT::readReport(uint8_t index)
384 char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
392 cmd[6] = calculateCheckSum(
cmd, 7);
394 if (!sendCommand(
cmd, res, 7, 19))
397 if (
static_cast<uint8_t
>(res[5]) != 0x80)
402 report.StateUB = res[6];
403 report.ModeUB = res[7];
404 report.SetPointUW = res[9] << 8 | res[8];
405 report.TempHtrIdUB = res[10];
406 report.TempHtrUW = res[12] << 8 | res[11];
407 report.TempAmbUW = res[14] << 8 | res[13];
408 report.PeriodUW = res[16] << 8 | res[15];
409 report.DutyCycleUB = res[17];
423 double currentPeriod = HeaterMonitorNP[index]->np[MONITOR_PERIOD].value;
424 double currentDuty = HeaterMonitorNP[index]->np[MONITOR_DUTY].value;
425 IPState currentState = HeaterMonitorNP[index]->s;
427 HeaterMonitorNP[index]->np[MONITOR_PERIOD].value = report.PeriodUW / 10.0;
428 HeaterMonitorNP[index]->np[MONITOR_DUTY].value = report.DutyCycleUB;
431 bool paramChanged = std::fabs(currentPeriod - HeaterMonitorNP[index]->np[MONITOR_PERIOD].value) > 0.1 ||
432 std::fabs(
currentDuty - HeaterMonitorNP[index]->np[MONITOR_DUTY].value) > 0 ||
433 currentState != HeaterMonitorNP[index]->s;
436 return (paramChanged);
442 bool DeltaT::initializeHeaters()
444 char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
451 cmd[5] = calculateCheckSum(
cmd, 6);
453 if (!sendCommand(
cmd, res, 6, 7))
456 uint8_t nHeaters = res[5];
458 LOGF_INFO(
"Detected %d heaters", nHeaters);
461 for (uint8_t i = 0; i < nHeaters; i++)
464 std::unique_ptr<PID> Controller;
465 Controller.reset(
new PID(1, 100, 0, 200, 0, 0.75));
466 m_Controllers.push_back(std::move(Controller));
468 std::unique_ptr<ISwitchVectorProperty> ControlSP;
470 std::unique_ptr<ISwitch[]> ControlS;
471 ControlS.reset(
new ISwitch[4]);
474 snprintf(switchName,
MAXINDINAME,
"HEATER_%d", i + 1);
475 snprintf(groupLabel,
MAXINDINAME,
"%s", getHeaterName(i));
483 HeaterControlSP.push_back(std::move(ControlSP));
484 HeaterControlS.push_back(std::move(ControlS));
488 for (uint8_t i = 0; i < nHeaters; i++)
492 std::unique_ptr<INumberVectorProperty> ControlNP;
494 std::unique_ptr<INumber[]> ControlN;
495 ControlN.reset(
new INumber[4]);
498 snprintf(numberName,
MAXINDINAME,
"PARAM_%d", i + 1);
499 snprintf(groupLabel,
MAXINDINAME,
"%s", getHeaterName(i));
500 IUFillNumber(&ControlN[PARAM_PERIOD],
"PARAM_PERIOD",
"Period",
"%.1f", 0.1, 60, 1, 1);
501 IUFillNumber(&ControlN[PARAM_DUTY],
"PARAM_DUTY",
"Duty",
"%.f", 1, 100, 5, 1);
502 IUFillNumber(&ControlN[PARAM_CONTROL],
"PARAM_CONTROL",
"ΔAmbient =",
"%.1f", 0, 100, 5, 2.5);
503 IUFillNumber(&ControlN[PARAM_THRESHOLD],
"PARAM_THRESHOLD",
"Ambient less",
"%.1f", -50, 50, 5, 2.5);
507 HeaterParamNP.push_back(std::move(ControlNP));
508 HeaterParamN.push_back(std::move(ControlN));
512 std::unique_ptr<INumberVectorProperty> MonitorNP;
514 std::unique_ptr<INumber[]> MonitorN;
515 MonitorN.reset(
new INumber[2]);
517 snprintf(numberName,
MAXINDINAME,
"MONITOR_%d", i + 1);
518 snprintf(groupLabel,
MAXINDINAME,
"%s", getHeaterName(i));
519 IUFillNumber(&MonitorN[MONITOR_PERIOD],
"MONITOR_PERIOD",
"Period",
"%.1f", 0.1, 60, 1, 1);
520 IUFillNumber(&MonitorN[MONITOR_DUTY],
"MONITOR_DUTY",
"Duty",
"%.f", 1, 100, 5, 1);
524 HeaterMonitorNP.push_back(std::move(MonitorNP));
525 HeaterMonitorN.push_back(std::move(MonitorN));
534 bool DeltaT::setHeaterEnabled(uint8_t index,
bool enabled)
538 char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
546 cmd[6] = calculateCheckSum(
cmd, 7);
548 if (!sendCommand(
cmd, res, 7, 7))
552 return (
static_cast<uint8_t
>(res[5]) == 0x80);
556 return setHeaterParam(index, HeaterParamNP[index]->np[PARAM_PERIOD].value, HeaterParamNP[index]->np[PARAM_DUTY].value);
563 bool DeltaT::setHeaterParam(uint8_t index,
double period,
double duty)
565 char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
567 uint16_t seconds = period * 10;
576 cmd[6] = seconds & 0xFF;
578 cmd[7] = (seconds >> 8) & 0xFF;
579 cmd[8] =
static_cast<uint8_t
>(duty);
580 cmd[9] = calculateCheckSum(
cmd, 10);
582 if (!sendCommand(
cmd, res, 10, 7))
586 return (
static_cast<uint8_t
>(res[5]) == 0x80);
592 bool DeltaT::forceBoot()
594 char cmd[DRIVER_LEN] = {0};
600 cmd[5] = calculateCheckSum(
cmd, 6);
601 return sendCommand(
cmd,
nullptr, 6, 0);
607 bool DeltaT::forceReset()
609 char cmd[DRIVER_LEN] = {0};
615 cmd[5] = calculateCheckSum(
cmd, 6);
616 return sendCommand(
cmd,
nullptr, 6, 0);
622 bool DeltaT::readTemperature()
624 char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
626 for (uint8_t i = 0; i < 3; i++)
634 cmd[6] = calculateCheckSum(
cmd, 7);
636 if (!sendCommand(
cmd, res, 7, 8))
639 double newTemperature = calculateTemperature(res[5], res[6]);
641 if (std::abs(TemperatureN[i].value - newTemperature) > TEMPERATURE_CONTROL_THRESHOLD)
642 TemperatureN[i].value = newTemperature;
652 double DeltaT::calculateTemperature(uint8_t lsb, uint8_t msb)
654 if (lsb == 0x7F && msb == 0x7F)
657 int raw_temperature = lsb << 8 | msb;
658 if (raw_temperature & 0x8000)
659 raw_temperature = raw_temperature - 0x10000;
661 return raw_temperature / 16.0;
667 bool DeltaT::sendCommand(
const char *
cmd,
char * res, uint32_t cmd_len, uint32_t res_len)
669 int nbytes_written = 0, nbytes_read = 0, rc = 0;
671 for (
int j = 0; j < 3; j++)
673 char hex_cmd[DRIVER_LEN * 3] = {0};
674 hexDump(hex_cmd,
cmd, cmd_len);
682 LOGF_ERROR(
"Serial write error: %s.", errstr);
686 if (res ==
nullptr || res_len == 0)
690 rc =
tty_read(PortFD, res, res_len, DRIVER_TIMEOUT, &nbytes_read);
706 char hex_res[DRIVER_LEN * 3] = {0};
707 hexDump(hex_res, res, res_len);
710 int8_t chk = calculateCheckSum(res, res_len);
712 if (res_len > 0 && chk != res[res_len - 1])
724 void DeltaT::hexDump(
char * buf,
const char * data, uint32_t size)
726 for (uint32_t i = 0; i < size; i++)
727 sprintf(buf + 3 * i,
"%02X ",
static_cast<uint8_t
>(data[i]));
730 buf[3 * size - 1] =
'\0';
736 std::vector<std::string> DeltaT::split(
const std::string &input,
const std::string ®ex)
739 std::regex re(regex);
740 std::sregex_token_iterator
741 first{input.begin(), input.end(), re, -1},
743 return {first, last};
749 template <
typename T>
750 std::string DeltaT::to_string(
const T a_value,
const int n)
752 std::ostringstream out;
754 out << std::fixed << a_value;
761 uint8_t DeltaT::calculateCheckSum(
const char *
cmd, uint32_t len)
764 for (uint32_t i = 1; i < len - 1; i++)
766 return (-sum & 0xFF);
void registerHandshake(std::function< bool()> callback)
registerHandshake Register a handshake function to be called once the intial connection to the device...
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
Initilize 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 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()
Initilize 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.
void setDriverInterface(uint16_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
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.
const char * IUFindOnSwitchName(ISState *states, char *names[], int n)
Returns the name of the first ON switch it finds in the supplied arguments.
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.
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,...)
auto get(const nlohmann::detail::iteration_proxy_value< IteratorType > &i) -> decltype(i.key())
NLOHMANN_BASIC_JSON_TPL_DECLARATION std::string to_string(const NLOHMANN_BASIC_JSON_TPL &j)
user-defined to_string function for JSON values
Number vector property descriptor.
Switch vector property descriptor.