123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- #!/usr/bin/env python
- # -*- coding: UTF-8 -*-
- #
-
- import config
- from rpi_envsens.dht import dht_22
- from rpi_envsens.bmp import bmp_180
- import helpers
- import garage_protocol
- import gui
- import logging
- import os
- import report
- import socket_protocol
- import task
- import tcp_socket
- import time
- import wetation_protocol
- import wx
-
- ############################################################################################################################
- # #
- # Application Structure #
- # #
- # +-----------------------------+ +------------------------------+ +---------------------------+ #
- # | Data Request Task (1s) | | Data Receive Callback | | GUI Update WX-IDLE-TASK | #
- # | Counter 0-9 | | | | | #
- # | | | Append data to ValueStorage | | - Update all GUI Elements | #
- # | *: Request door_pos | +------------------------------+ | - Check Data Status to | #
- # | 0: Request env-data-in bmp | | mark all fragile con- | #
- # | 1: Request env-data-in dht | | tent | #
- # | 5: Request env-data-out bmp | +---------------------------+ #
- # | 6: Request env-data-out dht | #
- # | 9: Reconnect if needed | #
- # +-----------------------------+ #
- # #
- ############################################################################################################################
-
-
- try:
- from config import APP_NAME as ROOT_LOGGER_NAME
- except ImportError:
- ROOT_LOGGER_NAME = 'root'
-
- logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
-
-
- class ValueStorage(dict):
- MAX_AGE = 60 # Seconds
-
- DATA_KEY_GATE_POSITION = 'gate_position'
- DATA_KEY_HUMIDITY_IN = 'humidity_in'
- DATA_KEY_HUMIDITY_OUT = 'humidity_out'
- DATA_KEY_PRESSURE_IN = 'pressure_in'
- DATA_KEY_PRESSURE_OUT = 'pressure_out'
- DATA_KEY_TEMPERATURE_IN = 'temp_in'
- DATA_KEY_TEMPERATURE_OUT = 'temp_out'
- DATA_KEY_2ND_TEMPERATURE_IN = '2nd_temp_in'
- DATA_KEY_2ND_TEMPERATURE_OUT = '2nd_temp_out'
-
- FORMATTER_TEMP = ('%.1f °C', '-.- °C')
- FORMATTER_HUMI = ('%.1f %%', '-.- %')
- FORMATTER_PRES = ('%4d hPa', '- hPa')
-
- DATA_KEYS_CONT_STAT = [DATA_KEY_HUMIDITY_IN, DATA_KEY_HUMIDITY_OUT, DATA_KEY_PRESSURE_IN, DATA_KEY_PRESSURE_OUT, DATA_KEY_TEMPERATURE_IN, DATA_KEY_TEMPERATURE_OUT, DATA_KEY_2ND_TEMPERATURE_IN, DATA_KEY_2ND_TEMPERATURE_OUT]
-
- def append(self, drk, value):
- tm = time.time()
- logger.info('Value stored (%s): %s', drk, repr(value))
- if drk in self.DATA_KEYS_CONT_STAT:
- self[drk] = (value.mean, tm)
- if drk + '.min' not in self or value.min < self[drk + '.min'][0]:
- self[drk + '.min'] = (value.min, tm)
- if drk + '.max' not in self or value.max < self[drk + '.max'][0]:
- self[drk + '.max'] = (value.max, tm)
- else:
- self[drk] = (value, tm)
-
- def reset_min_max(self, data_key):
- del self[data_key + '.min']
- del self[data_key + '.max']
-
- def __get_formatter__(self, key):
- return {
- self.DATA_KEY_2ND_TEMPERATURE_IN: self.FORMATTER_TEMP,
- self.DATA_KEY_2ND_TEMPERATURE_OUT: self.FORMATTER_TEMP,
- self.DATA_KEY_HUMIDITY_IN: self.FORMATTER_HUMI,
- self.DATA_KEY_HUMIDITY_OUT: self.FORMATTER_HUMI,
- self.DATA_KEY_PRESSURE_IN: self.FORMATTER_PRES,
- self.DATA_KEY_PRESSURE_OUT: self.FORMATTER_PRES,
- self.DATA_KEY_TEMPERATURE_IN: self.FORMATTER_TEMP,
- self.DATA_KEY_TEMPERATURE_OUT: self.FORMATTER_TEMP,
- }.get(key)
-
- def get_validated(self, key, value_type=None, default=None):
- tm = time.time()
- fallback = {
- self.DATA_KEY_TEMPERATURE_IN: self.DATA_KEY_2ND_TEMPERATURE_IN,
- self.DATA_KEY_TEMPERATURE_OUT: self.DATA_KEY_2ND_TEMPERATURE_OUT,
- }.get(key)
- if value_type in ['min', 'max']:
- key = key + '.' + value_type
- value = dict.get(self, key)
- if value is None and fallback is not None:
- value = dict.get(self, fallback)
- if value is None:
- return default
- if tm - value[1] <= self.MAX_AGE:
- if key == self.DATA_KEY_GATE_POSITION:
- return int(value[0] * 50)
- else:
- return value[0]
- else:
- return default
-
- def get(self, key, value_type=None, formatted=True, default=None):
- formatter = self.__get_formatter__(key)
- value = self.get_validated(key, value_type, default=default)
- if not formatted:
- return value
- else:
- if formatter is None:
- return default
- else:
- try:
- return formatter[0] % value
- except Exception:
- return formatter[1]
-
-
- class Wetation(gui.Wetation):
- SRC_ID_GARAGE = 0
- SRC_ID_IN = 1
- SRC_ID_OUT = 2
- ALL_SRC_IDS = [SRC_ID_GARAGE, SRC_ID_IN, SRC_ID_OUT, ]
-
- SRC_NAMES = {
- SRC_ID_GARAGE: u'garage',
- SRC_ID_IN: u'wet-in',
- SRC_ID_OUT: u'wet-out',
- }
-
- SRC_IPS = {
- SRC_ID_GARAGE: config.server_garage_ip,
- SRC_ID_IN: config.server_in_ip,
- SRC_ID_OUT: config.server_out_ip,
- }
-
- SRC_PORTS = {
- SRC_ID_GARAGE: config.server_garage_port,
- SRC_ID_IN: config.server_in_port,
- SRC_ID_OUT: config.server_out_port,
- }
-
- SRC_SECRETS = {
- SRC_ID_GARAGE: config.server_garage_secret,
- SRC_ID_IN: config.server_in_secret,
- SRC_ID_OUT: config.server_out_secret,
- }
-
- SRC_CLASSES = {
- SRC_ID_GARAGE: garage_protocol.my_base_protocol_tcp,
- SRC_ID_IN: wetation_protocol.my_base_protocol_tcp,
- SRC_ID_OUT: wetation_protocol.my_base_protocol_tcp,
- }
-
- def __init__(self, *args, **kwds):
-
- gui.Wetation.__init__(self, *args, **kwds)
- self.Bind(wx.EVT_IDLE, self.gui_update)
- 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.__src__ = {}
- self.__value_storage__ = ValueStorage()
- self.__init_communication__()
- #
- self.__drt_cnt__ = -1
- self.__task_1s__ = task.periodic(1, self.data_request_task)
-
- def __init_communication__(self):
- #
- # Start TCP-Clients
- for src_id in self.ALL_SRC_IDS:
- cn = self.SRC_NAMES[src_id]
- c_tcp = tcp_socket.tcp_client_stp(self.SRC_IPS[src_id], self.SRC_PORTS[src_id], channel_name=cn)
- self.__src__[src_id] = self.SRC_CLASSES[src_id](c_tcp, secret=self.SRC_SECRETS[src_id], auto_auth=True, channel_name=cn)
- #
- self.__src__[src_id].register_callback(None, None, self.data_receive_callback, src_id)
-
- def gate_oc_evt(self, event):
- r = wx.MessageDialog(
- self,
- "Soll das Garagentor betätigt werden?",
- "Garage",
- wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING
- ).ShowModal()
- if r == wx.ID_YES:
- logger.info("Gate open/close request")
- self.__src__[self.SRC_ID_GARAGE].send(socket_protocol.SID_EXECUTE_REQUEST, garage_protocol.OPEN_CLOSE_GATE, None)
- event.Skip()
-
- def reset_in_temp_minmax_evt(self, event):
- self.__value_storage__.reset_min_max(ValueStorage.DATA_KEY_TEMPERATURE_IN)
- self.__value_storage__.reset_min_max(ValueStorage.DATA_KEY_2ND_TEMPERATURE_IN)
- event.Skip()
-
- def reset_out_temp_minmax_evt(self, event):
- self.__value_storage__.reset_min_max(ValueStorage.DATA_KEY_TEMPERATURE_OUT)
- self.__value_storage__.reset_min_max(ValueStorage.DATA_KEY_2ND_TEMPERATURE_OUT)
- event.Skip()
-
- def data_request_task(self, rt):
- (rt)
- if self.__drt_cnt__ < 9:
- self.__drt_cnt__ += 1
- else:
- self.__drt_cnt__ = 0
- logger.debug('data_request_task stated with cnt = %d', self.__drt_cnt__)
- #
- # Request GATE_POSITION
- #
- self.__src__[self.SRC_ID_GARAGE].send(service_id=socket_protocol.SID_READ_REQUEST, data_id=garage_protocol.GATE_POSITION, data=None)
- #
- # Request WET-OUT-DATA
- #
- if self.__drt_cnt__ == 0:
- self.__src__[self.SRC_ID_OUT].send(service_id=socket_protocol.SID_READ_REQUEST, data_id=wetation_protocol.ENVDATA_STATISTIC_BMP, data=None)
- elif self.__drt_cnt__ == 1:
- self.__src__[self.SRC_ID_OUT].send(service_id=socket_protocol.SID_READ_REQUEST, data_id=wetation_protocol.ENVDATA_STATISTIC_DHT, data=None)
- #
- # Request WET-IN-DATA
- #
- elif self.__drt_cnt__ == 5:
- self.__src__[self.SRC_ID_IN].send(service_id=socket_protocol.SID_READ_REQUEST, data_id=wetation_protocol.ENVDATA_STATISTIC_BMP, data=None)
- elif self.__drt_cnt__ == 6:
- self.__src__[self.SRC_ID_IN].send(service_id=socket_protocol.SID_READ_REQUEST, data_id=wetation_protocol.ENVDATA_STATISTIC_DHT, data=None)
- elif self.__drt_cnt__ == 9:
- self.__reconnect__()
- #
-
- def __reconnect__(self):
- for src_id in self.ALL_SRC_IDS:
- if not self.__src__[src_id].is_connected():
- logger.warning("Trying to reconnect prot_id %s", self.SRC_NAMES.get(src_id, 'Unknown'))
- self.__src__[src_id].reconnect()
-
- def __data_receive_key__(self, src_id, data_id, key=None, default_value=None):
- return {
- self.SRC_ID_GARAGE: {
- garage_protocol.GATE_POSITION: {
- None: ValueStorage.DATA_KEY_GATE_POSITION,
- }
- },
- self.SRC_ID_IN: {
- wetation_protocol.ENVDATA_STATISTIC_BMP: {
- bmp_180.KEY_PRESSURE: ValueStorage.DATA_KEY_PRESSURE_IN,
- bmp_180.KEY_TEMPERATURE: ValueStorage.DATA_KEY_2ND_TEMPERATURE_IN
- },
- wetation_protocol.ENVDATA_STATISTIC_DHT: {
- dht_22.KEY_HUMIDITY: ValueStorage.DATA_KEY_HUMIDITY_IN,
- dht_22.KEY_TEMPERATURE: ValueStorage.DATA_KEY_TEMPERATURE_IN
- },
- },
- self.SRC_ID_OUT: {
- wetation_protocol.ENVDATA_STATISTIC_BMP: {
- bmp_180.KEY_PRESSURE: ValueStorage.DATA_KEY_PRESSURE_OUT,
- bmp_180.KEY_TEMPERATURE: ValueStorage.DATA_KEY_2ND_TEMPERATURE_OUT
- },
- wetation_protocol.ENVDATA_STATISTIC_DHT: {
- dht_22.KEY_HUMIDITY: ValueStorage.DATA_KEY_HUMIDITY_OUT,
- dht_22.KEY_TEMPERATURE: ValueStorage.DATA_KEY_TEMPERATURE_OUT
- },
- }
- }.get(src_id, {}).get(data_id, {}).get(key, default_value)
-
- def data_receive_callback(self, data, src_id):
- if src_id in [self.SRC_ID_IN, self.SRC_ID_OUT] and data.get_data_id() in [wetation_protocol.ENVDATA_STATISTIC_DHT, wetation_protocol.ENVDATA_STATISTIC_BMP]:
- try:
- for key in data.get_data():
- if key != bmp_180.KEY_TIME:
- drk = self.__data_receive_key__(src_id, data.get_data_id(), key)
- if drk is not None:
- self.__process_rx_data__(
- drk,
- helpers.continues_statistic(**data.get_data()[key])
- )
- else:
- self.__warning_rx_data__('Unknown RX-Data', data, src_id)
- except TypeError as e:
- self.__warning_rx_data__('Wront DataType in RX-Data (%s)' % e, data, src_id)
- else:
- drk = self.__data_receive_key__(src_id, data.get_data_id())
- if drk is not None:
- self.__process_rx_data__(
- drk,
- data.get_data()
- )
- else:
- self.__warning_rx_data__('Unknown RX-Data', data, src_id)
-
- def __process_rx_data__(self, drk, data):
- self.__value_storage__.append(drk, data)
-
- def __warning_rx_data__(self, desc, data, src_id):
- logger.warning('%s from %s: %s', desc, self.SRC_NAMES.get(src_id, 'Unknown'), repr(data))
-
- def __check_data_status__(self):
- if dict.get(self.__value_storage__, ValueStorage.DATA_KEY_TEMPERATURE_IN) is None and dict.get(self.__value_storage__, ValueStorage.DATA_KEY_2ND_TEMPERATURE_IN) is not None:
- self.in_temperature.SetBackgroundColour((255, 255, 200, 255))
- else:
- self.in_temperature.SetBackgroundColour((250, 249, 255, 255))
- if dict.get(self.__value_storage__, ValueStorage.DATA_KEY_TEMPERATURE_OUT) is None and dict.get(self.__value_storage__, ValueStorage.DATA_KEY_2ND_TEMPERATURE_OUT) is not None:
- self.out_temperature.SetBackgroundColour((255, 255, 200, 255))
- else:
- self.out_temperature.SetBackgroundColour((250, 249, 255, 255))
- if dict.get(self.__value_storage__, ValueStorage.DATA_KEY_GATE_POSITION) is None:
- self.heading_garage.SetBackgroundColour((142, 35, 35, 255))
- else:
- self.heading_garage.SetBackgroundColour((35, 35, 142, 255))
-
- def gui_update(self, event):
- #
- # TIME and DATE
- #
- self.time.SetLabel(time.strftime("%H:%M"))
- self.date.SetLabel(time.strftime("%d.%m.%Y"))
- #
- update_list = [
- # WET-OUT
- (self.out_humidity.SetLabel, [ValueStorage.DATA_KEY_HUMIDITY_OUT, 'mean', True]),
- (self.out_pressure.SetLabel, [ValueStorage.DATA_KEY_PRESSURE_OUT, 'mean', True]),
- (self.out_temperature.SetLabel, [ValueStorage.DATA_KEY_TEMPERATURE_OUT, 'mean', True]),
- (self.out_temperature_max.SetLabel, [ValueStorage.DATA_KEY_TEMPERATURE_OUT, 'max', True]),
- (self.out_temperature_min.SetLabel, [ValueStorage.DATA_KEY_TEMPERATURE_OUT, 'min', True]),
- # WET-IN
- (self.in_humidity.SetLabel, [ValueStorage.DATA_KEY_HUMIDITY_IN, 'mean', True]),
- (self.in_pressure.SetLabel, [ValueStorage.DATA_KEY_PRESSURE_IN, 'mean', True]),
- (self.in_temperature.SetLabel, [ValueStorage.DATA_KEY_TEMPERATURE_IN, 'mean', True]),
- (self.in_temperature_max.SetLabel, [ValueStorage.DATA_KEY_TEMPERATURE_IN, 'max', True]),
- (self.in_temperature_min.SetLabel, [ValueStorage.DATA_KEY_TEMPERATURE_IN, 'min', True]),
- # GATE
- (self.gate_position.SetValue, [ValueStorage.DATA_KEY_GATE_POSITION, None, False, 50])
- ]
- #
- for func, args in update_list:
- func(self.__value_storage__.get(*args))
- self.__check_data_status__()
- #
- #
- self.Layout()
- event.Skip()
-
- def run(self):
- self.__task_1s__.run()
-
- def close(self):
- self.__task_1s__.stop()
- self.__task_1s__.join()
-
- def __del__(self):
- self.close()
-
-
- class MyApp(wx.App):
- def OnInit(self):
- self.frame = Wetation(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.APP_NAME, config.LOGLVL), ), fmt=config.formatter, host=config.LOGHOST, port=config.LOGPORT)
- #
- app = MyApp(0)
- app.frame.run()
- app.MainLoop()
- app.frame.close()
|