def start_graph_live(self, a_ax): """ main thread """ # print( f"start_graph_live in adapter {self.name} at {self.tcpip}" ) # msg = f"? ? start_graph_live for device: {self.name} " # AppGlobal.what_thread( threading.get_ident(), msg, 20 ) msg = "SmartPlugAdapter.start_graph_live " AppGlobal.log_if_wrong_thread(threading.get_ident(), msg=msg, main=True) if not self.monitor: return if self.gd_time_adj is None: self.gd_time_adj = [] self.gd_power = [] #self.reset_graphing_data() # why bother consider keeping data !! #if self.live_graph_lines is None: # or use self.live_graph_ready if self.live_graph_ready == False: # or use self.live_graph_ready self.live_graph_lines, = a_ax.plot( [], [], 'o', label=self.name ) # note unpack comma -- seem to be a list of 1 element # msg = f"self.live_graph_lines created for device: {self.name} {type(self.live_graph_lines)} {self.live_graph_lines}" # AppGlobal.logger.info( msg ) # print( msg ) # got >>>> why a list self.live_graph_lines: <class 'list'> [<matplotlib.lines.Line2D object at 0x00000274E0287AC8>] self.linestyle, self.colorstyle, self.markerstyle, self.widthstyle = AppGlobal.graph_live.line_style.get_next_style( ) self.live_graph_ready = True
def check_timer_done(self, ): """ for now do not assume timer is running call from ht polling action """ msg = "SmartPlugAdapter.check_timer_done " AppGlobal.log_if_wrong_thread(threading.get_ident(), msg=msg, main=False) if not (self.timer_on): return now = time.time() sec_left = (self.timer_start + self.time_sec) - now # could be timer_end stored # print( f"check_timer_done sec_left {sec_left} for plug: {self.name} " ) # debug if sec_left <= 0: self.off() self.display_msg("Timer Stop") else: self.timer_sec_left = sec_left a_string = self.sec_to_string(sec_left) # just update display self.display_msg(f"Timer {a_string}")
def update_graph_live(self, ): """ call from main thread from graph_live but could we use our own polling ?? return True or False, depending on weather we were already set up """ msg = "SmartPlugAdapter.update_graph_live " AppGlobal.log_if_wrong_thread(threading.get_ident(), msg=msg, main=True) # msg = f"Adapter.update_graph_live device: {self.name} data: {self.graphing_new_data} ready: {self.live_graph_ready}" # AppGlobal.logger.info( msg ) if (not self.graphing_new_data) or (not self.live_graph_ready): return False self.live_graph_lines.set_xdata(self.gd_time_adj) self.live_graph_lines.set_ydata(self.gd_power) # not sure these need to be done on every cycle but works self.live_graph_lines.set_linestyle(self.linestyle) self.live_graph_lines.set_color(self.colorstyle) self.live_graph_lines.set_marker(self.markerstyle) self.live_graph_lines.set_linewidth(self.widthstyle) self.live_graph_lines.set_label( self.name ) # not currently working lacks update or.... setting legend later seemed to do it once may be enough self.graphing_new_data = False return True
def check_recording(self, ): """ call helper thread, best or ok in main -- used by smart_plug not smart_plug_graphing if recording then record the data in db for now do not assume timer is running call from ht polling action """ msg = "SmartPlugAdapter.check_recording " AppGlobal.log_if_wrong_thread(threading.get_ident(), msg=msg, main=False) if not (self.recording or self.monitor): return # msg = f"?ht? adapter check_recording " # AppGlobal.what_thread( threading.get_ident(), msg, 20 ) now = time.time() if now < self.next_record_time: # or monitor time return self.last_record_time = now self.next_record_time = now + self.delta_t try: plug = pyHS100.SmartPlug(self.tcpip) plug_data = plug.get_emeter_realtime() # ?? could add plug state - on off,..... # id = f"{self.name}{self.last_record_time}r" # print ( f"record data {id}:{plug_data})" ) rnd = "%.2f" % plug_data["power"] msg = f'{rnd} watts' #!! bit of rounding would be nice here self.gui_tk_label_2.config(text=msg) except pyHS100.smartdevice.SmartDeviceException as exception: # look up correct exception self.timer_on = False #self.record_off() to much messaging and will throw own error msg = f"failed to communicate with plug - record off for: {self.name} {self.tcpip}" AppGlobal.gui.print_info_string(msg) return if AppGlobal.graph_live_flag and self.monitor and self.live_graph_ready: # print( "check_recording" ) self.graphing_new_data = True #!! normalized and unnormalize data self.gd_time.append(self.last_record_time) self.gd_time_adj.append( AppGlobal.graph_live.rescale_time_function( self.last_record_time)) #?? make local ref?? self.gd_power.append(plug_data["power"]) if self.recording: db_data = ((self.name, self.last_record_time, "r", "?", plug_data["voltage"], plug_data["current"], plug_data["power"], plug_data["total"]), ) self.insert_measurements(db_data)
def polling_mt(self, ): """ polling for the main thread ( gui thread ) """ msg = "SmartPlugAdapter.polling_mt " AppGlobal.log_if_wrong_thread(threading.get_ident(), msg=msg, main=True)
def polling_ht(self, ): """ polling for the helper thread not sure this is ever needed """ msg = "GraphLive.polling_ht " AppGlobal.log_if_wrong_thread(threading.get_ident(), msg=msg, main=False)
def polling(self): """ started from gui thread as beginning of new thread this is an infinite loop monitoring the queue actions based on queue_to_helper and run_event application purpose is the device polling where we monitor/record the devices """ #self.logger.debug( "HealperThread.polling() entered " ) msg = "smart plug Helper.polling()" AppGlobal.log_if_wrong_thread(threading.get_ident(), msg=msg, main=False) while True: try: # if self.last_time + 10 < time.time(): # self.last_time = time.time() # self.print_info_string( "Time" + str( self.last_time ) ) # + a_port ) (action, function, function_args) = self.rec_from_queue() if action != "": self.logger.debug("smart_plug_helper.polling() queue: " + action + " " + str(function) + " " + str(function_args)) # ?? comment out if action == "call": #print( "ht making call" ) sys.stdout.flush() self.controller.helper_task_active = True function(*function_args) self.logger.debug( "smart_plug_helper.polling() return running helper loop " ) # ?? comment out #self.print_helper_label( "return running helper loop " ) self.controller.helper_task_active = False # do we maintain this, or move to helper -- looks like not used, app global better location if action == "stop": # seems to be working fine msg = "helper got stop in the queue -- end thread with return " # AppGlobal.what_thread( threading.get_ident(), msg, 50 ) self.controller.helper_task_active = False return # this will kill the thread --- think it is the return which should end the loop ? or do we need a break -- we seem to keep looping msg = "helper got stop in the queue -- ran thru return " # AppGlobal.what_thread( threading.get_ident(), msg, 50 ) self.device_polling() # must catch all exceptions if we do not want polling to stop ... but maybe we do except Exception as he: #self.logger.info( "schedule_me_helper.HelperThread threw exception from " + he.msg ) # info debug... !! also info to msg area msg = f"smart_plug_helper.HelperThread threw exception: {he}" print(f"see log: {msg}") self.logger.error(msg, exc_info=True) # info debug... time.sleep( self.ht_delta_t) # ok here since it is the main pooling loop #self.logger.debug( "HealperThread.polling() done with sleep " ) return
def polling_ht(self, ): """ polling for the helper thread """ msg = "SmartPlugAdapter.polling_ht " AppGlobal.log_if_wrong_thread(threading.get_ident(), msg=msg, main=False) self.check_timer_done() self.check_recording()
def reset_graphing_data(self, ): """ fine in helper thread ok in both """ AppGlobal.log_if_wrong_thread( threading.get_ident(), msg="now in smart_plug_adapter.reset_graphing_data", main=True) # print( "reset_graphing_data" ) self.gd_time = [] self.gd_time_adj = [] self.gd_power = [] #self.gd_power_adj = [] self.gd_energy = [] self.gd_energy_adj = [] self.graphing_new_data = False
def run(self): """ called when thread is started from gt its call should be considered as from ht """ self.parameters.init_from_helper() self.scheduled_event_list = AppGlobal.scheduled_event_list id = threading.get_ident() msg = f"helper run " AppGlobal.log_if_wrong_thread(threading.get_ident(), msg=msg, main=False) self.logger.debug(msg) # print( "==============================================" ) self.polling() # may mach name in gui thread
def graph_power_from_db(self, line_style=None, ax=None): """ None default because required hence exception """ msg = "SmartPlugAdapter.graph_power_from_db " AppGlobal.log_if_wrong_thread(threading.get_ident(), msg=msg, main=True) line_style.get_next_style() time_data = self.gd_time_adj if ((time_data is None) or len(time_data) == 0): print(f"no data for {self.name}") return ax.plot(time_data, self.gd_power, linestyle=line_style.linestyle, marker=line_style.markerstyle, color=line_style.colorstyle, label=self.name) # label= "Power (Watts)"
def device_polling(self): """ helper thread poll the devices - now just smartplugs, but fairly easily adapted to other polling tasks. so simple may put back in polling also extended for graphing """ # print( "device_polling" ) msg = "HealperThread.device_polling " AppGlobal.log_if_wrong_thread(threading.get_ident(), msg=msg, main=False) # AppGlobal.what_thread( threading.get_ident(), msg, 50 ) now = time.time() for i_adapter in AppGlobal.smartplug_adapter_list: i_adapter.polling_ht() # now the live graph # print( f"AppGlobal.graph_live {AppGlobal.graph_live_flag}") if not AppGlobal.graph_live_flag: return # print( "smart_plug_helper device_polling graph_live" ) # think this test now in graph_live update_graph_bool = False for i_adapter in AppGlobal.smartplug_adapter_list: if i_adapter.graphing_new_data: update_graph_bool = True break # print( f"update_graph_bool {update_graph_bool}" ) if not update_graph_bool: # print( f"update_graph_bool {update_graph_bool}" ) return AppGlobal.logger.error( "SmartPlugHelper.device_polling off to graph live") AppGlobal.graph_live.polling_ht()
def polling_mt(self, ): """ polling for the main thread ( gui thread ) """ msg = "GraphLive.polling_mt " AppGlobal.log_if_wrong_thread(threading.get_ident(), msg=msg, main=True) # AppGlobal.logger.error( msg ) # collect flags from the individual devices need_redraw_flag = False for i_adapter in AppGlobal.smartplug_adapter_list: if i_adapter.update_graph_live(): need_redraw_flag = True #AppGlobal.logger.error( "GraphLive.polling_mt test next" ) if need_redraw_flag: AppGlobal.logger.error("GraphLive.polling_mt need redraw True") self.ax.relim() self.ax.autoscale_view() #We need to draw *and* flush AppGlobal.logger.error("GraphLive.polling_mt next draw flush") self.figure.canvas.draw() self.figure.canvas.flush_events() plt.legend(loc=2) # help with update -- seems to work # AppGlobal.logger.error( "GraphLive.polling_mt polling show next" ) plt.show(block=False) # AppGlobal.logger.error( "GraphLive.polling_mtpolling post next" ) # ------------ for tracking memory use self.debug_count += 1 if self.debug_count > 50: AppGlobal.show_process_memory( "GraphLive.polling_mt polling time to log", log_level=20) self.debug_count = 0
def start_graph_live(self, ): """ call from main thread start off a session of live graphing -- need to run in main thread moved from the plug no validation if start is ok no inactivation of controls .... even this should perhaps come from a checkbox what do we want to do: graph only plugs that are being monitored or recorded accumulate the data in a list ( list of tuples might be better than two lists or even a numpy type thing ) ??? what is monitor, record is turned off ( keep data and graph line so far stop adding to data ) ??? what if new adapter is turned on ( was it on earlire ?? ) ??? export csv export all cached data to a csv, if none just a message """ msg = "GraphLive.start_graph_live" # AppGlobal.what_thread( threading.get_ident(), msg, 20 ) AppGlobal.log_if_wrong_thread(threading.get_ident(), msg=msg, main=True) self.rescale_time_function = lambda x: x # default identity function self.set_rescale_time_function() #Set up plot self.figure, self.ax = plt.subplots( figsize=(self.parameters.graph_x_size, self.parameters.graph_y_size)) self.figure.canvas.mpl_connect('close_event', self.end_graph_live_evt) plt.legend( loc=2 ) # for label to show up ? # made small sq show up no content, too early ? # lets try to build the line in each adapter self.lines, = self.ax.plot( [], [], 'o', color="red", linewidth=2.5, linestyle="-" ) # the line is the thing to update ... can we add later, lets assume we can #Auto scale on unknown axis and known limits on the other self.ax.set_autoscaley_on(True) #self.ax.set_xlim(self.min_x, self.max_x) #Other stuff self.ax.grid(linestyle='-', linewidth='0.5', color='red') self.ax.tick_params(axis='y', labelcolor='red') self.ax.set_title(f"Power measured by SmartPlugs") self.ax.set_xlabel( f"Time in {self.graph_time_units} from {self.graph_time_zero}" ) # these have been set multiple times self.ax.set_ylabel( f"Power in watts") # these have been set multiple times for i_adapter in AppGlobal.smartplug_adapter_list: i_adapter.start_graph_live( self.ax ) # this is a setup it does not start graphing by itself plt.show(block=False) AppGlobal.logger.error("GraphLive.start_graph_live complete") AppGlobal.graph_live_flag = True # this will kick off in polling set last