293 lines
13 KiB
Python
293 lines
13 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: UTF-8 -*-
|
|
#
|
|
|
|
import config
|
|
from rpi_envsens.dht import dht_22
|
|
from rpi_envsens.bmp import bmp_180
|
|
import garage_protocol
|
|
import gui
|
|
import logging
|
|
import numbers
|
|
import os
|
|
import report
|
|
import task
|
|
import tcp_socket
|
|
import time
|
|
import wetation_protocol
|
|
import wx
|
|
|
|
logger = logging.getLogger('APP')
|
|
|
|
|
|
class WetationFrameProt(gui.Wetation):
|
|
PROT_ID_GARAGE = 0
|
|
PROT_ID_IN = 1
|
|
PROT_ID_OUT = 2
|
|
ALL_PROT_IDS = [PROT_ID_GARAGE, PROT_ID_IN, PROT_ID_OUT, ]
|
|
|
|
PROT_IPS = {
|
|
PROT_ID_GARAGE: config.server_garage_ip,
|
|
PROT_ID_IN: config.server_in_ip,
|
|
PROT_ID_OUT: config.server_out_ip,
|
|
}
|
|
|
|
PROT_PORTS = {
|
|
PROT_ID_GARAGE: config.server_garage_port,
|
|
PROT_ID_IN: config.server_in_port,
|
|
PROT_ID_OUT: config.server_out_port,
|
|
}
|
|
|
|
PROT_SECRETS = {
|
|
PROT_ID_GARAGE: config.server_garage_secret,
|
|
PROT_ID_IN: config.server_in_secret,
|
|
PROT_ID_OUT: config.server_out_secret,
|
|
}
|
|
|
|
PROT_CLASSES = {
|
|
PROT_ID_GARAGE: garage_protocol.my_base_protocol_tcp,
|
|
PROT_ID_IN: wetation_protocol.my_base_protocol_tcp,
|
|
PROT_ID_OUT: wetation_protocol.my_base_protocol_tcp,
|
|
}
|
|
|
|
REQUEST_MSGS_SLOW = {
|
|
PROT_ID_IN: [
|
|
{
|
|
'service_id': wetation_protocol.my_base_protocol_tcp.SID_READ_REQUEST,
|
|
'data_id': wetation_protocol.my_base_protocol_tcp.CURRENT_ENVDATA,
|
|
},
|
|
],
|
|
PROT_ID_OUT: [
|
|
{
|
|
'service_id': wetation_protocol.my_base_protocol_tcp.SID_READ_REQUEST,
|
|
'data_id': wetation_protocol.my_base_protocol_tcp.CURRENT_ENVDATA,
|
|
},
|
|
],
|
|
}
|
|
|
|
REQUEST_MSGS_FAST = {
|
|
PROT_ID_GARAGE: [
|
|
{
|
|
'service_id': garage_protocol.my_base_protocol_tcp.SID_READ_REQUEST,
|
|
'data_id': garage_protocol.my_base_protocol_tcp.GATE_POSITION,
|
|
},
|
|
],
|
|
}
|
|
|
|
|
|
def __init__(self, *args, **kwds):
|
|
self.__min_temp_in__ = None
|
|
self.__min_temp_out__ = None
|
|
self.__max_temp_in__ = None
|
|
self.__max_temp_out__ = None
|
|
self.__prot__ = {}
|
|
gui.Wetation.__init__(self, *args, **kwds)
|
|
self.in_temperature_min.Bind(wx.EVT_LEFT_DOWN, self.reset_in_temp_minmax_evt)
|
|
self.in_temperature_max.Bind(wx.EVT_LEFT_DOWN, self.reset_in_temp_minmax_evt)
|
|
self.out_temperature_min.Bind(wx.EVT_LEFT_DOWN, self.reset_out_temp_minmax_evt)
|
|
self.out_temperature_max.Bind(wx.EVT_LEFT_DOWN, self.reset_out_temp_minmax_evt)
|
|
self.ShowFullScreen(config.FULL_SCREEN)
|
|
|
|
self.__init_communication__()
|
|
|
|
self.__task_1s__ = task.periodic(1, self.__task_1s_callback__)
|
|
self.__task_10s__ = task.periodic(10, self.__task_10s_callback__)
|
|
self.__task_60s__ = task.periodic(60, self.__task_60s_callback__)
|
|
|
|
def __init_communication__(self):
|
|
#
|
|
# Start TCP-Clients
|
|
for prot_id in self.ALL_PROT_IDS:
|
|
logger.debug('Initiating communication channel for prot_id %d', prot_id)
|
|
c_tcp = tcp_socket.tcp_client_stp(self.PROT_IPS[prot_id], self.PROT_PORTS[prot_id], rx_log_lvl=logging.DEBUG)
|
|
self.__prot__[prot_id] = self.PROT_CLASSES[prot_id](c_tcp, secret=self.PROT_SECRETS[prot_id], auto_auth=True)
|
|
#
|
|
self.__prot__[prot_id].register_callback(None, None, self.__prot_resp_callbacks__, prot_id)
|
|
|
|
def __initiate_data_request__(self, rt, request_msgs):
|
|
for prot_id in request_msgs:
|
|
for request_msg in request_msgs.get(prot_id, []):
|
|
service_id = request_msg['service_id']
|
|
data_id = request_msg['data_id']
|
|
if self.__prot__[prot_id].connection_established():
|
|
logger.debug('Sending data request for prot_id %d, service_id %d and data_id %d', prot_id, service_id, data_id)
|
|
self.__prot__[prot_id].send(service_id, data_id, None)
|
|
else:
|
|
wx.CallAfter(self.__no_data__, prot_id, service_id + 1, data_id)
|
|
|
|
def __task_1s_callback__(self, rt):
|
|
# request data from fast prot ids
|
|
self.__initiate_data_request__(rt, self.REQUEST_MSGS_FAST)
|
|
# set date and time
|
|
wx.CallAfter(self.update_time)
|
|
|
|
def __task_10s_callback__(self, rt):
|
|
# request data from slow prot ids
|
|
self.__initiate_data_request__(rt, self.REQUEST_MSGS_SLOW)
|
|
|
|
def __task_60s_callback__(self, rt):
|
|
# reconnect prots if needed
|
|
for prot_id in self.ALL_PROT_IDS:
|
|
if not self.__prot__[prot_id].connected():
|
|
logger.debug("Trying to reconnect prot_id %d", prot_id)
|
|
self.__prot__[prot_id].reconnect()
|
|
|
|
def __validate_response_data__(self, prot_id, service_id, data_id, data):
|
|
rv = False
|
|
if prot_id == self.PROT_ID_GARAGE:
|
|
if service_id == garage_protocol.my_base_protocol_tcp.SID_READ_RESPONSE and data_id == garage_protocol.my_base_protocol_tcp.GATE_POSITION:
|
|
rv = isinstance(data, numbers.Number)
|
|
elif service_id == garage_protocol.my_base_protocol_tcp.SID_EXECUTE_RESPONSE and data_id == garage_protocol.my_base_protocol_tcp.OPEN_CLOSE_GATE:
|
|
if data is True:
|
|
rv = True
|
|
elif prot_id in [self.PROT_ID_IN, self.PROT_ID_OUT]:
|
|
if service_id == wetation_protocol.my_base_protocol_tcp.SID_READ_RESPONSE and data_id == wetation_protocol.my_base_protocol_tcp.CURRENT_ENVDATA:
|
|
rv = isinstance(data.get(dht_22.KEY_TEMPERATURE), numbers.Number) and isinstance(data.get(dht_22.KEY_HUMIDITY), numbers.Number) and isinstance(data.get(bmp_180.KEY_PRESSURE), numbers.Number)
|
|
if rv is False:
|
|
logger.warning("Validation failed for message: prot_id=%s, service_id=%s, data_id=%s, data=%s", repr(prot_id), repr(service_id), repr(data_id), repr(data))
|
|
return rv
|
|
|
|
def __prot_resp_callbacks__(self, msg, prot_id):
|
|
service_id = msg.get_service_id()
|
|
data_id = msg.get_data_id()
|
|
data = msg.get_data()
|
|
if self.__validate_response_data__(prot_id, service_id, data_id, data):
|
|
wx.CallAfter(self.__data__, prot_id, service_id, data_id, data)
|
|
return wetation_protocol.my_base_protocol_tcp.STATUS_OKAY, None
|
|
else:
|
|
wx.CallAfter(self.__no_data__, prot_id, service_id, data_id)
|
|
return wetation_protocol.my_base_protocol_tcp.STATUS_SERVICE_OR_DATA_UNKNOWN, None
|
|
|
|
def __no_data__(self, prot_id, service_id, data_id):
|
|
if prot_id == self.PROT_ID_GARAGE:
|
|
if service_id == garage_protocol.my_base_protocol_tcp.SID_READ_RESPONSE and data_id == garage_protocol.my_base_protocol_tcp.GATE_POSITION:
|
|
logger.debug('Resetting GUI for prot_id %d, service_id=%d, data_id=%d', prot_id, service_id, data_id)
|
|
self.heading_garage.Show(False)
|
|
self.gate_oc.Show(False)
|
|
self.gate_open.Show(False)
|
|
self.gate_position.Show(False)
|
|
self.gate_close.Show(False)
|
|
self.Layout()
|
|
return
|
|
elif service_id == garage_protocol.my_base_protocol_tcp.SID_EXECUTE_RESPONSE and data_id == garage_protocol.my_base_protocol_tcp.OPEN_CLOSE_GATE:
|
|
return
|
|
elif prot_id in [self.PROT_ID_IN, self.PROT_ID_OUT]:
|
|
if service_id == wetation_protocol.my_base_protocol_tcp.SID_READ_RESPONSE and data_id == wetation_protocol.my_base_protocol_tcp.CURRENT_ENVDATA:
|
|
logger.debug('Resetting GUI for prot_id %d, service_id=%d, data_id=%d', prot_id, service_id, data_id)
|
|
txt_temperature = '-.- °C'
|
|
txt_humidity = '-.- %'
|
|
txt_pressure = '- hPa'
|
|
if prot_id == self.PROT_ID_IN:
|
|
self.in_temperature.SetLabel(txt_temperature)
|
|
self.in_humidity.SetLabel(txt_humidity)
|
|
self.in_pressure.SetLabel(txt_pressure)
|
|
else:
|
|
self.out_temperature.SetLabel(txt_temperature)
|
|
self.out_humidity.SetLabel(txt_humidity)
|
|
self.out_pressure.SetLabel(txt_pressure)
|
|
self.Layout()
|
|
return
|
|
logger.warning("Unknown response with no valid data for prot_id %d, service_id=%d, data_id=%d", prot_id, service_id, data_id)
|
|
|
|
def __data__(self, prot_id, service_id, data_id, data):
|
|
if prot_id == self.PROT_ID_GARAGE:
|
|
if service_id == garage_protocol.my_base_protocol_tcp.SID_READ_RESPONSE and data_id == garage_protocol.my_base_protocol_tcp.GATE_POSITION:
|
|
self.heading_garage.Show(True)
|
|
self.gate_oc.Show(True)
|
|
self.gate_open.Show(True)
|
|
self.gate_position.Show(True)
|
|
self.gate_close.Show(True)
|
|
self.gate_position.SetValue(int(data * 100))
|
|
self.Layout()
|
|
return garage_protocol.my_base_protocol_tcp.STATUS_OKAY, None
|
|
elif service_id == garage_protocol.my_base_protocol_tcp.SID_EXECUTE_RESPONSE and data_id == garage_protocol.my_base_protocol_tcp.OPEN_CLOSE_GATE:
|
|
return garage_protocol.my_base_protocol_tcp.STATUS_OKAY, None
|
|
elif prot_id in [self.PROT_ID_IN, self.PROT_ID_OUT]:
|
|
if service_id == wetation_protocol.my_base_protocol_tcp.SID_READ_RESPONSE and data_id == wetation_protocol.my_base_protocol_tcp.CURRENT_ENVDATA:
|
|
temp = data[dht_22.KEY_TEMPERATURE]
|
|
if prot_id == self.PROT_ID_IN:
|
|
if self.__max_temp_in__ is None or temp > self.__max_temp_in__:
|
|
self.__max_temp_in__ = temp
|
|
if self.__min_temp_in__ is None or temp < self.__min_temp_in__:
|
|
self.__min_temp_in__ = temp
|
|
wx.CallAfter(self.update_current_env_data, data, self.in_temperature, self.in_humidity, self.in_pressure, self.in_temperature_min, self.in_temperature_max, self.__min_temp_in__, self.__max_temp_in__)
|
|
else:
|
|
if self.__max_temp_out__ is None or temp > self.__max_temp_out__:
|
|
self.__max_temp_out__ = temp
|
|
if self.__min_temp_out__ is None or temp < self.__min_temp_out__:
|
|
self.__min_temp_out__ = temp
|
|
wx.CallAfter(self.update_current_env_data, data, self.out_temperature, self.out_humidity, self.out_pressure, self.out_temperature_min, self.out_temperature_max, self.__min_temp_out__, self.__max_temp_out__)
|
|
return wetation_protocol.my_base_protocol_tcp.STATUS_OKAY, None
|
|
logger.warning("Unknown response with valid data for prot_id %d, service_id=%d, data_id=%d", prot_id, service_id, data_id)
|
|
return wetation_protocol.my_base_protocol_tcp.STATUS_SERVICE_OR_DATA_UNKNOWN, None
|
|
|
|
def update_current_env_data(self, env_data, temperature, humidity, pressure, temperature_min, temperature_max, value_min, value_max):
|
|
if isinstance(value_min, numbers.Number) and isinstance(value_max, numbers.Number):
|
|
temperature_min.SetLabel('%.1f' % value_min)
|
|
temperature_max.SetLabel('%.1f' % value_max)
|
|
temperature.SetLabel('%.1f °C' % env_data[dht_22.KEY_TEMPERATURE])
|
|
humidity.SetLabel('%.1f %%' % env_data[dht_22.KEY_HUMIDITY])
|
|
pressure.SetLabel('%.0f hPa' % env_data[bmp_180.KEY_PRESSURE])
|
|
self.Layout()
|
|
|
|
def update_time(self):
|
|
self.time.SetLabel(time.strftime("%H:%M"))
|
|
self.date.SetLabel(time.strftime("%d.%m.%Y"))
|
|
self.Layout()
|
|
|
|
def gate_oc_evt(self, event):
|
|
logger.debug("Gate open/close request")
|
|
self.__prot__[self.PROT_ID_GARAGE].send(garage_protocol.my_base_protocol_tcp.SID_EXECUTE_REQUEST, garage_protocol.my_base_protocol_tcp.OPEN_CLOSE_GATE, None)
|
|
event.Skip()
|
|
|
|
def reset_in_temp_minmax_evt(self, event):
|
|
self.__min_temp_in__ = None
|
|
self.__max_temp_in__ = None
|
|
self.in_temperature_min.SetLabel("-.- °C")
|
|
self.in_temperature_max.SetLabel("-.- °C")
|
|
self.Layout()
|
|
event.Skip()
|
|
|
|
def reset_out_temp_minmax_evt(self, event):
|
|
self.__min_temp_out__ = None
|
|
self.__max_temp_out__ = None
|
|
self.out_temperature_min.SetLabel("-.- °C")
|
|
self.out_temperature_max.SetLabel("-.- °C")
|
|
self.Layout()
|
|
event.Skip()
|
|
|
|
def run(self):
|
|
self.__task_1s__.run()
|
|
self.__task_10s__.run()
|
|
self.__task_60s__.run()
|
|
|
|
def close(self):
|
|
self.__task_1s__.stop()
|
|
self.__task_1s__.join()
|
|
self.__task_10s__.stop()
|
|
self.__task_10s__.join()
|
|
self.__task_60s__.stop()
|
|
self.__task_60s__.join()
|
|
|
|
def __del__(self):
|
|
self.close()
|
|
|
|
|
|
class MyApp(wx.App):
|
|
def OnInit(self):
|
|
self.frame = WetationFrameProt(None, wx.ID_ANY, "")
|
|
self.SetTopWindow(self.frame)
|
|
self.frame.Show()
|
|
return True
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
report.appLoggingConfigure(os.path.dirname(__file__), config.LOGTARGET, config.loggers)
|
|
#
|
|
app = MyApp(0)
|
|
app.frame.run()
|
|
app.MainLoop()
|
|
app.frame.close()
|