/*
* Copyright (c) 2017 Shaun Feakes - All rights reserved
*
* You may use redistribute and/or modify this code under the terms of
* the GNU General Public License version 2 as published by the
* Free Software Foundation. For the terms of this license,
* see .
*
* You are free to use this software under the terms of the GNU General
* Public License, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* https://github.com/sfeakes/aqualinkd
*/
#include
#include
#include
#include
#include
#include
#include "aqualink.h"
#include "utils.h"
#include "aq_programmer.h"
#include "aq_serial.h"
#ifdef AQ_PDA
#include "pda.h"
#include "pda_menu.h"
#include "pda_aq_programmer.h"
#endif
#include "aq_panel.h"
#include "onetouch_aq_programmer.h"
#include "iaqtouch_aq_programmer.h"
#include "serialadapter.h"
#include "color_lights.h"
#include "config.h"
#include "devices_jandy.h"
#ifdef AQ_DEBUG
#include
#include "timespec_subtract.h"
#endif
bool select_sub_menu_item(struct aqualinkdata *aq_data, char* item_string);
bool select_menu_item(struct aqualinkdata *aq_data, char* item_string);
//void send_cmd(unsigned char cmd, struct aqualinkdata *aq_data);
void cancel_menu();
void *set_aqualink_pool_heater_temps( void *ptr );
void *set_aqualink_spa_heater_temps( void *ptr );
void *set_aqualink_freeze_heater_temps( void *ptr );
void *set_aqualink_time( void *ptr );
void *get_aqualink_pool_spa_heater_temps( void *ptr );
void *get_aqualink_programs( void *ptr );
void *get_freeze_protect_temp( void *ptr );
void *get_aqualink_diag_model( void *ptr );
void *get_aqualink_aux_labels( void *ptr );
//void *threadded_send_cmd( void *ptr );
void *set_aqualink_light_programmode( void *ptr );
void *set_aqualink_light_colormode( void *ptr );
void *set_aqualink_SWG( void *ptr );
void *set_aqualink_boost( void *ptr );
/*
void *set_aqualink_onetouch_pump_rpm( void *ptr );
void *set_aqualink_onetouch_macro( void *ptr );
void *get_aqualink_onetouch_setpoints( void *ptr );
*/
//void *get_aqualink_PDA_device_status( void *ptr );
//void *set_aqualink_PDA_device_on_off( void *ptr );
bool waitForButtonState(struct aqualinkdata *aq_data, aqkey* button, aqledstate state, int numMessageReceived);
//bool waitForMessage(struct aqualinkdata *aq_data, char* message, int numMessageReceived);
bool waitForEitherMessage(struct aqualinkdata *aq_data, char* message1, char* message2, int numMessageReceived);
bool push_aq_cmd(unsigned char cmd);
//void waitfor_queue2empty();
//void longwaitfor_queue2empty();
void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data, bool allowOveride);
#define MAX_STACK 20
int _stack_place = 0;
unsigned char _commands[MAX_STACK];
//unsigned char pgm_commands[MAX_STACK];
unsigned char _pgm_command = NUL;
//unsigned char _ot_pgm_command = NUL;
bool _last_sent_was_cmd = false;
// External view of adding to queue
void aq_send_cmd(unsigned char cmd) {
push_aq_cmd(cmd);
}
/*
void ot_send_cmd(unsigned char cmd) {
_ot_pgm_command = cmd;
}
unsigned char pop_ot_cmd(unsigned char receive_type)
{
unsigned char cmd = NUL;
if (receive_type == CMD_STATUS) {
cmd = _ot_pgm_command;
_ot_pgm_command = NUL;
}
return cmd;
}
*/
bool push_aq_cmd(unsigned char cmd) {
//LOG(PROG_LOG, LOG_NOTICE, "push_aq_cmd '0x%02hhx'\n", cmd);
if (_stack_place < MAX_STACK) {
_commands[_stack_place] = cmd;
_stack_place++;
} else {
LOG(PROG_LOG, LOG_ERR, "Command queue overflow, too many unsent commands to RS control panel\n");
return false;
}
return true;
}
int get_aq_cmd_length()
{
return _stack_place;
}
int _expectNextMessage = 0;
unsigned char _last_sent_command = NUL;
unsigned char pop_aq_cmd(struct aqualinkdata *aq_data)
{
unsigned char cmd = NUL;
if ( _expectNextMessage > 0 &&
(aq_data->last_packet_type == CMD_MSG || aq_data->last_packet_type == CMD_MSG_LONG || aq_data->last_packet_type == CMD_MSG_LOOP_ST))
{
_expectNextMessage=0;
} else if (_expectNextMessage > 3) {
// NSF Should probably check this is a status command AND we are in programming mode.
LOG(PROG_LOG, LOG_ERR, "Did not receive expected reply from last RS SEND command, resending '0x%02hhx'\n", _last_sent_command);
_expectNextMessage=0;
return _last_sent_command;
} else if (_expectNextMessage > 0) {
_expectNextMessage++;
}
// Only send commands on status messages
// Are we in programming mode and it's not ONETOUCH programming mode
if (in_programming_mode(aq_data) && ( in_ot_programming_mode(aq_data) == false && in_iaqt_programming_mode(aq_data) == false )) {
//if (aq_data->active_thread.thread_id != 0) {
if ( _pgm_command != NUL && aq_data->last_packet_type == CMD_STATUS) {
cmd = _pgm_command;
_pgm_command = NUL;
LOG(PROG_LOG, LOG_DEBUG_SERIAL, "RS SEND cmd '0x%02hhx' (programming)\n", cmd);
} else if (_pgm_command != NUL) {
LOG(PROG_LOG, LOG_DEBUG_SERIAL, "RS Waiting to send cmd '0x%02hhx' (programming)\n", _pgm_command);
} else {
LOG(PROG_LOG, LOG_DEBUG_SERIAL, "RS SEND cmd '0x%02hhx' empty queue (programming)\n", cmd);
}
} else if (_stack_place > 0 && aq_data->last_packet_type == CMD_STATUS ) {
cmd = _commands[0];
_stack_place--;
LOG(PROG_LOG, LOG_DEBUG_SERIAL, "RS SEND cmd '0x%02hhx'\n", cmd);
//LOG(PROG_LOG, LOG_NOTICE, "pop_cmd '0x%02hhx'\n", cmd);
memmove(&_commands[0], &_commands[1], sizeof(unsigned char) * _stack_place ) ;
} else {
LOG(PROG_LOG, LOG_DEBUG_SERIAL, "RS SEND cmd '0x%02hhx'\n", cmd);
}
//printf("RSM sending cmd '0x%02hhx' in reply to '0x%02hhx'\n",cmd,aq_data->last_packet_type);
if (cmd == KEY_ENTER || cmd == KEY_RIGHT || cmd == KEY_LEFT || cmd == KEY_MENU ) { //KEY_CANCEL KEY_HOLD KEY_OVERRIDE
_expectNextMessage=1;
_last_sent_command = cmd;
}
return cmd;
}
/*
unsigned char pop_aq_cmd_OLD(struct aqualinkdata *aq_data)
{
unsigned char cmd = NUL;
// Only send commands on status messages
// Are we in programming mode
if (aq_data->active_thread.thread_id != 0) {
if ( (_pgm_command == KEY_MENU && aq_data->last_packet_type == CMD_STATUS) ||
// Need to not the key_menu below
( _pgm_command != NUL && (aq_data->last_packet_type == CMD_STATUS || aq_data->last_packet_type == CMD_MSG_LONG) )) {
cmd = _pgm_command;
_pgm_command = NUL;
LOG(PROG_LOG, LOG_DEBUG, "RS SEND cmd '0x%02hhx' (programming)\n", cmd);
} else if (_pgm_command != NUL) {
LOG(PROG_LOG, LOG_DEBUG, "RS Waiting to send cmd '0x%02hhx' (programming)\n", _pgm_command);
} else {
LOG(PROG_LOG, LOG_DEBUG, "RS SEND cmd '0x%02hhx' empty queue (programming)\n", cmd);
}
} else if (_stack_place > 0 && aq_data->last_packet_type == CMD_STATUS ) {
cmd = _commands[0];
_stack_place--;
LOG(PROG_LOG, LOG_DEBUG, "RS SEND cmd '0x%02hhx'\n", cmd);
memmove(&_commands[0], &_commands[1], sizeof(unsigned char) * _stack_place ) ;
} else {
LOG(PROG_LOG, LOG_DEBUG, "RS SEND cmd '0x%02hhx'\n", cmd);
}
//printf("RSM sending cmd '0x%02hhx' in reply to '0x%02hhx'\n",cmd,aq_data->last_packet_type);
return cmd;
}
*/
int roundTo(int num, int denominator) {
return ((num + (denominator/2) ) / denominator )* denominator;
}
//(Intelliflo VF you set GPM, not RPM)
int RPM_check(pump_type type, int value, struct aqualinkdata *aqdata)
{
int rtn = value;
// RPM 3450 seems to be max
// RPM 600 min
// GPM 130 max
// GPM 15 min
if (type == VFPUMP) {
if (rtn > 130)
rtn = 130;
else if (rtn < 15)
rtn = 15;
else
rtn = roundTo(rtn, 5);
} else {
if (rtn > 3450)
rtn = 3450;
else if (rtn < 600)
rtn = 600;
else
rtn = roundTo(rtn, 5);
}
return rtn;
}
int setpoint_check(int type, int value, struct aqualinkdata *aqdata)
{
int rtn = value;
int max = 0;
int min = 0;
char *type_msg;
switch(type) {
case POOL_HTR_SETOINT:
type_msg = (isSINGLE_DEV_PANEL?"Temp1":"Pool");
if ( aqdata->temp_units == CELSIUS ) {
max = HEATER_MAX_C;
min = (isSINGLE_DEV_PANEL?HEATER_MIN_C+1:HEATER_MIN_C);
} else {
max = HEATER_MAX_F;
min = (isSINGLE_DEV_PANEL?HEATER_MIN_F+1:HEATER_MIN_F);
}
// if single device then TEMP1 & 2 (not pool & spa), TEMP1 must be set higher than TEMP2
if (isSINGLE_DEV_PANEL &&
aqdata->spa_htr_set_point != TEMP_UNKNOWN &&
min <= aqdata->spa_htr_set_point)
{
min = aqdata->spa_htr_set_point + 1;
}
break;
case SPA_HTR_SETOINT:
type_msg = (isSINGLE_DEV_PANEL?"Temp2":"Spa");
if ( aqdata->temp_units == CELSIUS ) {
max = (isSINGLE_DEV_PANEL?HEATER_MAX_C-1:HEATER_MAX_C);
min = HEATER_MIN_C;
} else {
max = (isSINGLE_DEV_PANEL?HEATER_MAX_F-1:HEATER_MAX_F);
min = HEATER_MIN_F;
}
// if single device then TEMP1 & 2 (not pool & spa), TEMP2 must be set lower than TEMP1
if (isSINGLE_DEV_PANEL &&
aqdata->pool_htr_set_point != TEMP_UNKNOWN &&
max >= aqdata->pool_htr_set_point)
{
max = aqdata->pool_htr_set_point - 1;
}
break;
case FREEZE_SETPOINT:
type_msg = "Freeze protect";
if ( aqdata->temp_units == CELSIUS ) {
max = FREEZE_PT_MAX_C;
min = FREEZE_PT_MIN_C;
} else {
max = FREEZE_PT_MAX_F;
min = FREEZE_PT_MIN_F;
}
break;
case SWG_SETPOINT:
type_msg = "Salt Water Generator";
max = SWG_PERCENT_MAX;
min = SWG_PERCENT_MIN;
break;
default:
type_msg = "Unknown";
break;
}
if (rtn > max)
rtn = max;
else if (rtn < min)
rtn = min;
// If SWG make sure it's 0,5,10,15,20......
if (type == SWG_SETPOINT) {
rtn = roundTo(rtn, 5);
}
if (rtn != value)
LOG(PROG_LOG, LOG_WARNING, "Setpoint of %d for %s is outside range, using %d\n",value,type_msg,rtn);
//else
// LOG(PROG_LOG, LOG_NOTICE, "Setting setpoint of %s to %d\n",type_msg,rtn);
return rtn;
}
/*
Figure out the fastest way in get all needed startup data depending on what protocols
are available and what's just called us
*/
void queueGetProgramData(emulation_type source_type, struct aqualinkdata *aq_data)
{
LOG(PROG_LOG, LOG_INFO, "Initial setup call from %s with RSSA=%s ONETouch=%s IAQTouch=%s ExtendedProgramming=%s\n",
(source_type == ALLBUTTON)?"AllButton":((source_type == RSSADAPTER)?"RSSA":((source_type == ONETOUCH)?"OneTouch":((source_type == IAQTOUCH)?"IAQTouch":"PDA"))),
(isRSSA_ENABLED)?"enabled":"disabled",
(isONET_ENABLED)?"enabled":"disabled",
(isIAQT_ENABLED)?"enabled":"disabled",
(isEXTP_ENABLED)?"enabled":"disabled"
);
if (isRSSA_ENABLED && isEXTP_ENABLED == true) {
// serial adapter enabled and extended programming
if (source_type == RSSADAPTER) {
_aq_programmer(AQ_GET_RSSADAPTER_SETPOINTS, NULL, aq_data, false);
} else if (source_type == ONETOUCH && isEXTP_ENABLED) {
//_aq_programmer(AQ_GET_ONETOUCH_FREEZEPROTECT, NULL, aq_data, false); // Add back and remove below once tested and working
//_aq_programmer(AQ_GET_ONETOUCH_SETPOINTS, NULL, aq_data, false);
} else if (source_type == IAQTOUCH && isEXTP_ENABLED) {
//_aq_programmer(AQ_GET_IAQTOUCH_FREEZEPROTECT, NULL, aq_data, false); // Add back and remove below once tested and working
//_aq_programmer(AQ_GET_IAQTOUCH_SETPOINTS, NULL, aq_data, false); // This get's freeze & heaters, we should just get freeze if isRSSA_ENABLED
} else if (source_type == ALLBUTTON) {
_aq_programmer(AQ_GET_FREEZE_PROTECT_TEMP, NULL, aq_data, false); // This is still quicker that IAQ or ONE Touch protocols at the moment.
if (_aqconfig_.use_panel_aux_labels) {
_aq_programmer(AQ_GET_AUX_LABELS, NULL, aq_data, false);
}
}
} else if (isRSSA_ENABLED && isEXTP_ENABLED == false) {
// serial adapter enabled with no extended programming
if (source_type == RSSADAPTER) {
_aq_programmer(AQ_GET_RSSADAPTER_SETPOINTS, NULL, aq_data, false);
} else if (source_type == ALLBUTTON) {
_aq_programmer(AQ_GET_FREEZE_PROTECT_TEMP, NULL, aq_data, false);
if (_aqconfig_.use_panel_aux_labels) {
_aq_programmer(AQ_GET_AUX_LABELS, NULL, aq_data, false);
}
}
} else if (!isRSSA_ENABLED && isEXTP_ENABLED && isONET_ENABLED) {
// One touch extended and no serial adapter
if (source_type == ONETOUCH) {
_aq_programmer(AQ_GET_ONETOUCH_SETPOINTS, NULL, aq_data, false);
} else if (source_type == ALLBUTTON) {
if (_aqconfig_.use_panel_aux_labels) {
_aq_programmer(AQ_GET_AUX_LABELS, NULL, aq_data, false);
}
}
} else if (!isRSSA_ENABLED && isEXTP_ENABLED && isIAQT_ENABLED) {
// IAQ touch extended and no serial adapter
if (source_type == IAQTOUCH) {
_aq_programmer(AQ_GET_IAQTOUCH_SETPOINTS, NULL, aq_data, false);
} else if (source_type == ALLBUTTON) {
if (_aqconfig_.use_panel_aux_labels) {
_aq_programmer(AQ_GET_AUX_LABELS, NULL, aq_data, false);
}
}
#ifdef AQ_PDA
} else if ( source_type == AQUAPDA) {
aq_programmer(AQ_PDA_INIT, NULL, aq_data);
#endif
} else { // Must be all button only
aq_programmer(AQ_GET_POOL_SPA_HEATER_TEMPS, NULL, aq_data);
aq_programmer(AQ_GET_FREEZE_PROTECT_TEMP, NULL, aq_data);
if (_aqconfig_.use_panel_aux_labels) {
aq_programmer(AQ_GET_AUX_LABELS, NULL, aq_data);
}
}
}
void queueGetProgramData_OLD(emulation_type source_type, struct aqualinkdata *aq_data)
{
// Wait for onetouch if enabeled.
//if ( source_type == ALLBUTTON && ( onetouch_enabled() == false || extended_device_id_programming() == false ) ) {
if ( source_type == RSSADAPTER ) {
aq_programmer(AQ_GET_RSSADAPTER_SETPOINTS, NULL, aq_data);
} else if ( source_type == ALLBUTTON && ( isEXTP_ENABLED == false || (isONET_ENABLED == false && isIAQT_ENABLED == false)) ) {
aq_send_cmd(NUL);
if (!isRSSA_ENABLED) {
aq_programmer(AQ_GET_POOL_SPA_HEATER_TEMPS, NULL, aq_data);
}
aq_programmer(AQ_GET_FREEZE_PROTECT_TEMP, NULL, aq_data);
if (_aqconfig_.use_panel_aux_labels) {
aq_programmer(AQ_GET_AUX_LABELS, NULL, aq_data);
}
#ifdef AQ_ONETOUCH
} else if ( source_type == ONETOUCH) {
if (!isRSSA_ENABLED) {
aq_programmer(AQ_GET_ONETOUCH_FREEZEPROTECT, NULL, aq_data);
} else {
aq_programmer(AQ_GET_ONETOUCH_SETPOINTS, NULL, aq_data); // This get's freeze & heaters, we should just get freeze if isRSSA_ENABLED
}
if (_aqconfig_.use_panel_aux_labels)
aq_programmer(AQ_GET_AUX_LABELS, NULL, aq_data);
#endif
#ifdef AQ_IAQTOUCH
} else if ( source_type == IAQTOUCH) {
//if (!isRSSA_ENABLED)
aq_programmer(AQ_GET_IAQTOUCH_SETPOINTS, NULL, aq_data); // This get's freeze & heaters, we should just get freeze if isRSSA_ENABLED
if (_aqconfig_.use_panel_aux_labels)
aq_programmer(AQ_GET_AUX_LABELS, NULL, aq_data);
//aq_programmer(AQ_GET_IAQTOUCH_AUX_LABELS, NULL, aq_data); // This is not working yet
#endif
#ifdef AQ_PDA
} else if ( source_type == AQUAPDA) {
aq_programmer(AQ_PDA_INIT, NULL, aq_data);
#endif
}
}
/*
void queueGetProgramData(emulation_type source_type, struct aqualinkdata *aq_data)
{
queueGetExtendedProgramData(source_type, aq_data, false);
}
*/
/*
void kick_aq_program_thread(struct aqualinkdata *aq_data)
{
if (aq_data->active_thread.thread_id != 0) {
LOG(PROG_LOG, LOG_DEBUG, "Kicking thread %d,%p message '%s'\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id,aq_data->last_message);
pthread_cond_broadcast(&aq_data->active_thread.thread_cond);
}
}
*/
bool in_light_programming_mode(struct aqualinkdata *aq_data)
{
if ( ( aq_data->active_thread.thread_id != 0 ) &&
( aq_data->active_thread.ptype == AQ_SET_LIGHTPROGRAM_MODE ||
aq_data->active_thread.ptype == AQ_SET_LIGHTCOLOR_MODE)
) {
return true;
}
return false;
}
bool in_swg_programming_mode(struct aqualinkdata *aq_data)
{
if ( ( aq_data->active_thread.thread_id != 0 ) &&
( aq_data->active_thread.ptype == AQ_SET_ONETOUCH_SWG_PERCENT ||
aq_data->active_thread.ptype == AQ_SET_IAQTOUCH_SWG_PERCENT ||
aq_data->active_thread.ptype == AQ_SET_SWG_PERCENT ||
aq_data->active_thread.ptype == AQ_SET_ONETOUCH_BOOST ||
aq_data->active_thread.ptype == AQ_SET_IAQTOUCH_SWG_BOOST ||
aq_data->active_thread.ptype == AQ_SET_BOOST)
) {
return true;
}
return false;
}
bool in_ot_programming_mode(struct aqualinkdata *aq_data)
{
//( type != AQ_SET_PUMP_RPM || type != AQ_SET_OT_MACRO )) {
if ( ( aq_data->active_thread.thread_id != 0 ) &&
( aq_data->active_thread.ptype == AQ_SET_ONETOUCH_PUMP_RPM ||
aq_data->active_thread.ptype == AQ_SET_ONETOUCH_MACRO ||
aq_data->active_thread.ptype == AQ_GET_ONETOUCH_SETPOINTS ||
aq_data->active_thread.ptype == AQ_SET_ONETOUCH_TIME ||
aq_data->active_thread.ptype == AQ_SET_ONETOUCH_SWG_PERCENT ||
aq_data->active_thread.ptype == AQ_SET_ONETOUCH_BOOST ||
aq_data->active_thread.ptype == AQ_SET_ONETOUCH_POOL_HEATER_TEMP ||
aq_data->active_thread.ptype == AQ_SET_ONETOUCH_SPA_HEATER_TEMP ||
aq_data->active_thread.ptype == AQ_SET_ONETOUCH_FREEZEPROTECT)
) {
return true;
}
return false;
}
bool in_iaqt_programming_mode(struct aqualinkdata *aq_data)
{
//( type != AQ_SET_PUMP_RPM || type != AQ_SET_OT_MACRO )) {
if ( ( aq_data->active_thread.thread_id != 0 ) &&
( aq_data->active_thread.ptype == AQ_SET_IAQTOUCH_PUMP_RPM ||
aq_data->active_thread.ptype == AQ_GET_IAQTOUCH_VSP_ASSIGNMENT ||
aq_data->active_thread.ptype == AQ_GET_IAQTOUCH_SETPOINTS ||
aq_data->active_thread.ptype == AQ_GET_IAQTOUCH_AUX_LABELS ||
aq_data->active_thread.ptype == AQ_SET_IAQTOUCH_SWG_PERCENT ||
aq_data->active_thread.ptype == AQ_SET_IAQTOUCH_SWG_BOOST ||
aq_data->active_thread.ptype == AQ_SET_IAQTOUCH_POOL_HEATER_TEMP ||
aq_data->active_thread.ptype == AQ_SET_IAQTOUCH_SPA_HEATER_TEMP ||
aq_data->active_thread.ptype == AQ_SET_IAQTOUCH_SET_TIME ||
aq_data->active_thread.ptype == AQ_SET_IAQTOUCH_PUMP_VS_PROGRAM)
) {
return true;
}
return false;
}
/*
Don't need this at the moment, it's kind-a inverse of above two functions
bool in_allb_programming_mode(struct aqualinkdata *aq_data)
{
if ( ( aq_data->active_thread.thread_id != 0 ) &&
( aq_data->active_thread.ptype == AQ_GET_POOL_SPA_HEATER_TEMPS ||
aq_data->active_thread.ptype == AQ_GET_FREEZE_PROTECT_TEMP ||
aq_data->active_thread.ptype == AQ_SET_TIME ||
aq_data->active_thread.ptype == AQ_SET_POOL_HEATER_TEMP ||
aq_data->active_thread.ptype == AQ_SET_SPA_HEATER_TEMP ||
aq_data->active_thread.ptype == AQ_SET_FRZ_PROTECTION_TEMP ||
aq_data->active_thread.ptype == AQ_GET_DIAGNOSTICS_MODEL ||
aq_data->active_thread.ptype == AQ_GET_PROGRAMS ||
aq_data->active_thread.ptype == AQ_SET_LIGHTPROGRAM_MODE ||
aq_data->active_thread.ptype == AQ_SET_LIGHTCOLOR_MODE ||
aq_data->active_thread.ptype == AQ_SET_SWG_PERCENT ||
aq_data->active_thread.ptype == AQ_GET_AUX_LABELS ||
aq_data->active_thread.ptype == AQ_SET_BOOST ||
aq_data->active_thread.ptype == AQ_SET_PUMP_RPM ||
aq_data->active_thread.ptype == AQ_SET_PUMP_VS_PROGRAM)
) {
return true;
}
return false;
}
*/
bool in_programming_mode(struct aqualinkdata *aq_data)
{
if ( aq_data->active_thread.thread_id != 0 ) {
return true;
}
return false;
}
void kick_aq_program_thread(struct aqualinkdata *aq_data, emulation_type source_type)
{
if ( aq_data->active_thread.thread_id != 0 ) {
if ( (source_type == ONETOUCH) && in_ot_programming_mode(aq_data))
{
LOG(ONET_LOG, LOG_DEBUG, "Kicking OneTouch thread %d,%p\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id);
pthread_cond_broadcast(&aq_data->active_thread.thread_cond);
}
else if (source_type == ALLBUTTON && !in_ot_programming_mode(aq_data)) {
LOG(PROG_LOG, LOG_DEBUG, "Kicking RS thread %d,%p message '%s'\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id,aq_data->last_message);
pthread_cond_broadcast(&aq_data->active_thread.thread_cond);
}
else if (source_type == IAQTOUCH && in_iaqt_programming_mode(aq_data)) {
LOG(IAQT_LOG, LOG_DEBUG, "Kicking IAQ Touch thread %d,%p\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id);
pthread_cond_broadcast(&aq_data->active_thread.thread_cond);
}
#ifdef AQ_PDA
else if (source_type == AQUAPDA && !in_ot_programming_mode(aq_data)) {
LOG(PDA_LOG, LOG_DEBUG, "Kicking PDA thread %d,%p\n",aq_data->active_thread.ptype, aq_data->active_thread.thread_id);
pthread_cond_broadcast(&aq_data->active_thread.thread_cond);
}
#endif
}
}
void aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data){
_aq_programmer(r_type, args, aq_data, true);
}
void _aq_programmer(program_type r_type, char *args, struct aqualinkdata *aq_data, bool allowOveride)
{
struct programmingThreadCtrl *programmingthread = malloc(sizeof(struct programmingThreadCtrl));
/*
Seemed like a good idea to use lookuptable, but need to figure out a way to check program_type actually exists in array,
//void* (*pf[])(void *ptr) = {get_freeze_protect_temp, get_freeze_protect_temp, get_freeze_protect_temp};
//static const void * (*fun_lookup[])(void *ptr) = {
static void * (*function_lookup[])(void *ptr) = {
[AQ_GET_POOL_SPA_HEATER_TEMPS] = get_aqualink_pool_spa_heater_temps,
[AQ_GET_FREEZE_PROTECT_TEMP] = get_freeze_protect_temp,
[AQ_SET_TIME] = set_aqualink_time,
[AQ_SET_POOL_HEATER_TEMP] = set_aqualink_pool_heater_temps,
[AQ_SET_SPA_HEATER_TEMP] = set_aqualink_spa_heater_temps,
[AQ_SET_FRZ_PROTECTION_TEMP] = set_aqualink_freeze_heater_temps,
[AQ_GET_DIAGNOSTICS_MODEL] = get_aqualink_diag_model,
[AQ_GET_PROGRAMS] = get_aqualink_programs,
[AQ_SET_LIGHTPROGRAM_MODE] = set_aqualink_light_programmode,
[AQ_SET_LIGHTCOLOR_MODE] = set_aqualink_light_colormode,
[AQ_SET_SWG_PERCENT] = set_aqualink_SWG,
[AQ_GET_AUX_LABELS] = get_aqualink_aux_labels,
[AQ_SET_BOOST] = set_aqualink_boost,
#ifdef AQ_PDA
[AQ_PDA_INIT] = set_aqualink_PDA_init,
[AQ_PDA_DEVICE_STATUS] = get_aqualink_PDA_device_status,
[AQ_PDA_DEVICE_ON_OFF] = set_aqualink_PDA_device_on_off,
[AQ_PDA_WAKE_INIT] = set_aqualink_PDA_wakeinit,
#endif
#ifdef AQ_ONETOUCH
//[AQ_SET_PUMP_RPM] = ,
//[AQ_SET_PUMP_VS_PROGRAM] = ,
//[AQ_SET_ONETOUCH_MACRO] = ,
[AQ_SET_ONETOUCH_PUMP_RPM] = set_aqualink_onetouch_pump_rpm,
[AQ_GET_ONETOUCH_SETPOINTS] = get_aqualink_onetouch_setpoints,
[AQ_SET_ONETOUCH_POOL_HEATER_TEMP] = set_aqualink_onetouch_pool_heater_temp,
[AQ_SET_ONETOUCH_SPA_HEATER_TEMP] = set_aqualink_onetouch_spa_heater_temp,
[AQ_SET_ONETOUCH_FREEZEPROTECT] = set_aqualink_onetouch_freezeprotect,
[AQ_SET_ONETOUCH_TIME] = set_aqualink_onetouch_time,
[AQ_SET_ONETOUCH_BOOST] = set_aqualink_onetouch_boost,
[AQ_SET_ONETOUCH_SWG_PERCENT] = set_aqualink_onetouch_swg_percent,
#endif
#ifdef AQ_IAQTOUCH
[AQ_SET_IAQTOUCH_PUMP_RPM] = set_aqualink_iaqtouch_pump_rpm,
[AQ_SET_IAQTOUCH_PUMP_VS_PROGRAM] = set_aqualink_iaqtouch_pump_vs_program,
[AQ_GET_IAQTOUCH_VSP_ASSIGNMENT] = set_aqualink_iaqtouch_vsp_assignments,
[AQ_GET_IAQTOUCH_SETPOINTS] = get_aqualink_iaqtouch_setpoints,
[AQ_GET_IAQTOUCH_AUX_LABELS] = get_aqualink_iaqtouch_aux_labels,
[AQ_SET_IAQTOUCH_SWG_PERCENT] = set_aqualink_iaqtouch_swg_percent,
[AQ_SET_IAQTOUCH_SWG_BOOST] = set_aqualink_iaqtouch_swg_boost,
[AQ_SET_IAQTOUCH_POOL_HEATER_TEMP] = set_aqualink_iaqtouch_pool_heater_temp,
[AQ_SET_IAQTOUCH_SPA_HEATER_TEMP] = set_aqualink_iaqtouch_spa_heater_temp,
[AQ_SET_IAQTOUCH_SET_TIME] = set_aqualink_iaqtouch_time,
#endif
[AQ_SET_BOOST] = set_aqualink_boost
};
*/
program_type type = r_type;
// RS SerialAdapter is quickest for changing thermostat temps, so use that if enabeled.
// VSP RPM can only be changed with oneTouch or iAquatouch so check / use those
// VSP Program is only available with iAquatouch, so check / use that.
if (isRSSA_ENABLED && (r_type == AQ_SET_POOL_HEATER_TEMP || r_type == AQ_SET_SPA_HEATER_TEMP)) {
if (r_type == AQ_SET_POOL_HEATER_TEMP)
type = AQ_SET_RSSADAPTER_POOL_HEATER_TEMP;
else if (r_type == AQ_SET_SPA_HEATER_TEMP)
type = AQ_SET_RSSADAPTER_SPA_HEATER_TEMP;
} else if (r_type == AQ_SET_PUMP_RPM) {
if (isONET_ENABLED)
type = AQ_SET_ONETOUCH_PUMP_RPM;
else if (isIAQT_ENABLED)
type = AQ_SET_IAQTOUCH_PUMP_RPM;
else {
LOG(PROG_LOG, LOG_ERR, "Can only change pump RPM with an extended device id\n",type);
return;
}
} else if (r_type == AQ_SET_PUMP_VS_PROGRAM) {
if (isIAQT_ENABLED)
type = AQ_SET_IAQTOUCH_PUMP_VS_PROGRAM;
else {
LOG(PROG_LOG, LOG_ERR, "Can only change pump VS Program with an iAqualink Touch device id\n",type);
return;
}
}
#ifdef AQ_ONETOUCH
// reset any types if to onetouch if available and if one touch is quicker
// At moment. onetouch is quicker for boost, and slower for heaters
else if (isONET_ENABLED && isEXTP_ENABLED) {
switch (r_type){
case AQ_GET_POOL_SPA_HEATER_TEMPS:
//case AQ_GET_FREEZE_PROTECT_TEMP:
type = AQ_GET_ONETOUCH_SETPOINTS;
break;
case AQ_SET_POOL_HEATER_TEMP:
type = AQ_SET_ONETOUCH_POOL_HEATER_TEMP;
break;
case AQ_SET_SPA_HEATER_TEMP:
type = AQ_SET_ONETOUCH_SPA_HEATER_TEMP;
break;
case AQ_SET_SWG_PERCENT:
type = AQ_SET_ONETOUCH_SWG_PERCENT;
break;
case AQ_SET_BOOST:
type = AQ_SET_ONETOUCH_BOOST;
break;
// NSF ONE TOUCH TIME IS NOT WORKING YET
//case AQ_SET_TIME:
// type = AQ_SET_ONETOUCH_TIME;
//break;
default:
type = r_type;
break;
}
}
#endif
#ifdef AQ_IAQTOUCH
else if (isIAQT_ENABLED && isEXTP_ENABLED) {
// IAQ Touch programming modes that should overite standard ones.
switch (r_type){
case AQ_GET_POOL_SPA_HEATER_TEMPS:
//case AQ_GET_FREEZE_PROTECT_TEMP:
type = AQ_GET_IAQTOUCH_SETPOINTS;
break;
case AQ_SET_SWG_PERCENT:
type = AQ_SET_IAQTOUCH_SWG_PERCENT;
break;
case AQ_SET_BOOST:
type = AQ_SET_IAQTOUCH_SWG_BOOST;
break;
case AQ_SET_POOL_HEATER_TEMP:
type = AQ_SET_IAQTOUCH_POOL_HEATER_TEMP;
break;
case AQ_SET_SPA_HEATER_TEMP:
type = AQ_SET_IAQTOUCH_SPA_HEATER_TEMP;
break;
case AQ_SET_TIME:
type = AQ_SET_IAQTOUCH_SET_TIME;
break;
default:
type = r_type;
break;
}
}
#endif
#ifdef AQ_PDA
// Check we are doing something valid request
if (isPDA_PANEL) {
pda_reset_sleep();
if (type != AQ_PDA_INIT &&
type != AQ_PDA_WAKE_INIT &&
type != AQ_PDA_DEVICE_STATUS &&
type != AQ_SET_POOL_HEATER_TEMP &&
type != AQ_SET_SPA_HEATER_TEMP &&
type != AQ_SET_SWG_PERCENT &&
type != AQ_PDA_DEVICE_ON_OFF &&
#ifdef BETA_PDA_AUTOLABEL
type != AQ_GET_AUX_LABELS &&
#endif
type != AQ_GET_POOL_SPA_HEATER_TEMPS &&
type != AQ_SET_FRZ_PROTECTION_TEMP &&
type != AQ_SET_BOOST &&
type != AQ_SET_TIME) {
LOG(PROG_LOG, LOG_ERR, "Selected Programming mode '%d' not supported with PDA mode control panel\n",type);
return;
}
}
#endif
if (!allowOveride) {
// Reset anything back from changes above.
type = r_type;
}
LOG(PROG_LOG, LOG_NOTICE, "Starting programming thread '%s'\n",ptypeName(type));
programmingthread->aq_data = aq_data;
programmingthread->thread_id = 0;
//programmingthread->thread_args = args;
if (args != NULL /*&& type != AQ_SEND_CMD*/)
strncpy(programmingthread->thread_args, args, sizeof(programmingthread->thread_args)-1);
switch(type) {
/*
case AQ_SEND_CMD:
LOG(PROG_LOG, LOG_INFO, "Queue send '0x%02hhx' to controller\n", &args[0]);
unsigned char cmd = (unsigned char) &args[0];
if (cmd == NUL) {
LOG(PROG_LOG, LOG_INFO, "Queue send '0x%02hhx' to controller (NEW)\n", cmd);
push_aq_cmd( cmd );
} else {
LOG(PROG_LOG, LOG_INFO, "Queue send '0x%02hhx' to controller (OLD)\n", cmd);
push_aq_cmd((unsigned char)*args);
}*/
//LOG(PROG_LOG, LOG_INFO, "Queue send '0x%02hhx' to controller\n", (unsigned char)*args);
/*
if(aq_data->active_thread.thread_id == 0) { // No need to thread a plane send if no active threads
send_cmd( (unsigned char)*args, aq_data);
} else if( pthread_create( &programmingthread->thread_id , NULL , threadded_send_cmd, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
*/
//break;
case AQ_GET_RSSADAPTER_SETPOINTS:
get_aqualink_rssadapter_setpoints();
return; // No need to create this as thread.
break;
case AQ_SET_RSSADAPTER_POOL_HEATER_TEMP:
set_aqualink_rssadapter_pool_setpoint(args, aq_data);
return; // No need to create this as thread.
break;
case AQ_SET_RSSADAPTER_SPA_HEATER_TEMP:
set_aqualink_rssadapter_spa_setpoint(args, aq_data);
return; // No need to create this as thread.
break;
case AQ_ADD_RSSADAPTER_POOL_HEATER_TEMP:
increase_aqualink_rssadapter_pool_setpoint(args, aq_data);
return; // No need to create this as thread.
break;
case AQ_ADD_RSSADAPTER_SPA_HEATER_TEMP:
increase_aqualink_rssadapter_spa_setpoint(args, aq_data);
return; // No need to create this as thread.
break;
case AQ_GET_POOL_SPA_HEATER_TEMPS:
if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_pool_spa_heater_temps, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_GET_FREEZE_PROTECT_TEMP:
if( pthread_create( &programmingthread->thread_id , NULL , get_freeze_protect_temp, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_TIME:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_time, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_POOL_HEATER_TEMP:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_pool_heater_temps, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_SPA_HEATER_TEMP:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_spa_heater_temps, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_FRZ_PROTECTION_TEMP:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_freeze_heater_temps, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_GET_DIAGNOSTICS_MODEL:
if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_diag_model, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_GET_PROGRAMS:
if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_programs, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_LIGHTPROGRAM_MODE:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_light_programmode, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_LIGHTCOLOR_MODE:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_light_colormode, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_SWG_PERCENT:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_SWG, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_GET_AUX_LABELS:
if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_aux_labels, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_BOOST:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_boost, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
#ifdef AQ_ONETOUCH
case AQ_SET_ONETOUCH_PUMP_RPM:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_onetouch_pump_rpm, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_GET_ONETOUCH_FREEZEPROTECT:
if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_onetouch_freezeprotect, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_GET_ONETOUCH_SETPOINTS:
if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_onetouch_setpoints, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_ONETOUCH_TIME:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_onetouch_time, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_ONETOUCH_BOOST:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_onetouch_boost, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_ONETOUCH_SWG_PERCENT:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_onetouch_swg_percent, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_ONETOUCH_POOL_HEATER_TEMP:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_onetouch_pool_heater_temp, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_ONETOUCH_SPA_HEATER_TEMP:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_onetouch_spa_heater_temp, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_ONETOUCH_FREEZEPROTECT:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_onetouch_freezeprotect, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
#endif
#ifdef AQ_IAQTOUCH
case AQ_SET_IAQTOUCH_PUMP_RPM:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_iaqtouch_pump_rpm, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_GET_IAQTOUCH_VSP_ASSIGNMENT:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_iaqtouch_vsp_assignments, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_GET_IAQTOUCH_SETPOINTS:
if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_iaqtouch_setpoints, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_GET_IAQTOUCH_FREEZEPROTECT:
if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_iaqtouch_freezeprotect, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_GET_IAQTOUCH_AUX_LABELS:
if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_iaqtouch_aux_labels, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_IAQTOUCH_SWG_PERCENT:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_iaqtouch_swg_percent, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_IAQTOUCH_SWG_BOOST:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_iaqtouch_swg_boost, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_IAQTOUCH_POOL_HEATER_TEMP:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_iaqtouch_pool_heater_temp, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_IAQTOUCH_SPA_HEATER_TEMP:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_iaqtouch_spa_heater_temp, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_IAQTOUCH_SET_TIME:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_iaqtouch_time, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_SET_IAQTOUCH_PUMP_VS_PROGRAM:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_iaqtouch_pump_vs_program, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
#endif
#ifdef AQ_PDA
case AQ_PDA_INIT:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_PDA_init, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_PDA_WAKE_INIT:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_PDA_wakeinit, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_PDA_DEVICE_STATUS:
if( pthread_create( &programmingthread->thread_id , NULL , get_aqualink_PDA_device_status, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
case AQ_PDA_DEVICE_ON_OFF:
if( pthread_create( &programmingthread->thread_id , NULL , set_aqualink_PDA_device_on_off, (void*)programmingthread) < 0) {
LOG(PROG_LOG, LOG_ERR, "could not create thread\n");
return;
}
break;
#endif
default:
LOG(PROG_LOG, LOG_ERR, "Didn't understand programming mode type\n");
break;
}
if ( programmingthread->thread_id != 0 ) {
//LOG(PROG_LOG, LOG_DEBUG, "********* DID pthread_detach %d\n",programmingthread->thread_id);
pthread_detach(programmingthread->thread_id);
} else {
//LOG(PROG_LOG, LOG_DEBUG, "********* DID NOT pthread_detach\n");
}
}
void waitForSingleThreadOrTerminate(struct programmingThreadCtrl *threadCtrl, program_type type)
{
//static int tries = 120;
int tries = 120;
static int waitTime = 1;
int i=0;
i = 0;
while (get_aq_cmd_length() > 0 && ( i++ <= tries) ) {
LOG(PROG_LOG, LOG_DEBUG, "Thread %p (%s) sleeping, waiting command queue to empty\n", &threadCtrl->thread_id, ptypeName(type));
sleep(waitTime);
}
if (i >= tries) {
LOG(PROG_LOG, LOG_ERR, "Thread %p (%s) timeout waiting, ending\n",&threadCtrl->thread_id,ptypeName(type));
free(threadCtrl);
pthread_exit(0);
}
while ( (threadCtrl->aq_data->active_thread.thread_id != 0) && ( i++ <= tries) ) {
//LOG(PROG_LOG, LOG_DEBUG, "Thread %d sleeping, waiting for thread %d to finish\n", threadCtrl->thread_id, threadCtrl->aq_data->active_thread.thread_id);
LOG(PROG_LOG, LOG_DEBUG, "Thread %p (%s) sleeping, waiting for thread %p (%s) to finish\n",
&threadCtrl->thread_id, ptypeName(type),
threadCtrl->aq_data->active_thread.thread_id, ptypeName(threadCtrl->aq_data->active_thread.ptype));
sleep(waitTime);
}
if (i >= tries) {
//LOG(PROG_LOG, LOG_ERR, "Thread %d timeout waiting, ending\n",threadCtrl->thread_id);
LOG(PROG_LOG, LOG_ERR, "Thread (%s) %p timeout waiting for thread (%s) %p to finish\n",
ptypeName(type), &threadCtrl->thread_id, ptypeName(threadCtrl->aq_data->active_thread.ptype),
threadCtrl->aq_data->active_thread.thread_id);
free(threadCtrl);
pthread_exit(0);
}
// Clear out any messages to the UI.
threadCtrl->aq_data->last_display_message[0] = '\0';
threadCtrl->aq_data->active_thread.thread_id = &threadCtrl->thread_id;
threadCtrl->aq_data->active_thread.ptype = type;
#ifdef AQ_DEBUG
clock_gettime(CLOCK_REALTIME, &threadCtrl->aq_data->start_active_time);
#endif
LOG(PROG_LOG, LOG_INFO, "Programming: %s, %d\n", ptypeName(threadCtrl->aq_data->active_thread.ptype), threadCtrl->aq_data->active_thread.ptype);
LOG(PROG_LOG, LOG_DEBUG, "Thread %d,%p is active (%s)\n",
threadCtrl->aq_data->active_thread.ptype,
threadCtrl->aq_data->active_thread.thread_id,
ptypeName(threadCtrl->aq_data->active_thread.ptype));
}
void cleanAndTerminateThread(struct programmingThreadCtrl *threadCtrl)
{
waitfor_queue2empty();
#ifndef AQ_DEBUG
LOG(PROG_LOG, LOG_DEBUG, "Thread %d,%p (%s) finished\n",threadCtrl->aq_data->active_thread.ptype, threadCtrl->thread_id,ptypeName(threadCtrl->aq_data->active_thread.ptype));
#else
struct timespec elapsed;
clock_gettime(CLOCK_REALTIME, &threadCtrl->aq_data->last_active_time);
timespec_subtract(&elapsed, &threadCtrl->aq_data->last_active_time, &threadCtrl->aq_data->start_active_time);
LOG(PROG_LOG, LOG_NOTICE, "Thread %d,%p (%s) finished in %d.%03ld sec\n",
threadCtrl->aq_data->active_thread.ptype,
threadCtrl->aq_data->active_thread.thread_id,
ptypeName(threadCtrl->aq_data->active_thread.ptype),
elapsed.tv_sec, elapsed.tv_nsec / 1000000L);
#endif
// Quick delay to allow for last message to be sent.
delay(500);
threadCtrl->aq_data->active_thread.thread_id = 0;
threadCtrl->aq_data->active_thread.ptype = AQP_NULL;
threadCtrl->thread_id = 0;
// Force update, change display message
threadCtrl->aq_data->updated = true;
free(threadCtrl);
pthread_exit(0);
}
bool setAqualinkNumericField_new(struct aqualinkdata *aq_data, char *value_label, int value, int increment);
bool setAqualinkNumericField(struct aqualinkdata *aq_data, char *value_label, int value)
{
return setAqualinkNumericField_new(aq_data, value_label, value, 1);
}
bool setAqualinkNumericField_new(struct aqualinkdata *aq_data, char *value_label, int value, int increment)
{
LOG(PROG_LOG, LOG_DEBUG,"Setting menu item '%s' to %d\n",value_label, value);
//char leading[10]; // description of the field (POOL, SPA, FRZ)
int current_val=-1; // integer value of the current set point
//char trailing[10]; // the degrees and scale
char searchBuf[20];
sprintf(searchBuf, "^%s", value_label);
int val_len = strlen(value_label);
int i=0;
do
{
if (waitForMessage(aq_data, searchBuf, 4) != true) {
LOG(PROG_LOG, LOG_WARNING, "AQ_Programmer Could not set numeric input '%s', not found\n",value_label);
cancel_menu();
return false;
}
//LOG(PROG_LOG, LOG_DEBUG,"WAITING for kick value=%d\n",current_val);
//sscanf(aq_data->last_message, "%s %d%s", leading, ¤t_val, trailing);
//sscanf(aq_data->last_message, "%*[^0123456789]%d", ¤t_val);
sscanf(&aq_data->last_message[val_len], "%*[^0123456789]%d", ¤t_val);
LOG(PROG_LOG, LOG_DEBUG, "%s set to %d, looking for %d\n",value_label,current_val,value);
if(value > current_val) {
// Increment the field.
sprintf(searchBuf, "%s %d", value_label, current_val+increment);
send_cmd(KEY_RIGHT);
}
else if(value < current_val) {
// Decrement the field.
sprintf(searchBuf, "%s %d", value_label, current_val-increment);
send_cmd(KEY_LEFT);
}
else {
// Just send ENTER. We are at the right value.
sprintf(searchBuf, "%s %d", value_label, current_val);
send_cmd(KEY_ENTER);
}
if (i++ >= 100) {
LOG(PROG_LOG, LOG_WARNING, "AQ_Programmer Could not set numeric input '%s', to '%d'\n",value_label,value);
send_cmd(KEY_ENTER);
break;
}
} while(value != current_val);
return true;
}
bool OLD_setAqualinkNumericField_OLD(struct aqualinkdata *aq_data, char *value_label, int value)
{ // Works for everything but not SWG
LOG(PROG_LOG, LOG_DEBUG,"Setting menu item '%s' to %d\n",value_label, value);
char leading[10]; // description of the field (POOL, SPA, FRZ)
int current_val; // integer value of the current set point
char trailing[10]; // the degrees and scale
char searchBuf[20];
sprintf(searchBuf, "^%s", value_label);
do
{
if (waitForMessage(aq_data, searchBuf, 3) != true) {
LOG(PROG_LOG, LOG_WARNING, "AQ_Programmer Could not set numeric input '%s', not found\n",value_label);
cancel_menu();
return false;
}
//LOG(PROG_LOG, LOG_DEBUG,"WAITING for kick value=%d\n",current_val);
sscanf(aq_data->last_message, "%s %d%s", leading, ¤t_val, trailing);
LOG(PROG_LOG, LOG_DEBUG, "%s set to %d, looking for %d\n",value_label,current_val,value);
if(value > current_val) {
// Increment the field.
sprintf(searchBuf, "%s %d", value_label, current_val+1);
send_cmd(KEY_RIGHT);
}
else if(value < current_val) {
// Decrement the field.
sprintf(searchBuf, "%s %d", value_label, current_val-1);
send_cmd(KEY_LEFT);
}
else {
// Just send ENTER. We are at the right value.
sprintf(searchBuf, "%s %d", value_label, current_val);
send_cmd(KEY_ENTER);
}
} while(value != current_val);
return true;
}
/*
void *threadded_send_cmd( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
waitForSingleThreadOrTerminate(threadCtrl, AQ_SEND_CMD);
send_cmd( (unsigned char)*threadCtrl->thread_args, aq_data);
cleanAndTerminateThread(threadCtrl);
return ptr;
}
*/
void *set_aqualink_boost( void *ptr )
{
struct programmingThreadCtrl *threadCtrl;
threadCtrl = (struct programmingThreadCtrl *) ptr;
struct aqualinkdata *aq_data = threadCtrl->aq_data;
waitForSingleThreadOrTerminate(threadCtrl, AQ_SET_BOOST);
/*
menu
BOOST POOL
PRESS ENTER* TO START BOOST POOL