diff --git a/Makefile b/Makefile index 1f9063c4..b6b5a140 100755 --- a/Makefile +++ b/Makefile @@ -14,7 +14,8 @@ AQ_PDA = true AQ_ONETOUCH = true AQ_IAQTOUCH = true AQ_MANAGER =true -AQ_RS_EXTRA_OPTS = false +#AQ_RS_EXTRA_OPTS = false +#AQ_CONTAINER = false // this is for compiling for containers #AQ_MEMCMP = true // Not implimented correctly yet. # Turn off threadded net services @@ -75,7 +76,7 @@ endif # Main source files SRCS = aqualinkd.c utils.c config.c aq_serial.c aq_panel.c aq_programmer.c net_services.c json_messages.c rs_msg_utils.c\ devices_jandy.c packetLogger.c devices_pentair.c color_lights.c serialadapter.c aq_timer.c aq_scheduler.c web_config.c\ - serial_logger.c mongoose.c timespec_subtract.c + serial_logger.c mongoose.c simulator.c timespec_subtract.c AQ_FLAGS = @@ -130,6 +131,7 @@ DBG_CFLAGS = $(DBGFLAGS) $(AQ_FLAGS) $(MGFLAGS) # Other sources. DBG_SRC = $(SRCS) debug_timer.c SL_SRC = serial_logger.c aq_serial.c utils.c packetLogger.c rs_msg_utils.c timespec_subtract.c +#MG_SRC = mongoose.c # Build durectories OBJ_DIR := ./build @@ -140,7 +142,7 @@ SL_OBJ_DIR := $(OBJ_DIR)/slog OBJ_FILES := $(patsubst %.c,$(OBJ_DIR)/%.o,$(SRCS)) DBG_OBJ_FILES := $(patsubst %.c,$(DBG_OBJ_DIR)/%.o,$(DBG_SRC)) SL_OBJ_FILES := $(patsubst %.c,$(SL_OBJ_DIR)/%.o,$(SL_SRC)) - +#MG_OBJ_FILES := $(patsubst %.c,$(OBJ_DIR)/%.o,$(MG_SRC)) # define the executable file MAIN = ./release/aqualinkd @@ -161,6 +163,12 @@ slog: $(SLOG) aqdebug: $(DEBG) $(info $(DEBG) has been compiled) +# Container, add container flag and compile +container: CFLAGS := $(CFLAGS) -D AQ_CONTAINER +container: $(MAIN) $(SLOG) + $(info $(MAIN) has been compiled (** For Container use **)) + $(info $(SLOG) has been compiled (** For Container use **)) + #debug, Just change compile flags and call MAIN debug: CFLAGS = $(DFLAGS) debug: $(MAIN) $(SLOG) diff --git a/README.md b/README.md index 4ae9f2aa..d982578a 100644 --- a/README.md +++ b/README.md @@ -56,9 +56,10 @@ https://github.com/sfeakes/AqualinkD/wiki/Jandy-Aqualink-RS485-protocol -### Simulator -Designed to mimic AqualinkRS6 All Button keypad and (like the keypad) is used to fully configure the master control panel
- +### Simulators +Designed to mimic AqualinkRS devices, used to fully configure the master control panel
+ + ### In Apple Home app. @@ -71,13 +72,26 @@ Designed to mimic AqualinkRS6 All Button keypad and (like the keypad) is used to ## All Web interfaces. * http://aqualink.ip/ <- (Standard WEB UI * http://aqualink.ip/simple.html <- (Simple opion if you don't like the above) -* http://aqualink.ip/simulator.html <- (RS8 All Button Control Panel simulator) -* http://aqualink.ip/debug.html <- (Turn on/off debug/serial debug & download logs) +* http://aqualink.ip/simulator.html <- (Displays all simulators in one page with tabs) +* http://aqualink.ip/aqmanager.html <- (Manage AqualinkD configuration & runtime) +* http://aqualink.ip/allbutton_sim.html <- (All Button Simulator) +* http://aqualink.ip/onetouch_sim.html <- (One Touch Simulator) +* http://aqualink.ip/aquapda_sim.html <- (PDA simulator) # # ToDo (future release) * Allow selecting of pre-defined VSP programs (Aqualink Touch & OneTouch protocols.) * Add set time to OneTouch protocol. -* Publish AqualinkD Management console. (Configure, Restart, run serial_logger) within AqualinkD. +* Update AqualinkD Management console to manage configuration +* Create iAqualink Touch Simulator + +# Update in Release 2.3.4 +* Changes for Docker +* Updated simulator code base and added new simulators for AllButton, OneTouch & PDA. + * /allbutton_sim.html + * /onetouch_sim.html + * /aquapda_sim.html + * On PDA only panel AqualinkD has to share the same ID with the PDA simulator. There for AqualinkD will not respond to commands while simulator is active. +* Now you can completley program the control panel with the simulators removing the need to have Jandy device. # Update in Release 2.3.3 * Introduced Aqualink Manager UI http://aqualink.ip/aqmanager.html diff --git a/aq_programmer.h b/aq_programmer.h index 39e6fdd6..b3e8a006 100644 --- a/aq_programmer.h +++ b/aq_programmer.h @@ -29,12 +29,14 @@ #define LIGHT_MODE_BUFER PTHREAD_ARG typedef enum emulation_type{ + SIM_NONE = -1, ALLBUTTON, RSSADAPTER, ONETOUCH, IAQTOUCH, AQUAPDA, // AQUAPALM and PDA are taken as specific type. - JANDY_DEVICE // Very rarley used. + JANDY_DEVICE, // Very rarley used. + SIMULATOR } emulation_type; typedef enum { diff --git a/aq_scheduler.c b/aq_scheduler.c index 98c2a886..6bf76dfe 100644 --- a/aq_scheduler.c +++ b/aq_scheduler.c @@ -38,7 +38,11 @@ Example /etc/cron.d/aqualinkd */ bool remount_root_ro(bool readonly) { - // NSF Check if config is RO_ROOT set + +#ifdef AQ_CONTAINER + // In container this is pointless + return false; +#endif if (readonly) { LOG(SCHD_LOG,LOG_INFO, "reMounting root RO\n"); diff --git a/aq_serial.c b/aq_serial.c index cb28600a..652a6e03 100644 --- a/aq_serial.c +++ b/aq_serial.c @@ -336,30 +336,15 @@ int _init_serial_port(const char* tty, bool blocking, bool readahead); int init_serial_port(const char* tty) { #ifdef AQ_NO_THREAD_NETSERVICE - if (_aqconfig_.rs_poll_speed < 0) - return init_blocking_serial_port(_aqconfig_.serial_port); - else if (_aqconfig_.readahead_b4_write) - return init_readahead_serial_port(_aqconfig_.serial_port); - else - return init_serial_port(_aqconfig_.serial_port); -#elif AQ_RS_EXTRA_OPTS - if (_aqconfig_.readahead_b4_write) - return init_readahead_serial_port(_aqconfig_.serial_port); - else + if (_aqconfig_.rs_poll_speed < 0) { return init_blocking_serial_port(_aqconfig_.serial_port); + } #else return init_blocking_serial_port(_aqconfig_.serial_port); #endif } -#ifdef AQ_RS_EXTRA_OPTS -int init_readahead_serial_port(const char* tty) -{ - return _init_serial_port(tty, false, true); -} -#endif - int init_blocking_serial_port(const char* tty) { _blocking_fds = _init_serial_port(tty, true, false); @@ -767,14 +752,6 @@ void send_packet(int fd, unsigned char *packet, int length) if (nwrite != length) LOG(RSSD_LOG, LOG_ERR, "write to serial port failed\n"); } else { -#ifdef AQ_RS_EXTRA_OPTS - if (_aqconfig_.readahead_b4_write) { - if (cleanOutSerial(fd, false) != 0x00) { - LOG(RSSD_LOG, LOG_ERR, "ERROR on RS485, AqualinkD was too slow in replying to message! (please check for performance issues)\n"); - cleanOutSerial(fd, true); - } - } -#endif int nwrite, i; for (i = 0; i < length; i += nwrite) { nwrite = write(fd, packet + i, length - i); @@ -1026,25 +1003,8 @@ int get_packet(int fd, unsigned char* packet) // Clean out rest of buffer, make sure their is nothing else /* Doesn't work for shit due to probe message speed, need to come back and re-think - if (_aqconfig_.readahead_b4_write) { - if (endOfPacket == true) { - do { - bytesRead = read(fd, &byte, 1); - //if (bytesRead==1) { LOG(RSSD_LOG,LOG_ERR, "Cleanout buffer read 0x%02hhx\n",byte); } - if (bytesRead==1 && byte != 0x00) { - LOG(RSSD_LOG,LOG_ERR, "SERIOUS ERROR on RS485, AqualinkD caught packet collision on bus, ignoring!\n"); - LOG(RSSD_LOG,LOG_ERR, "Error Cleanout read 0x%02hhx\n",byte); - // Run the buffer out - do { - bytesRead = read(fd, &byte, 1); - if (bytesRead==1) { LOG(RSSD_LOG,LOG_ERR, "Error Cleanout read 0x%02hhx\n",byte); } - } while (bytesRead==1); - return 0; - } - } while (bytesRead==1); - } - } - */ +*/ + //LOG(RSSD_LOG,LOG_DEBUG, "Serial checksum, length %d got 0x%02hhx expected 0x%02hhx\n", index, packet[index-3], generate_checksum(packet, index)); if (jandyPacketStarted) { if (check_jandy_checksum(packet, index) != true){ diff --git a/aq_serial.h b/aq_serial.h index d3176895..2b9d5c7e 100644 --- a/aq_serial.h +++ b/aq_serial.h @@ -458,7 +458,7 @@ typedef enum { int init_serial_port(const char* tty); int init_blocking_serial_port(const char* tty); -int init_readahead_serial_port(const char* tty); +//int init_readahead_serial_port(const char* tty); void close_serial_port(int file_descriptor); void close_blocking_serial_port(); diff --git a/aqualink.h b/aqualink.h index fe5bf3c4..d3dd3833 100644 --- a/aqualink.h +++ b/aqualink.h @@ -138,6 +138,16 @@ typedef enum pump_type { } pump_type; */ +/* +typedef enum simulator_type { + SIM_NONE, + SIM_ALLB, + SIM_ONET, + SIM_PDA, + SIM_IAQT +} simulator_type; +*/ + #define PUMP_PRIMING -1 #define PUMP_OFFLINE -2 #define PUMP_ERROR -3 @@ -227,7 +237,16 @@ struct aqualinkdata //unsigned short total_ordered_buttons; unsigned char last_packet_type; int swg_delayed_percent; - bool simulate_panel; + //bool simulate_panel; // NSF remove in future + unsigned char simulator_packet[AQ_MAXPKTLEN+1]; + bool simulator_packet_updated; + int simulator_packet_length; + + //bool simulator_active; // should be redundant with other two + unsigned char simulator_id; + //simulator_type simulator_active; + emulation_type simulator_active; + bool aqManagerActive; int open_websockets; struct programmingthread active_thread; diff --git a/aqualinkd.c b/aqualinkd.c index b04d57b5..65665e18 100644 --- a/aqualinkd.c +++ b/aqualinkd.c @@ -50,6 +50,7 @@ #include "version.h" #include "rs_msg_utils.h" #include "serialadapter.h" +#include "simulator.h" #include "debug_timer.h" #ifdef AQ_MANAGER @@ -743,7 +744,8 @@ void _processMessage(char *message, bool reset) { LOG(AQRS_LOG,LOG_DEBUG_SERIAL, "Ignoring '%s'\n", msg); //_aqualink_data.display_message = msg; - if (in_programming_mode(&_aqualink_data) == false && _aqualink_data.simulate_panel == false && + //if (in_programming_mode(&_aqualink_data) == false && _aqualink_data.simulate_panel == false && + if (in_programming_mode(&_aqualink_data) == false && stristr(msg, "JANDY AquaLinkRS") == NULL && //stristr(msg, "PUMP O") == NULL &&// Catch 'PUMP ON' and 'PUMP OFF' but not 'PUMP WILL TURN ON' strncasecmp(msg, "PUMP O", 6) != 0 &&// Catch 'PUMP ON' and 'PUMP OFF' but not 'PUMP WILL TURN ON' @@ -1059,7 +1061,6 @@ int main(int argc, char *argv[]) char defaultCfg[] = "./aqualinkd.conf"; char *cfgFile; - //printf ("TIMER = %d\n",TIMR_LOG); #ifdef AQ_MEMCMP @@ -1137,7 +1138,7 @@ int main(int argc, char *argv[]) else if (strcmp(argv[i], "-rsrd") == 0) { _cmdln_lograwRS485 = true; - } + } } // Set this here, so it doesn;t get reset if the manager restarts the AqualinkD process. @@ -1316,14 +1317,6 @@ int startup(char *self, char *cfgFile) if (READ_RSDEV_SWG && _aqconfig_.swg_zero_ignore != DEFAULT_SWG_ZERO_IGNORE_COUNT) LOG(AQUA_LOG,LOG_NOTICE, "Ignore SWG 0 msg count = %d\n", _aqconfig_.swg_zero_ignore); - -#ifdef AQ_RS_EXTRA_OPTS - if (_aqconfig_.readahead_b4_write == true) - LOG(AQUA_LOG,LOG_NOTICE, "Serial Read Ahead Write = %s\n", bool2text(_aqconfig_.readahead_b4_write)); - if (_aqconfig_.prioritize_ack == true) - LOG(AQUA_LOG,LOG_NOTICE, "Serial Prioritize Ack = %s\n", bool2text(_aqconfig_.prioritize_ack)); -#endif - if (_aqconfig_.ftdi_low_latency == true) LOG(AQUA_LOG,LOG_NOTICE, "Serial FTDI low latency = %s\n", bool2text(_aqconfig_.ftdi_low_latency)); @@ -1342,13 +1335,6 @@ int startup(char *self, char *cfgFile) LOG(AQUA_LOG,LOG_NOTICE, "RS Poll Speed = %d\n", _aqconfig_.rs_poll_speed); #endif -#if defined AQ_RS_EXTRA_OPTS && defined AQ_NO_THREAD_NETSERVICE - if (_aqconfig_.rs_poll_speed < 0 && _aqconfig_.readahead_b4_write) { - LOG(AQUA_LOG,LOG_WARNING, "Serial Read Ahead Write is not valid when using Negative RS Poll Speed, turning Serial Read Ahead Write off\n"); - _aqconfig_.readahead_b4_write = false; - } -#endif - //for (i = 0; i < TOTAL_BUTONS; i++) for (i = 0; i < _aqualink_data.total_buttons; i++) { @@ -1451,6 +1437,20 @@ void caculate_ack_packet(int rs_fd, unsigned char *packet_buffer, emulation_type //DEBUG_TIMER_STOP(_rs_packet_timer,AQUA_LOG,"PDA Emulation type Processed packet in"); break; #endif + case SIMULATOR: + if (_aqualink_data.simulator_active == ALLBUTTON) { + send_extended_ack(rs_fd, (packet_buffer[PKT_CMD]==CMD_MSG_LONG?ACK_SCREEN_BUSY_SCROLL:ACK_NORMAL), pop_simulator_cmd(packet_buffer[PKT_CMD])); + } else if (_aqualink_data.simulator_active == ONETOUCH) { + send_extended_ack(rs_fd, ACK_ONETOUCH, pop_simulator_cmd(packet_buffer[PKT_CMD])); + } else if (_aqualink_data.simulator_active == IAQTOUCH) { + LOG(SIM_LOG,LOG_WARNING, "IAQTOUCH not implimented yet!\n"); + } else if (_aqualink_data.simulator_active == AQUAPDA) { + send_extended_ack(rs_fd, ACK_PDA, pop_simulator_cmd(packet_buffer[PKT_CMD])); + } else { + LOG(SIM_LOG,LOG_ERR, "No idea on this protocol (%d), not implimented!!!\n",_aqualink_data.simulator_active); + } + break; + default: LOG(AQUA_LOG,LOG_WARNING, "Can't caculate ACK, No idea what packet this source packet was for!\n"); //DEBUG_TIMER_STOP(_rs_packet_timer,AQUA_LOG,"Unknown Emulation type Processed packet in"); @@ -1507,7 +1507,7 @@ void main_loop() int blank_read_reconnect = MAX_ZERO_READ_BEFORE_RECONNECT_BLOCKING; // Will get reset if non blocking sprintf(_aqualink_data.last_display_message, "%s", "Connecting to Control Panel"); - _aqualink_data.simulate_panel = false; + //_aqualink_data.simulate_panel = false; _aqualink_data.active_thread.thread_id = 0; _aqualink_data.air_temp = TEMP_UNKNOWN; _aqualink_data.pool_temp = TEMP_UNKNOWN; @@ -1528,6 +1528,8 @@ void main_loop() _aqualink_data.open_websockets = 0; _aqualink_data.ph = TEMP_UNKNOWN; _aqualink_data.orp = TEMP_UNKNOWN; + _aqualink_data.simulator_id = NUL; + _aqualink_data.simulator_active = SIM_NONE; pthread_mutex_init(&_aqualink_data.active_thread.thread_mutex, NULL); pthread_cond_init(&_aqualink_data.active_thread.thread_cond, NULL); @@ -1563,8 +1565,10 @@ void main_loop() rs_fd = init_serial_port(_aqconfig_.serial_port); if (rs_fd == -1) { - LOG(AQUA_LOG,LOG_ERR, "Error Aqualink setting serial port: %s\n", _aqconfig_.serial_port); - exit(EXIT_FAILURE); + LOG(AQUA_LOG,LOG_ERR, "Error Aqualink setting serial port: %s\n", _aqconfig_.serial_port); +#ifndef AQ_CONTAINER + exit(EXIT_FAILURE); +#endif } LOG(AQUA_LOG,LOG_NOTICE, "Listening to Aqualink RS8 on serial port: %s\n", _aqconfig_.serial_port); @@ -1634,16 +1638,15 @@ void main_loop() LOG(AQUA_LOG,LOG_NOTICE, "Waiting for Control Panel probe\n"); i=0; - // Turn off read ahead while dealing with probes -#ifdef AQ_RS_EXTRA_OPTS - bool read_ahead = _aqconfig_.readahead_b4_write; - _aqconfig_.readahead_b4_write = false; -#endif // Loop until we get the probe messages, that means we didn;t start too soon after last shutdown. while ( (got_probe == false || got_probe_rssa == false || got_probe_extended == false ) && _keepRunning == true) { if (blank_read == blank_read_reconnect) { LOG(AQUA_LOG,LOG_ERR, "Nothing read on '%s', are you sure that's right?\n",_aqconfig_.serial_port); +#ifdef AQ_CONTAINER + // Reset blank reads here, we want to ignore TTY errors in container to keep it running + blank_read = 1; +#endif } else if (blank_read == blank_read_reconnect*2) { LOG(AQUA_LOG,LOG_ERR, "I'm done, exiting, please check '%s'\n",_aqconfig_.serial_port); stopPacketLogger(); @@ -1749,11 +1752,6 @@ void main_loop() * */ -//int max_blank_read = 0; -#ifdef AQ_RS_EXTRA_OPTS - _aqconfig_.readahead_b4_write = read_ahead; -#endif - LOG(AQUA_LOG,LOG_NOTICE, "Starting communication with Control Panel\n"); // Not the best way to do this, but ok for moment @@ -1819,7 +1817,6 @@ void main_loop() #ifdef AQ_NO_THREAD_NETSERVICE if (_aqconfig_.rs_poll_speed < 0) { #else - //if (!_aqconfig_.readahead_b4_write) { if (serial_blockingmode() && (packet_length == AQSERR_READ || packet_length == AQSERR_TIMEOUT) ) { #endif LOG(AQUA_LOG,LOG_ERR, "Nothing read on blocking serial port\n"); @@ -1843,80 +1840,73 @@ void main_loop() blank_read = 0; //changed = false; + if (_aqualink_data.simulator_active != SIM_NONE) { + // Check if we have a valid connection + if ( _aqualink_data.simulator_id != NUL && packet_buffer[PKT_DEST] == _aqualink_data.simulator_id) { + // Action comand and Send to web + processSimulatorPacket(packet_buffer, packet_length, &_aqualink_data); + caculate_ack_packet(rs_fd, packet_buffer, SIMULATOR); + DEBUG_TIMER_STOP(_rs_packet_timer,AQUA_LOG,"Simulator Emulation Processed packet in"); + } + else if ( _aqualink_data.simulator_id == NUL + && packet_buffer[PKT_CMD] == CMD_PROBE + && packet_buffer[PKT_DEST] != _aqconfig_.device_id // Check no conflicting id's +#if defined AQ_ONETOUCH || defined AQ_IAQTOUCH + && packet_buffer[PKT_DEST] != _aqconfig_.extended_device_id // Check no conflicting id's +#endif + ) + { + if (is_simulator_packet(&_aqualink_data, packet_buffer, packet_length)) { + _aqualink_data.simulator_id = packet_buffer[PKT_DEST]; + // reply to probe + LOG(SIM_LOG,LOG_NOTICE, "Got probe on '0x%02hhx', using for simulator ID\n",packet_buffer[PKT_DEST]); + processSimulatorPacket(packet_buffer, packet_length, &_aqualink_data); + caculate_ack_packet(rs_fd, packet_buffer, SIMULATOR); + } else { + LOG(SIM_LOG,LOG_INFO, "Got probe on '0x%02hhx' Still waiting for valid simulator probe\n",packet_buffer[PKT_DEST]); + } + DEBUG_TIMER_STOP(_rs_packet_timer,AQUA_LOG,"Simulator Emulation Processed packet in"); + } + } + + if (packet_length > 0 && packet_buffer[PKT_DEST] == _aqconfig_.device_id && getProtocolType(packet_buffer) == JANDY) { if (getLogLevel(AQUA_LOG) >= LOG_DEBUG) { LOG(AQUA_LOG,LOG_DEBUG, "RS received packet of type %s length %d\n", get_packet_type(packet_buffer, packet_length), packet_length); logPacketRead(packet_buffer, packet_length); } -#ifdef AQ_RS_EXTRA_OPTS - // If we did not process the packet, above we need to record it for the caculate_ack_packet call. - // Should find a better place to put this, but since prioritize_ack is expermental it's ok for now. - // NSF We shouldn;t need to do the same for rssa / onetouch / iaqtouch and probably rssaadapter since they don;t queue commands & programming commands. - if (_aqconfig_.prioritize_ack) { - _aqualink_data.last_packet_type = packet_buffer[PKT_CMD]; - else -#endif - _aqualink_data.updated = process_packet(packet_buffer, packet_length); + _aqualink_data.updated = process_packet(packet_buffer, packet_length); #ifdef AQ_PDA - if (isPDA_PANEL) - caculate_ack_packet(rs_fd, packet_buffer, AQUAPDA); + if (isPDA_PANEL) { + // If we are in simulator mode, the sim has already send the ack + if (_aqualink_data.simulator_active == SIM_NONE) { + caculate_ack_packet(rs_fd, packet_buffer, AQUAPDA); + } + } else #endif caculate_ack_packet(rs_fd, packet_buffer, ALLBUTTON); -#ifdef AQ_RS_EXTRA_OPTS - if (_aqconfig_.prioritize_ack) - _aqualink_data.updated = process_packet(packet_buffer, packet_length); -#endif DEBUG_TIMER_STOP(_rs_packet_timer,AQUA_LOG,"AllButton Emulation Processed packet in"); } else if (packet_length > 0 && isRSSA_ENABLED && packet_buffer[PKT_DEST] == _aqconfig_.rssa_device_id && getProtocolType(packet_buffer) == JANDY) { -#ifdef AQ_RS_EXTRA_OPTS - if (_aqconfig_.prioritize_ack) { - caculate_ack_packet(rs_fd, packet_buffer, RSSADAPTER); - _aqualink_data.updated = process_rssadapter_packet(packet_buffer, packet_length, &_aqualink_data); - } - else -#endif - { - _aqualink_data.updated = process_rssadapter_packet(packet_buffer, packet_length, &_aqualink_data); - caculate_ack_packet(rs_fd, packet_buffer, RSSADAPTER); - } + _aqualink_data.updated = process_rssadapter_packet(packet_buffer, packet_length, &_aqualink_data); + caculate_ack_packet(rs_fd, packet_buffer, RSSADAPTER); DEBUG_TIMER_STOP(_rs_packet_timer,AQUA_LOG,"SerialAdapter Emulation Processed packet in"); } #ifdef AQ_ONETOUCH else if (packet_length > 0 && isONET_ENABLED && packet_buffer[PKT_DEST] == _aqconfig_.extended_device_id && getProtocolType(packet_buffer) == JANDY) { - #ifdef AQ_RS_EXTRA_OPTS - if (_aqconfig_.prioritize_ack) { - set_onetouch_lastmsg(packet_buffer[PKT_CMD]); - caculate_ack_packet(rs_fd, packet_buffer, ONETOUCH); - _aqualink_data.updated = process_onetouch_packet(packet_buffer, packet_length, &_aqualink_data); - } - else - #endif - { - _aqualink_data.updated = process_onetouch_packet(packet_buffer, packet_length, &_aqualink_data); - caculate_ack_packet(rs_fd, packet_buffer, ONETOUCH); - } + _aqualink_data.updated = process_onetouch_packet(packet_buffer, packet_length, &_aqualink_data); + caculate_ack_packet(rs_fd, packet_buffer, ONETOUCH); DEBUG_TIMER_STOP(_rs_packet_timer,AQUA_LOG,"OneTouch Emulation Processed packet in"); } #endif #ifdef AQ_IAQTOUCH else if (packet_length > 0 && isIAQT_ENABLED && packet_buffer[PKT_DEST] == _aqconfig_.extended_device_id && getProtocolType(packet_buffer) == JANDY) { - #ifdef AQ_RS_EXTRA_OPTS - if (_aqconfig_.prioritize_ack) { - set_iaqtouch_lastmsg(packet_buffer[PKT_CMD]); - caculate_ack_packet(rs_fd, packet_buffer, IAQTOUCH); - _aqualink_data.updated = process_iaqtouch_packet(packet_buffer, packet_length, &_aqualink_data); - } - else - #endif - { - _aqualink_data.updated = process_iaqtouch_packet(packet_buffer, packet_length, &_aqualink_data); - caculate_ack_packet(rs_fd, packet_buffer, IAQTOUCH); - } + _aqualink_data.updated = process_iaqtouch_packet(packet_buffer, packet_length, &_aqualink_data); + caculate_ack_packet(rs_fd, packet_buffer, IAQTOUCH); DEBUG_TIMER_STOP(_rs_packet_timer,AQUA_LOG,"AquaTouch Emulation Processed packet in"); } #endif diff --git a/config.c b/config.c index 7c4d52de..54c6c9b7 100644 --- a/config.c +++ b/config.c @@ -138,13 +138,6 @@ void init_parameters (struct aqconfig * parms) parms->sync_panel_time = true; - -#ifdef AQ_RS_EXTRA_OPTS - // Default parameters for threading and USB blocking - parms->readahead_b4_write = false; - parms->prioritize_ack = false; -#endif - #ifdef AQ_NO_THREAD_NETSERVICE parms->rs_poll_speed = DEFAULT_POLL_SPEED; parms->thread_netservices = true; @@ -153,7 +146,7 @@ void init_parameters (struct aqconfig * parms) parms->enable_scheduler = true; parms->ftdi_low_latency = true; parms->frame_delay = 0; - + generate_mqtt_id(parms->mqtt_ID, MQTT_ID_LEN); } @@ -589,14 +582,6 @@ bool setConfigValue(struct aqualinkdata *aqdata, char *param, char *value) { } else if (strncasecmp (param, "display_warnings_in_web", 23) == 0) { _aqconfig_.display_warnings_web = text2bool(value); rtn=true; -#ifdef AQ_RS_EXTRA_OPTS - } else if (strncasecmp (param, "serial_readahead_b4_write", 25) == 0) { - _aqconfig_.readahead_b4_write = text2bool(value); - rtn=true; - } else if (strncasecmp (param, "prioritize_ack", 14) == 0) { - _aqconfig_.prioritize_ack = text2bool(value); - rtn=true; -#endif } else if (strncasecmp (param, "mqtt_timed_update", 17) == 0) { _aqconfig_.mqtt_timed_update = text2bool(value); rtn=true; diff --git a/config.h b/config.h index 8faa770c..c28cb6ec 100644 --- a/config.h +++ b/config.h @@ -54,7 +54,7 @@ struct aqconfig char *mqtt_server; char *mqtt_user; char *mqtt_passwd; - char mqtt_ID[MQTT_ID_LEN]; + char mqtt_ID[MQTT_ID_LEN+1]; int dzidx_air_temp; int dzidx_pool_water_temp; int dzidx_spa_water_temp; @@ -85,10 +85,12 @@ struct aqconfig bool log_raw_bytes; // Read as bytes unsigned char RSSD_LOG_filter; //bool log_raw_RS_bytes; + /* #ifdef AQ_RS_EXTRA_OPTS bool readahead_b4_write; bool prioritize_ack; #endif +*/ bool mqtt_timed_update; bool sync_panel_time; bool enable_scheduler; diff --git a/extras/allbutton_sim.png b/extras/allbutton_sim.png new file mode 100644 index 00000000..6b2df739 Binary files /dev/null and b/extras/allbutton_sim.png differ diff --git a/extras/onetouch_sim.png b/extras/onetouch_sim.png new file mode 100644 index 00000000..d5be9b55 Binary files /dev/null and b/extras/onetouch_sim.png differ diff --git a/json_messages.c b/json_messages.c index 7c75db28..5d0940a9 100644 --- a/json_messages.c +++ b/json_messages.c @@ -32,6 +32,7 @@ #include "version.h" #include "aq_timer.h" #include "aq_programmer.h" +#include "rs_msg_utils.h" //#define test_message "{\"type\": \"status\",\"version\": \"8157 REV MMM\",\"date\": \"09/01/16 THU\",\"time\": \"1:16 PM\",\"temp_units\": \"F\",\"air_temp\": \"96\",\"pool_temp\": \"86\",\"spa_temp\": \" \",\"battery\": \"ok\",\"pool_htr_set_pnt\": \"85\",\"spa_htr_set_pnt\": \"99\",\"freeze_protection\": \"off\",\"frz_protect_set_pnt\": \"0\",\"leds\": {\"pump\": \"on\",\"spa\": \"off\",\"aux1\": \"off\",\"aux2\": \"off\",\"aux3\": \"off\",\"aux4\": \"off\",\"aux5\": \"off\",\"aux6\": \"off\",\"aux7\": \"off\",\"pool_heater\": \"off\",\"spa_heater\": \"off\",\"solar_heater\": \"off\"}}" //#define test_labels "{\"type\": \"aux_labels\",\"aux1_label\": \"Cleaner\",\"aux2_label\": \"Waterfall\",\"aux3_label\": \"Spa Blower\",\"aux4_label\": \"Pool Light\",\"aux5_label\": \"Spa Light\",\"aux6_label\": \"Unassigned\",\"aux7_label\": \"Unassigned\"}" @@ -91,10 +92,12 @@ int build_logmsg_JSON(char *dest, int loglevel, const char *src, int dest_len, i const char* _getStatus(struct aqualinkdata *aqdata, const char *blankmsg) { + /* if (aqdata->active_thread.thread_id != 0 && !aqdata->simulate_panel) { //return JSON_PROGRAMMING; return programtypeDisplayName(aqdata->active_thread.ptype); } + */ //if (aqdata->last_message != NULL && stristr(aqdata->last_message, "SERVICE") != NULL ) { if (aqdata->service_mode_state == ON) { @@ -516,6 +519,7 @@ int build_aqualink_aqmanager_JSON(struct aqualinkdata *aqdata, char* buffer, int length += logmaskjsonobject(PROG_LOG, buffer+length); length += logmaskjsonobject(DBGT_LOG, buffer+length); length += logmaskjsonobject(TIMR_LOG, buffer+length); + length += logmaskjsonobject(SIM_LOG, buffer+length); if (buffer[length-1] == ',') length--; length += sprintf(buffer+length, "]"); @@ -709,6 +713,83 @@ int build_aux_labels_JSON(struct aqualinkdata *aqdata, char* buffer, int size) //return strlen(buffer); } +/* +const char* emulationtype2name(emulation_type type) { + switch (type) { + case ALLBUTTON: + return "allbutton"; + break; + case RSSADAPTER: + return "allbutton"; + break; + case ONETOUCH: + return "onetouch"; + break; + case IAQTOUCH: + return "iaqualinktouch"; + break; + case AQUAPDA: + return "aquapda"; + break; + case JANDY_DEVICE: + return "jandydevice"; + break; + case SIMULATOR: + return "allbutton"; + break; + default: + return "none"; + break; + } +} +*/ +int build_aqualink_simulator_packet_JSON(struct aqualinkdata *aqdata, char* buffer, int size) +{ + memset(&buffer[0], 0, size); + int length = 0; + int i; + + length += sprintf(buffer+length, "{\"type\": \"simpacket\""); + + if (aqdata->simulator_packet[PKT_DEST] >= 0x40 && aqdata->simulator_packet[PKT_DEST] <= 0x43) { + length += sprintf(buffer+length, ",\"simtype\": \"onetouch\""); + } else if (aqdata->simulator_packet[PKT_DEST] >= 0x08 && aqdata->simulator_packet[PKT_DEST] <= 0x0a) { + length += sprintf(buffer+length, ",\"simtype\": \"allbutton\""); + } else if (aqdata->simulator_packet[PKT_DEST] >= 0x30 && aqdata->simulator_packet[PKT_DEST] <= 0x33) { + length += sprintf(buffer+length, ",\"simtype\": \"iaqtouch\""); + } else if (aqdata->simulator_packet[PKT_DEST] >= 0x60 && aqdata->simulator_packet[PKT_DEST] <= 0x63) { + length += sprintf(buffer+length, ",\"simtype\": \"aquapda\""); + } else { + length += sprintf(buffer+length, ",\"simtype\": \"unknown\""); + } + //if (aqdata->simulator_packet[i][]) + //length += sprintf(buffer+length, ",\"simtype\": \"onetouch\""); + + length += sprintf(buffer+length, ",\"raw\": ["); + for (i=0; i < aqdata->simulator_packet_length; i++) + { + length += sprintf(buffer+length, "\"0x%02hhx\",", aqdata->simulator_packet[i]); + } + if (buffer[length-1] == ',') + length--; + length += sprintf(buffer+length, "]"); + + length += sprintf(buffer+length, ",\"dec\": ["); + for (i=0; i < aqdata->simulator_packet_length; i++) + { + length += sprintf(buffer+length, "%d,", aqdata->simulator_packet[i]); + } + if (buffer[length-1] == ',') + length--; + + length += sprintf(buffer+length, "]"); + + length += sprintf(buffer+length, "}"); + +//printf("Buffer=%d, used=%d, OUT='%s'\n",size,length,buffer); + + return length; +} // WS Received '{"parameter":"SPA_HTR","value":99}' // WS Received '{"command":"KEY_HTR_POOL"}' diff --git a/json_messages.h b/json_messages.h index bd010e80..07d78449 100644 --- a/json_messages.h +++ b/json_messages.h @@ -9,6 +9,7 @@ #define JSON_LABEL_SIZE 600 #define JSON_BUFFER_SIZE 5120 #define JSON_STATUS_SIZE 2048 +#define JSON_SIMULATOR_SIZE 2048 #define JSON_MQTT_MSG_SIZE 100 @@ -57,6 +58,7 @@ int build_aqualink_aqmanager_JSON(struct aqualinkdata *aqdata, char* buffer, int //int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch, char* buffer, int size, bool homekit); //int build_device_JSON(struct aqualinkdata *aqdata, int programable_switch1, int programable_switch2, char* buffer, int size, bool homekit); int build_device_JSON(struct aqualinkdata *aqdata, char* buffer, int size, bool homekit); +int build_aqualink_simulator_packet_JSON(struct aqualinkdata *aqdata, char* buffer, int size); #endif /* JSON_MESSAGES_H_ */ diff --git a/net_services.c b/net_services.c index 6b00fb78..6e428907 100644 --- a/net_services.c +++ b/net_services.c @@ -43,6 +43,7 @@ #include "aq_timer.h" #include "aq_scheduler.h" #include "rs_msg_utils.h" +#include "simulator.h" #include "version.h" #ifdef AQ_PDA @@ -141,6 +142,24 @@ void _broadcast_aqualinkstate_error(struct mg_connection *nc, char *msg) // Maybe enhacment in future to sent error messages to MQTT } + +void _broadcast_simulator_message(struct mg_connection *nc) { + struct mg_connection *c; + char data[JSON_SIMULATOR_SIZE]; + + build_aqualink_simulator_packet_JSON(_aqualink_data, data, JSON_SIMULATOR_SIZE); + + for (c = mg_next(nc->mgr, NULL); c != NULL; c = mg_next(nc->mgr, c)) { + if (is_websocket(c) && is_websocket_simulator(c)) { + ws_send(c, data); + } + } + + //LOG(NET_LOG,LOG_DEBUG, "Sent to simulator '%s'\n",data); + + _aqualink_data->simulator_packet_updated = false; +} + #ifdef AQ_MANAGER #define WS_LOG_LENGTH 200 @@ -159,7 +178,13 @@ sd_journal *open_journal() { sd_journal *journal; char filter[51]; +#ifndef AQ_CONTAINER + // Below works for local if (sd_journal_open(&journal, SD_JOURNAL_LOCAL_ONLY) < 0) +#else + // Container doesn't have local systemd_journal so use hosts through mapped filesystem + if (sd_journal_open_directory(&journal, "/var/log/journal", SD_JOURNAL_SYSTEM) < 0) +#endif { LOGSystemError(errno, NET_LOG, "Failed to open journal"); return journal; @@ -171,6 +196,7 @@ sd_journal *open_journal() { sd_journal_close(journal); return journal; } + /* Docker wll also have problem with this // Daemon will change PID after printing startup message, so don't filter on current PID if (_aqconfig_.deamonize != true) { snprintf(filter, 50, "_PID=%d",getpid()); @@ -180,7 +206,7 @@ sd_journal *open_journal() { sd_journal_close(journal); return journal; } - } + }*/ if (sd_journal_set_data_threshold(journal, LOGBUFFER) < 0) { @@ -414,7 +440,8 @@ void _broadcast_aqualinkstate(struct mg_connection *nc) #endif for (c = mg_next(nc->mgr, NULL); c != NULL; c = mg_next(nc->mgr, c)) { - if (is_websocket(c)) + //if (is_websocket(c) && !is_websocket_simulator(c)) // No need to broadcast status messages to simulator. + if (is_websocket(c)) // All button simulator needs status messages ws_send(c, data); #ifndef MG_DISABLE_MQTT else if (is_mqtt(c)) @@ -959,10 +986,25 @@ uriAtype action_URI(request_source from, const char *URI, int uri_length, float } else if (strncmp(ri1, "schedules", 9) == 0) { return uSchedules; } else if (strncmp(ri1, "simulator", 9) == 0 && from == NET_WS) { // Only valid from websocket. + if (ri2 != NULL && strncmp(ri2, "onetouch", 8) == 0) { + start_simulator(_aqualink_data, ONETOUCH); + } else if (ri2 != NULL && strncmp(ri2, "allbutton", 9) == 0) { + start_simulator(_aqualink_data, ALLBUTTON); + } else if (ri2 != NULL && strncmp(ri2, "aquapda", 7) == 0) { + start_simulator(_aqualink_data, AQUAPDA); + } else if (ri2 != NULL && strncmp(ri2, "iaqtouch", 8) == 0) { + start_simulator(_aqualink_data, IAQTOUCH); + } else { + return uBad; + } return uSimulator; + } else if (strncmp(ri1, "simcmd", 10) == 0 && from == NET_WS) { // Only valid from websocket. + simulator_send_cmd((unsigned char)value); + return uActioned; + /* } else if (strncmp(ri1, "rawcommand", 10) == 0 && from == NET_WS) { // Only valid from websocket. aq_send_cmd((unsigned char)value); - return uActioned; + return uActioned;*/ #ifdef AQ_MANAGER } else if (strncmp(ri1, "aqmanager", 9) == 0 && from == NET_WS) { // Only valid from websocket. return uAQmanager; @@ -1562,9 +1604,11 @@ void action_websocket_request(struct mg_connection *nc, struct websocket_message break; case uSimulator: { - LOG(NET_LOG,LOG_DEBUG, "Started Simulator Mode\n"); + LOG(NET_LOG,LOG_DEBUG, "Request to start Simulator\n"); set_websocket_simulator(nc); - _aqualink_data->simulate_panel = true; + //_aqualink_data->simulate_panel = true; + // Clear simulator ID incase sim type changes + //_aqualink_data->simulator_id = NUL; DEBUG_TIMER_START(&tid); char message[JSON_BUFFER_SIZE]; build_aqualink_status_JSON(_aqualink_data, message, JSON_BUFFER_SIZE); @@ -1706,9 +1750,8 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { if (is_websocket(nc)) { _aqualink_data->open_websockets--; LOG(NET_LOG,LOG_DEBUG, "-- Websocket left\n"); - // Need something below to detect is_websocket_simulator() and turn off aq_data.simulate_panel if (is_websocket_simulator(nc)) { - _aqualink_data->simulate_panel = false; + stop_simulator(_aqualink_data); LOG(NET_LOG,LOG_DEBUG, "Stoped Simulator Mode\n"); } else if (is_websocket_aqmanager(nc)) { _aqualink_data->aqManagerActive = false; @@ -1923,9 +1966,9 @@ void *net_services_thread( void *ptr ) while (_keepNetServicesRunning == true) { //poll_net_services(&_mgr, 10); - // Shorten poll cycle when logging messages to WS - //mg_mgr_poll(&_mgr, (_aqualink_data->aqManagerActive)?50:100); - mg_mgr_poll(&_mgr, 100); + // Shorten poll cycle when in simulator mode + mg_mgr_poll(&_mgr, (_aqualink_data->simulator_active != SIM_NONE)?10:100); + //mg_mgr_poll(&_mgr, 100); if (aqdata->updated == true /*|| _broadcast == true*/) { //LOG(NET_LOG,LOG_DEBUG, "********** Broadcast ************\n"); @@ -1933,10 +1976,13 @@ void *net_services_thread( void *ptr ) aqdata->updated = false; } #ifdef AQ_MANAGER - if ( ! broadcast_systemd_logmessages(_aqualink_data->aqManagerActive)) { + if ( ! broadcast_systemd_logmessages(aqdata->aqManagerActive)) { LOG(AQUA_LOG,LOG_ERR, "Couldn't open systemd journal log\n"); } #endif + if (aqdata->simulator_active != SIM_NONE && aqdata->simulator_packet_updated == true ) { + _broadcast_simulator_message(_mgr.active_connections); + } } f_end: @@ -1958,6 +2004,10 @@ void broadcast_aqualinkstate() { void broadcast_aqualinkstate_error(char *msg) { _broadcast_aqualinkstate_error(_mgr.active_connections, msg); } +void broadcast_simulator_message() { + _aqualink_data->simulator_packet_updated = true; +} + void stop_net_services() { _keepNetServicesRunning = false; @@ -2009,6 +2059,11 @@ void broadcast_aqualinkstate_error(/*struct mg_connection *nc,*/ char *msg) } LOG(NET_LOG,LOG_NOTICE, "Broadcast error to network\n"); } +void broadcast_simulator_message() { + if ( ! _aqconfig_.thread_netservices) { + return _broadcast_simulator_message(); + } +} time_t poll_net_services(/*struct mg_mgr *mgr,*/ int timeout_ms) { if (timeout_ms < 0) diff --git a/net_services.h b/net_services.h index afa23f99..b2449e4a 100644 --- a/net_services.h +++ b/net_services.h @@ -29,6 +29,7 @@ void stop_net_services(); time_t poll_net_services(int timeout_ms); void broadcast_aqualinkstate(); void broadcast_aqualinkstate_error(char *msg); +void broadcast_simulator_message(); // superseded with systemd/sd-journal //void broadcast_log(char *msg); diff --git a/pda_aq_programmer.c b/pda_aq_programmer.c index 0ab8e550..20c66a64 100644 --- a/pda_aq_programmer.c +++ b/pda_aq_programmer.c @@ -865,7 +865,7 @@ bool set_PDA_numeric_field_value(struct aqualinkdata *aq_data, int val, int cur_ char *hghlight_chars; int hlight_length=0; int i=0; - //hghlight_chars = pda_m_hlightchars(&hlight_length); + hghlight_chars = pda_m_hlightchars(&hlight_length); // NSF May need to take this out and there for the LOG entry after while while (hlight_length >= 15 || hlight_length <= 0) { delay(500); waitForPDANextMessageType(aq_data,CMD_PDA_HIGHLIGHTCHARS,5); diff --git a/release/aqualinkd b/release/aqualinkd index ac17b4c7..827d7c63 100755 Binary files a/release/aqualinkd and b/release/aqualinkd differ diff --git a/release/aqualinkd.conf b/release/aqualinkd.conf index 72879333..872ffb52 100755 --- a/release/aqualinkd.conf +++ b/release/aqualinkd.conf @@ -132,8 +132,6 @@ report_zero_pool_temp = no #serial_debug_filter = 0x00 # Not documented. These are experimental. Will change how RS485 / Serial works, Only use if asked to for problem solving purposes. -#serial_readahead_b4_write = yes -#prioritize_ack = yes rs485_frame_delay = 4 # Get rid of the startup warning message about no low latency. BETTER option is to buy a better adapter. diff --git a/release/aqualinkd.test.pda.conf b/release/aqualinkd.test.pda.conf index 64b10713..cfc4ec1a 100644 --- a/release/aqualinkd.test.pda.conf +++ b/release/aqualinkd.test.pda.conf @@ -98,11 +98,12 @@ device_id=0x60 #extended_device_id_programming = no # Not documented -serial_readahead_b4_write = yes -mqtt_timed_update = no -thread_netservices = yes -rs_poll_speed = -1 +#serial_readahead_b4_write = yes +#mqtt_timed_update = no +#thread_netservices = yes +#rs_poll_speed = -1 #rs_poll_speed = 1 +rs485_frame_delay = 4 # Your RS panel size. ie 4, 6, 8, 12 or 16 relates to RS4, RS6, RS8, RS12 or RS16. # VERY important that you select 12 or 16, if you have either of those size panels. diff --git a/release/serial_logger b/release/serial_logger index e883352f..046d3e57 100755 Binary files a/release/serial_logger and b/release/serial_logger differ diff --git a/serial_logger.c b/serial_logger.c index bcbeefdd..ad08f669 100644 --- a/serial_logger.c +++ b/serial_logger.c @@ -41,7 +41,7 @@ #define SLOG_MAX 80 #define PACKET_MAX 600 -#define VERSION "serial_logger V2.0" +#define VERSION "serial_logger V2.1" /* typedef enum used { @@ -86,7 +86,7 @@ bool _playback_file = false; int sl_timespec_subtract (struct timespec *result, const struct timespec *x, const struct timespec *y); -int _serial_logger(int rs_fd, char *port_name, int logPackets, int logLevel, bool panleProbe, bool rsSerialSpeedTest, bool errorMonitor); +int _serial_logger(int rs_fd, char *port_name, int logPackets, int logLevel, bool panleProbe, bool rsSerialSpeedTest, bool errorMonitor, bool printAllIDs); #ifdef SERIAL_LOGGER @@ -101,7 +101,7 @@ void intHandler(int dummy) { } #else int serial_logger (int rs_fd, char *port_name, int logLevel) { - return _serial_logger(rs_fd, port_name, PACKET_MAX, (logLevel>=LOG_NOTICE?logLevel:LOG_NOTICE), true, false, false); + return _serial_logger(rs_fd, port_name, PACKET_MAX, (logLevel>=LOG_NOTICE?logLevel:LOG_NOTICE), true, false, false, false); } #endif @@ -362,11 +362,9 @@ int main(int argc, char *argv[]) { bool rsSerialSpeedTest = false; bool serialBlocking = true; bool errorMonitor = false; + bool printAllIDs = false; // aq_serial.c uses the following -#ifdef AQ_RS_EXTRA_OPTS - _aqconfig_.readahead_b4_write = false; -#endif _aqconfig_.log_protocol_packets = false; _aqconfig_.log_raw_bytes = false; _aqconfig_.ftdi_low_latency = true; @@ -391,6 +389,7 @@ int main(int argc, char *argv[]) { fprintf(stderr, "\t-lpack (log RS packets to %s)\n",RS485LOGFILE); fprintf(stderr, "\t-lrawb (log raw RS bytes to %s)\n",RS485BYTELOGFILE); fprintf(stderr, "\t-e (monitor errors)\n"); + fprintf(stderr, "\t-a (Print all ID's the panel queried)\n"); fprintf(stderr, "\nie:\t%s /dev/ttyUSB0 -d -p 1000 -i 0x08 -i 0x0a\n\n", argv[0]); return 1; } @@ -424,6 +423,8 @@ int main(int argc, char *argv[]) { serialBlocking = false; } else if (strcmp(argv[i], "-e") == 0) { errorMonitor = true; + } else if (strcmp(argv[i], "-a") == 0) { + printAllIDs = true; } } @@ -471,7 +472,7 @@ int main(int argc, char *argv[]) { startPacketLogger(); - _serial_logger(rs_fd, argv[1], logPackets, logLevel, panleProbe, rsSerialSpeedTest, errorMonitor); + _serial_logger(rs_fd, argv[1], logPackets, logLevel, panleProbe, rsSerialSpeedTest, errorMonitor, printAllIDs); stopPacketLogger(); @@ -483,7 +484,7 @@ int main(int argc, char *argv[]) { -int _serial_logger(int rs_fd, char *port_name, int logPackets, int logLevel, bool panleProbe, bool rsSerialSpeedTest, bool errorMonitor) { +int _serial_logger(int rs_fd, char *port_name, int logPackets, int logLevel, bool panleProbe, bool rsSerialSpeedTest, bool errorMonitor, bool printAllIDs) { int packet_length; int last_packet_length = 0; unsigned char packet_buffer[AQ_MAXPKTLEN]; @@ -569,6 +570,7 @@ int _serial_logger(int rs_fd, char *port_name, int logPackets, int logLevel, boo } } if (found != true && sindex < SLOG_MAX) { + //printf("Added id 0x%02hhx\n",packet_buffer[PKT_DEST]); slog[sindex].ID = packet_buffer[PKT_DEST]; slog[sindex].inuse = false; sindex++; @@ -654,7 +656,8 @@ int _serial_logger(int rs_fd, char *port_name, int logPackets, int logLevel, boo for (i = 0; i < sindex; i++) { //LOG(RSSD_LOG, LOG_NOTICE, "ID 0x%02hhx is %s %s\n", slog[i].ID, (slog[i].inuse == true) ? "in use" : "not used", // (slog[i].inuse == false && canUse(slog[i].ID) == true)? " <-- can use for Aqualinkd" : ""); - if (logLevel >= LOG_DEBUG || slog[i].inuse == true || canUse(slog[i].ID) == true) { + //if (logLevel >= LOG_DEBUG || slog[i].inuse == true || canUse(slog[i].ID) == true) { + if (logLevel >= LOG_DEBUG || slog[i].inuse == true || canUse(slog[i].ID) == true || printAllIDs == true) { LOG(RSSD_LOG, LOG_NOTICE, "ID 0x%02hhx is %s %s\n", slog[i].ID, (slog[i].inuse == true) ? "in use " : "not used", (slog[i].inuse == false)?canUseExtended(slog[i].ID):getDevice(slog[i].ID)); } diff --git a/simulator.c b/simulator.c new file mode 100644 index 00000000..709574e4 --- /dev/null +++ b/simulator.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include + +#include "aqualink.h" +#include "net_services.h" +#include "packetLogger.h" + +#define MAX_STACK 20 +int _sim_stack_place = 0; +unsigned char _commands[MAX_STACK]; + +bool push_simulator_cmd(unsigned char cmd); + +int simulator_cmd_length() +{ + return _sim_stack_place; +} + +// External command +void simulator_send_cmd(unsigned char cmd) +{ + push_simulator_cmd(cmd); +} + +bool push_simulator_cmd(unsigned char cmd) +{ + if (_sim_stack_place < MAX_STACK) { + _commands[_sim_stack_place] = cmd; + _sim_stack_place++; + } else { + LOG(SIM_LOG, LOG_ERR, "Command queue overflow, too many unsent commands to RS control panel\n"); + return false; + } + + return true; +} + +unsigned char pop_simulator_cmd(unsigned char receive_type) +{ + unsigned char cmd = NUL; + + if (_sim_stack_place > 0 && receive_type == CMD_STATUS ) { + cmd = _commands[0]; + _sim_stack_place--; + memmove(&_commands[0], &_commands[1], sizeof(unsigned char) * _sim_stack_place ) ; + } + + LOG(SIM_LOG,LOG_DEBUG, "Sending '0x%02hhx' to controller\n", cmd); + return cmd; +} + +bool processSimulatorPacket(unsigned char *packet, int packet_length, struct aqualinkdata *aqdata) +{ + // copy packed into buffer to be sent to web + //memset(aqdata->simulator_packet, 0, sizeof aqdata->simulator_packet); + memcpy(aqdata->simulator_packet, packet, packet_length); + aqdata->simulator_packet_length = packet_length; + + if ( getLogLevel(SIM_LOG) >= LOG_DEBUG ) { + char buff[1000]; + //sprintf("Sending control command:") + beautifyPacket(buff, packet, packet_length, false); + LOG(SIM_LOG,LOG_DEBUG, "Received message : %s", buff); + } + + broadcast_simulator_message(); + + return true; +} + +bool start_simulator(struct aqualinkdata *aqdata, emulation_type type) { + + // If we are a PDA panel and PDA sim, we are connected, so just set that + // since PDA only panel can only handle one remote ID. + if (isPDA_PANEL && type == AQUAPDA) { + aqdata->simulator_active = type; + aqdata->simulator_id = _aqconfig_.device_id; + } + + // if type is same AND id is valid, sim is already started, their is nothing to do. + if (aqdata->simulator_active == type) { + if (aqdata->simulator_id >= 0x40 && aqdata->simulator_id <= 0x43) { + LOG(SIM_LOG,LOG_NOTICE, "OneTouch Simulator already active!\n"); + return true; + } else if (aqdata->simulator_id >= 0x08 && aqdata->simulator_id <= 0x0a) { + LOG(SIM_LOG,LOG_NOTICE, "AllButton Simulator already active!\n"); + return true; + } else if (aqdata->simulator_id >= 0x30 && aqdata->simulator_id <= 0x33) { + LOG(SIM_LOG,LOG_NOTICE, "iAqualinkTouch Simulator already active!\n"); + return true; + } else if (aqdata->simulator_id >= 0x60 && aqdata->simulator_id <= 0x63) { + LOG(SIM_LOG,LOG_NOTICE, "PDA Simulator already active!\n"); + return true; + } + } + + // Check it's a valid request + if (type == ALLBUTTON) { + LOG(SIM_LOG,LOG_NOTICE, "Starting AllButton Simulator!\n"); + } else if (type == ONETOUCH) { + LOG(SIM_LOG,LOG_NOTICE, "Starting OneTouch Simulator!\n"); + } else if (type == AQUAPDA ) { + LOG(SIM_LOG,LOG_NOTICE, "Starting PDA Simulator!\n"); + } else if (type == IAQTOUCH) { + LOG(SIM_LOG,LOG_NOTICE, "Starting iAqualinkTouch Simulator!\n"); + } else { + LOG(SIM_LOG,LOG_ERR, "Request to start simulator of unknown type : %d", type); + return false; + } + + // start the simulator + aqdata->simulator_active = type; + aqdata->simulator_id = NUL; + + return true; +} + +bool stop_simulator(struct aqualinkdata *aqdata) { + aqdata->simulator_active = SIM_NONE; + aqdata->simulator_id = NUL; + + LOG(SIM_LOG,LOG_DEBUG, "Stoped Simulator Mode\n"); + + return true; +} + +bool is_simulator_packet(struct aqualinkdata *aqdata, unsigned char *packet, int packet_length) { + + if ( (aqdata->simulator_active == ONETOUCH && packet[PKT_DEST] >= 0x40 && packet[PKT_DEST] <= 0x43) || + (aqdata->simulator_active == ALLBUTTON && packet[PKT_DEST] >= 0x08 && packet[PKT_DEST] <= 0x0a) || + (aqdata->simulator_active == IAQTOUCH && packet[PKT_DEST] >= 0x30 && packet[PKT_DEST] <= 0x33) || + (aqdata->simulator_active == AQUAPDA && packet[PKT_DEST] >= 0x60 && packet[PKT_DEST] <= 0x63) ) { + return true; + } else { + return false; + } +} \ No newline at end of file diff --git a/simulator.h b/simulator.h new file mode 100644 index 00000000..ee0e99fb --- /dev/null +++ b/simulator.h @@ -0,0 +1,18 @@ +#ifndef SIMULATOR_H_ +#define SIMULATOR_H_ + +#include + +bool processSimulatorPacket(unsigned char *packet_buffer, int packet_length, struct aqualinkdata *aqdata); +//unsigned char pop_simulator_cmd(struct aqualinkdata *aq_data); +unsigned char pop_simulator_cmd(unsigned char receive_type); +int simulator_cmd_length(); +//bool push_simulator_cmd(unsigned char cmd); +void simulator_send_cmd(unsigned char cmd); + +bool start_simulator(struct aqualinkdata *aqdata, emulation_type type); +bool stop_simulator(struct aqualinkdata *aqdata); + +bool is_simulator_packet(struct aqualinkdata *aqdata, unsigned char *packet, int packet_length); + +#endif // SIMULATOR_H_ \ No newline at end of file diff --git a/util.c b/util.c new file mode 100644 index 00000000..e69de29b diff --git a/utils.c b/utils.c index 827b253c..d18b5962 100644 --- a/utils.c +++ b/utils.c @@ -322,6 +322,9 @@ const char* logmask2name(int16_t from) case TIMR_LOG: return "Schd/Timer:"; break; + case SIM_LOG: + return "Simulator: "; + break; case AQUA_LOG: default: return "AqualinkD: "; diff --git a/utils.h b/utils.h index fafe53ed..0d6234bc 100644 --- a/utils.h +++ b/utils.h @@ -42,6 +42,7 @@ #define PROG_LOG (1 << 10) // Programmer #define DBGT_LOG (1 << 11) // Debug Timer #define TIMR_LOG (1 << 12) // Timers +#define SIM_LOG (1 << 13) // Simulator // Set scheduler log to timer log #define SCHD_LOG TIMR_LOG diff --git a/version.h b/version.h index fff34a98..45826be6 100644 --- a/version.h +++ b/version.h @@ -1,4 +1,4 @@ #define AQUALINKD_NAME "Aqualink Daemon" -#define AQUALINKD_VERSION "2.3.3" +#define AQUALINKD_VERSION "2.3.4" diff --git a/web/allbutton_sim.html b/web/allbutton_sim.html new file mode 100644 index 00000000..af51884f --- /dev/null +++ b/web/allbutton_sim.html @@ -0,0 +1,772 @@ + + + + + + AqualinkD Simulator + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+ + +   + + + +   + + + +   + + + +   + + + +   + +   +
+   + +   + +   + +   + +   + +   + +
+
+ + +
+
+ + +
+
+ +
 
+ + + + + + + + + +
 
+
+ + + \ No newline at end of file diff --git a/web/aqmanager.html b/web/aqmanager.html index 7a8d350e..5c8564b3 100644 --- a/web/aqmanager.html +++ b/web/aqmanager.html @@ -485,6 +485,7 @@ */ if (data['deamonized'] == 'off') { console.log("deamonized=" + data['deamonized'] + " Need to rename Restart to Reload"); + //disablebutton("restart"); } /* diff --git a/web/aquapda_sim.html b/web/aquapda_sim.html new file mode 100644 index 00000000..3c4dd061 --- /dev/null +++ b/web/aquapda_sim.html @@ -0,0 +1,686 @@ + + + + + + AqualinkD Simulator + + + + + + + + + + + + + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+                  +
+                  +
 
 
 
 
 
 
 
+
+
 
+
+ +
+ Warning
AqualinkD will not respond to commands while this window is open,
Please close when finished. +
+
+ +
+ + + \ No newline at end of file diff --git a/web/controller.html b/web/controller.html index e87fd381..da42990f 100644 --- a/web/controller.html +++ b/web/controller.html @@ -141,6 +141,27 @@ /*flex-direction: column;*/ } + .simulator_pane { + background-color: var(--options_pane_background); + border: 2px solid var(--options_pane_bordercolor); + border-radius: 20px; + justify-content: center; + align-items: center; + /* Actual size of sim 305 by 385 */ + width: 325px; + height: 405px; + padding: 10px; + /*overflow-y: scroll; didn't work*/ + /*height: 100%;*/ + /*flex-basis: content;*/ + /*flex-direction: column;*/ + } + + .simiframe { + width: 305px; + height: 385px; + } + .options { top: 10px; /*left: 10px;*/ @@ -609,6 +630,8 @@ document.getElementById('vspswitch_options').classList.remove("hide"); document.getElementById('timer_options').classList.remove("hide"); document.getElementById('scheduler_options').classList.remove("hide"); + //document.getElementById('simulator_options').classList.remove("hide"); + document.getElementById('simulator_iframe').classList.remove("hide"); setColors(); load_background(); showTileOptions(false); @@ -1233,6 +1256,7 @@ document.getElementById('vspswitch_options').style.display = 'none'; document.getElementById('timer_options').style.display = 'none'; document.getElementById('scheduler_options').style.display = 'none'; + document.getElementById('simulator_iframe').style.display = 'none'; } else if (id != null && document.getElementById(id).getAttribute('type') == 'switch_program') { active_option = document.getElementById('pswitch_options'); document.getElementById('thermostat_options').style.display = 'none'; @@ -1240,6 +1264,7 @@ document.getElementById('vspswitch_options').style.display = 'none'; document.getElementById('timer_options').style.display = 'none'; document.getElementById('scheduler_options').style.display = 'none'; + document.getElementById('simulator_iframe').style.display = 'none'; } else if (id != null && document.getElementById(id).getAttribute('type') == 'setpoint_swg') { active_option = document.getElementById('swg_options'); document.getElementById('thermostat_options').style.display = 'none'; @@ -1247,6 +1272,7 @@ document.getElementById('vspswitch_options').style.display = 'none'; document.getElementById('timer_options').style.display = 'none'; document.getElementById('scheduler_options').style.display = 'none'; + document.getElementById('simulator_iframe').style.display = 'none'; } else if (id != null && document.getElementById(id).getAttribute('type') == 'setpoint_freeze') { active_option = document.getElementById('swg_options'); document.getElementById('thermostat_options').style.display = 'none'; @@ -1254,6 +1280,7 @@ document.getElementById('vspswitch_options').style.display = 'none'; document.getElementById('timer_options').style.display = 'none'; document.getElementById('scheduler_options').style.display = 'none'; + document.getElementById('simulator_iframe').style.display = 'none'; } else if (id != null && document.getElementById(id).getAttribute('type') == 'switch_vsp') { active_option = document.getElementById('vspswitch_options'); document.getElementById('thermostat_options').style.display = 'none'; @@ -1261,6 +1288,7 @@ document.getElementById('swg_options').style.display = 'none'; document.getElementById('timer_options').style.display = 'none'; document.getElementById('scheduler_options').style.display = 'none'; + document.getElementById('simulator_iframe').style.display = 'none'; } else if (id != null && document.getElementById(id).getAttribute('type') == 'switch_timer') { active_option = document.getElementById('timer_options'); document.getElementById('thermostat_options').style.display = 'none'; @@ -1268,6 +1296,7 @@ document.getElementById('swg_options').style.display = 'none'; document.getElementById('vspswitch_options').style.display = 'none'; document.getElementById('scheduler_options').style.display = 'none'; + document.getElementById('simulator_iframe').style.display = 'none'; } else if (id != null && document.getElementById(id).getAttribute('type') == 'scheduler') { active_option = document.getElementById('scheduler_options'); document.getElementById('thermostat_options').style.display = 'none'; @@ -1275,6 +1304,15 @@ document.getElementById('swg_options').style.display = 'none'; document.getElementById('vspswitch_options').style.display = 'none'; document.getElementById('timer_options').style.display = 'none'; + document.getElementById('simulator_iframe').style.display = 'none'; + } else if (id != null && document.getElementById(id).getAttribute('type') == 'simulator') { + active_option = document.getElementById('simulator_iframe'); + document.getElementById('thermostat_options').style.display = 'none'; + document.getElementById('pswitch_options').style.display = 'none'; + document.getElementById('swg_options').style.display = 'none'; + document.getElementById('vspswitch_options').style.display = 'none'; + document.getElementById('timer_options').style.display = 'none'; + document.getElementById('scheduler_options').style.display = 'none'; } active_option.style.display = 'flex'; @@ -1310,6 +1348,7 @@ document.getElementById('vspswitch_options').style.display = 'none'; document.getElementById('timer_options').style.display = 'none'; document.getElementById('scheduler_options').style.display = 'none'; + document.getElementById('simulator_iframe').style.display = 'none'; document.getElementById('wrapper').classList.remove("opaque"); return; } @@ -1363,6 +1402,8 @@ } else if (type == 'scheduler') { title = document.getElementById("scheduler_options_title"); close_button = document.getElementById("scheduler_options_close"); + } else if (type == 'simulator') { + close_button = document.getElementById("simulator_iframe_close"); } else { slider = document.getElementById("option_slider_range"); slider_output = document.getElementById("option_slider_text_value"); @@ -1415,6 +1456,8 @@ if (type == 'scheduler') { title.innerHTML = "Scheduler"; + } else if (type == 'simulator') { + // title.innerHTML = "Simulator"; } else { title.innerHTML = document.getElementById(id + '_name').innerHTML; } @@ -1480,6 +1523,8 @@ cs_clearSchedules(); //cs_loadJSON("/api/schedules", cs_schedules,'jsonp'); get_schedules(); + } else if (type == 'simulator') { + startSimulator(); } else { slider.value = sp_value; oswitch = document.getElementById("option_switch"); @@ -1597,6 +1642,7 @@ if ( ! cs_createJSON() ) { return // This stops pane from closing. } + } else if (type == 'simulator') { } else { var value = slider.value; if (state == (tile.getAttribute('status') == 'off')) @@ -1673,6 +1719,16 @@ } } + function showSimulator(caller){ + caller.setAttribute('type', 'simulator'); + showTileOptions(true, caller.id); + } + + function startSimulator() { + console.log("Starting ifram sim init"); + document.getElementById('onetouch_iframe').display = 'block'; + } + // NSF CHANGE TO USING WS FOR THIS REQUEST /* function cs_loadJSON(path, success, error) { @@ -2418,6 +2474,7 @@
+
+
+ + +
+ +
+
@@ -2684,6 +2749,8 @@ + + diff --git a/web/onetouch_sim.html b/web/onetouch_sim.html new file mode 100644 index 00000000..ce17872e --- /dev/null +++ b/web/onetouch_sim.html @@ -0,0 +1,651 @@ + + + + + + AqualinkD Simulator + + + + + + + + + + + + + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
              
 
 
 
 
 
 
 
 
 
 
+ + + +
+ +
+ + + + + diff --git a/web/simulator.html b/web/simulator.html index 26d0d99e..10a834f6 100644 --- a/web/simulator.html +++ b/web/simulator.html @@ -10,590 +10,111 @@ - - function send_command(cmd) { - socket_di.send(JSON.stringify(cmd)); - } -/* - function reset() { - socket_di.send("reset\n"); - } -*/ - function init() { - startWebsockets(); - } + +
+
+ + + + + +
- + +
+ +
+
+ +
+
+ +
- -
+
+ +
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
-
-
- - -   - - - -   - - - -   - - - -   - - - -   - -   -
-   - -   - -   - -   - -   - -   - -
-
- - -
-
- - -
-
- -
 
- - - - - - - - - -
+
+
- + +
+ \ No newline at end of file