Selaa lähdekoodia

Application Structure reworked

master
Dirk Alders 3 vuotta sitten
vanhempi
commit
dad7b08a98
3 muutettua tiedostoa jossa 292 lisäystä ja 287 poistoa
  1. 1
    1
      report
  2. 290
    285
      smarthome.py
  3. 1
    1
      socket_protocol

+ 1
- 1
report

@@ -1 +1 @@
1
-Subproject commit 25889f225b3593d515e37bebffef21458c961f64
1
+Subproject commit 139f0bd0c29b8b44e235622f7fd8db7dc0037ec0

+ 290
- 285
smarthome.py Näytä tiedosto

@@ -9,7 +9,6 @@ import helpers
9 9
 import garage_protocol
10 10
 import gui
11 11
 import logging
12
-import numbers
13 12
 import os
14 13
 import report
15 14
 import socket_protocol
@@ -19,301 +18,180 @@ import time
19 18
 import wetation_protocol
20 19
 import wx
21 20
 
21
+############################################################################################################################
22
+#                                                                                                                          #
23
+# Application Structure                                                                                                    #
24
+#                                                                                                                          #
25
+#  +-----------------------------+   +------------------------------+   +---------------------------+                      #
26
+#  | Data Request Task (1s)      |   | Data Receive Callback        |   | GUI Update WX-IDLE-TASK   |                      #
27
+#  | Counter 0-9                 |   |                              |   |                           |                      #
28
+#  |                             |   | Append data to ValueStorage  |   | - Update all GUI Elements |                      #
29
+#  | *: Request door_pos         |   +------------------------------+   | - Check Data Status to    |                      #
30
+#  | 0: Request env-data-in bmp  |                                      |   mark all fragile con-   |                      #
31
+#  | 1: Request env-data-in dht  |                                      |   tent                    |                      #
32
+#  | 5: Request env-data-out bmp |                                      +---------------------------+                      #
33
+#  | 6: Request env-data-out dht |                                                                                         #
34
+#  | 9: Reconnect if needed      |                                                                                         #
35
+#  +-----------------------------+                                                                                         #
36
+#                                                                                                                          #
37
+############################################################################################################################
38
+
39
+
22 40
 try:
23 41
     from config import APP_NAME as ROOT_LOGGER_NAME
24 42
 except ImportError:
25 43
     ROOT_LOGGER_NAME = 'root'
26 44
 
45
+logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__)
46
+
47
+
48
+class ValueStorage(dict):
49
+    MAX_AGE = 60        # Seconds
50
+
51
+    DATA_KEY_GATE_POSITION = 'gate_position'
52
+    DATA_KEY_HUMIDITY_IN = 'humidity_in'
53
+    DATA_KEY_HUMIDITY_OUT = 'humidity_out'
54
+    DATA_KEY_PRESSURE_IN = 'pressure_in'
55
+    DATA_KEY_PRESSURE_OUT = 'pressure_out'
56
+    DATA_KEY_TEMPERATURE_IN = 'temp_in'
57
+    DATA_KEY_TEMPERATURE_OUT = 'temp_out'
58
+    DATA_KEY_2ND_TEMPERATURE_IN = '2nd_temp_in'
59
+    DATA_KEY_2ND_TEMPERATURE_OUT = '2nd_temp_out'
60
+
61
+    FORMATTER_TEMP = ('%.1f °C', '-.- °C')
62
+    FORMATTER_HUMI = ('%.1f %%', '-.- %')
63
+    FORMATTER_PRES = ('%4d hPa', '- hPa')
64
+
65
+    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]
66
+
67
+    def append(self, drk, value):
68
+        tm = time.time()
69
+        logger.info('Value stored (%s): %s', drk, repr(value))
70
+        if drk in self.DATA_KEYS_CONT_STAT:
71
+            self[drk] = (value.mean, tm)
72
+            if drk + '.min' not in self or value.min < self[drk + '.min'][0]:
73
+                self[drk + '.min'] = (value.min, tm)
74
+            if drk + '.max' not in self or value.max < self[drk + '.max'][0]:
75
+                self[drk + '.max'] = (value.max, tm)
76
+        else:
77
+            self[drk] = (value, tm)
78
+
79
+    def reset_min_max(self, data_key):
80
+        del self[data_key + '.min']
81
+        del self[data_key + '.max']
82
+
83
+    def __get_formatter__(self, key):
84
+        return {
85
+            self.DATA_KEY_2ND_TEMPERATURE_IN: self.FORMATTER_TEMP,
86
+            self.DATA_KEY_2ND_TEMPERATURE_OUT: self.FORMATTER_TEMP,
87
+            self.DATA_KEY_HUMIDITY_IN: self.FORMATTER_HUMI,
88
+            self.DATA_KEY_HUMIDITY_OUT: self.FORMATTER_HUMI,
89
+            self.DATA_KEY_PRESSURE_IN: self.FORMATTER_PRES,
90
+            self.DATA_KEY_PRESSURE_OUT: self.FORMATTER_PRES,
91
+            self.DATA_KEY_TEMPERATURE_IN: self.FORMATTER_TEMP,
92
+            self.DATA_KEY_TEMPERATURE_OUT: self.FORMATTER_TEMP,
93
+        }.get(key)
94
+
95
+    def get_validated(self, key, value_type=None, default=None):
96
+        tm = time.time()
97
+        fallback = {
98
+            self.DATA_KEY_TEMPERATURE_IN: self.DATA_KEY_2ND_TEMPERATURE_IN,
99
+            self.DATA_KEY_TEMPERATURE_OUT: self.DATA_KEY_2ND_TEMPERATURE_OUT,
100
+        }.get(key)
101
+        if value_type in ['min', 'max']:
102
+            key = key + '.' + value_type
103
+        value = dict.get(self, key)
104
+        if value is None and fallback is not None:
105
+            value = dict.get(self, fallback)
106
+        if value is None:
107
+            return default
108
+        if tm - value[1] <= self.MAX_AGE:
109
+            if key == self.DATA_KEY_GATE_POSITION:
110
+                return int(value[0] * 50)
111
+            else:
112
+                return value[0]
113
+        else:
114
+            return default
27 115
 
28
-class WetationFrameProt(gui.Wetation):
29
-    PROT_ID_GARAGE = 0
30
-    PROT_ID_IN = 1
31
-    PROT_ID_OUT = 2
32
-    ALL_PROT_IDS = [PROT_ID_GARAGE, PROT_ID_IN, PROT_ID_OUT, ]
33
-
34
-    PROT_NAMES = {
35
-        PROT_ID_GARAGE: u'garage',
36
-        PROT_ID_IN: u'wet-in',
37
-        PROT_ID_OUT: u'wet-out',
116
+    def get(self, key, value_type=None, formatted=True, default=None):
117
+        formatter = self.__get_formatter__(key)
118
+        value = self.get_validated(key, value_type, default=default)
119
+        if not formatted:
120
+            return value
121
+        else:
122
+            if formatter is None:
123
+                return default
124
+            else:
125
+                try:
126
+                    return formatter[0] % value
127
+                except Exception:
128
+                    return formatter[1]
129
+
130
+
131
+class Wetation(gui.Wetation):
132
+    SRC_ID_GARAGE = 0
133
+    SRC_ID_IN = 1
134
+    SRC_ID_OUT = 2
135
+    ALL_SRC_IDS = [SRC_ID_GARAGE, SRC_ID_IN, SRC_ID_OUT, ]
136
+
137
+    SRC_NAMES = {
138
+        SRC_ID_GARAGE: u'garage',
139
+        SRC_ID_IN: u'wet-in',
140
+        SRC_ID_OUT: u'wet-out',
38 141
     }
39 142
 
40
-    PROT_IPS = {
41
-        PROT_ID_GARAGE: config.server_garage_ip,
42
-        PROT_ID_IN: config.server_in_ip,
43
-        PROT_ID_OUT: config.server_out_ip,
143
+    SRC_IPS = {
144
+        SRC_ID_GARAGE: config.server_garage_ip,
145
+        SRC_ID_IN: config.server_in_ip,
146
+        SRC_ID_OUT: config.server_out_ip,
44 147
     }
45 148
 
46
-    PROT_PORTS = {
47
-        PROT_ID_GARAGE: config.server_garage_port,
48
-        PROT_ID_IN: config.server_in_port,
49
-        PROT_ID_OUT: config.server_out_port,
149
+    SRC_PORTS = {
150
+        SRC_ID_GARAGE: config.server_garage_port,
151
+        SRC_ID_IN: config.server_in_port,
152
+        SRC_ID_OUT: config.server_out_port,
50 153
     }
51 154
 
52
-    PROT_SECRETS = {
53
-        PROT_ID_GARAGE: config.server_garage_secret,
54
-        PROT_ID_IN: config.server_in_secret,
55
-        PROT_ID_OUT: config.server_out_secret,
56
-    }
57
-    
58
-    PROT_CLASSES = {
59
-        PROT_ID_GARAGE: garage_protocol.my_base_protocol_tcp,
60
-        PROT_ID_IN: wetation_protocol.my_base_protocol_tcp,
61
-        PROT_ID_OUT: wetation_protocol.my_base_protocol_tcp,
155
+    SRC_SECRETS = {
156
+        SRC_ID_GARAGE: config.server_garage_secret,
157
+        SRC_ID_IN: config.server_in_secret,
158
+        SRC_ID_OUT: config.server_out_secret,
62 159
     }
63 160
 
64
-    REQUEST_MSGS_SLOW = {
65
-        PROT_ID_IN: [
66
-            {
67
-                'service_id': socket_protocol.SID_READ_REQUEST,
68
-                'data_id': wetation_protocol.ENVDATA_STATISTIC_DHT,
69
-            },
70
-            {
71
-                'service_id': socket_protocol.SID_READ_REQUEST,
72
-                'data_id': wetation_protocol.ENVDATA_STATISTIC_BMP,
73
-            },
74
-        ],
75
-        PROT_ID_OUT: [
76
-            {
77
-                'service_id': socket_protocol.SID_READ_REQUEST,
78
-                'data_id': wetation_protocol.ENVDATA_STATISTIC_DHT,
79
-            },
80
-            {
81
-                'service_id': socket_protocol.SID_READ_REQUEST,
82
-                'data_id': wetation_protocol.ENVDATA_STATISTIC_BMP,
83
-            },
84
-        ],
161
+    SRC_CLASSES = {
162
+        SRC_ID_GARAGE: garage_protocol.my_base_protocol_tcp,
163
+        SRC_ID_IN: wetation_protocol.my_base_protocol_tcp,
164
+        SRC_ID_OUT: wetation_protocol.my_base_protocol_tcp,
85 165
     }
86 166
 
87
-    REQUEST_MSGS_FAST = {
88
-        PROT_ID_GARAGE: [
89
-            {
90
-                'service_id': socket_protocol.SID_READ_REQUEST,
91
-                'data_id': garage_protocol.GATE_POSITION,
92
-            },
93
-        ],
94
-    }
95
-
96
-
97 167
     def __init__(self, *args, **kwds):
98
-        self.__min_temp_in__ = None
99
-        self.__min_temp_out__ = None
100
-        self.__max_temp_in__ = None
101
-        self.__max_temp_out__ = None
102
-        self.__prot__ = {}
168
+
103 169
         gui.Wetation.__init__(self, *args, **kwds)
170
+        self.Bind(wx.EVT_IDLE, self.gui_update)
104 171
         self.in_temperature_min.Bind(wx.EVT_LEFT_DOWN, self.reset_in_temp_minmax_evt)
105 172
         self.in_temperature_max.Bind(wx.EVT_LEFT_DOWN, self.reset_in_temp_minmax_evt)
106 173
         self.out_temperature_min.Bind(wx.EVT_LEFT_DOWN, self.reset_out_temp_minmax_evt)
107 174
         self.out_temperature_max.Bind(wx.EVT_LEFT_DOWN, self.reset_out_temp_minmax_evt)
108 175
         self.ShowFullScreen(config.FULL_SCREEN)
109
-
176
+        #
177
+        self.__src__ = {}
178
+        self.__value_storage__ = ValueStorage()
110 179
         self.__init_communication__()
111
-        time.sleep(3.5)   # Wait for established connections before starting the tasks
112
-
113
-        self.__task_1s__ = task.periodic(1, self.__task_1s_callback__)
114
-        self.__task_10s__ = task.periodic(10, self.__task_10s_callback__)
115
-        self.__task_60s__ = task.periodic(60, self.__task_60s_callback__)
180
+        #
181
+        self.__drt_cnt__ = -1
182
+        self.__task_1s__ = task.periodic(1, self.data_request_task)
116 183
 
117 184
     def __init_communication__(self):
118 185
         #
119 186
         # Start TCP-Clients
120
-        for prot_id in self.ALL_PROT_IDS:
121
-            cn = self.PROT_NAMES[prot_id]
122
-            logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__ + '.' + cn)
123
-            logger.info('Initiating communication channel')
124
-            c_tcp = tcp_socket.tcp_client_stp(self.PROT_IPS[prot_id], self.PROT_PORTS[prot_id])
125
-            self.__prot__[prot_id] = self.PROT_CLASSES[prot_id](c_tcp, secret=self.PROT_SECRETS[prot_id], auto_auth=True, channel_name=cn)
187
+        for src_id in self.ALL_SRC_IDS:
188
+            cn = self.SRC_NAMES[src_id]
189
+            c_tcp = tcp_socket.tcp_client_stp(self.SRC_IPS[src_id], self.SRC_PORTS[src_id], channel_name=cn)
190
+            self.__src__[src_id] = self.SRC_CLASSES[src_id](c_tcp, secret=self.SRC_SECRETS[src_id], auto_auth=True, channel_name=cn)
126 191
             #
127
-            self.__prot__[prot_id].register_callback(None, None, self.__prot_resp_callbacks__, prot_id)
128
-
129
-    def __initiate_data_request__(self, rt, request_msgs):
130
-        for prot_id in request_msgs:
131
-            cn = self.PROT_NAMES[prot_id]
132
-            logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__ + '.' + cn)
133
-            for request_msg in request_msgs.get(prot_id, []):
134
-                service_id = request_msg['service_id']
135
-                data_id = request_msg['data_id']
136
-                if self.__prot__[prot_id].connection_established():
137
-                    logger.debug('Sending data request for service_id %d and data_id %d', service_id, data_id)
138
-                    self.__prot__[prot_id].send(service_id, data_id, None)
139
-                else:
140
-                    wx.CallAfter(self.__no_data__, prot_id, service_id + 1, data_id)
141
-
142
-    def __task_1s_callback__(self, rt):
143
-        # request data from fast prot ids
144
-        self.__initiate_data_request__(rt, self.REQUEST_MSGS_FAST)
145
-        # set date and time
146
-        wx.CallAfter(self.update_time)
147
-
148
-    def __task_10s_callback__(self, rt):
149
-        # request data from slow prot ids
150
-        self.__initiate_data_request__(rt, self.REQUEST_MSGS_SLOW)
151
-
152
-    def __task_60s_callback__(self, rt):
153
-        # reconnect prots if needed
154
-        for prot_id in self.ALL_PROT_IDS:
155
-            cn = self.PROT_NAMES[prot_id]
156
-            logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__ + '.' + cn)
157
-            if not self.__prot__[prot_id].connected():
158
-                logger.warning("Trying to reconnect prot_id %d", prot_id)
159
-                self.__prot__[prot_id].reconnect()
160
-
161
-    def __validate_response_data__(self, prot_id, service_id, data_id, data):
162
-        rv = False
163
-        cn = self.PROT_NAMES[prot_id]
164
-        logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__ + '.' + cn)
165
-        if prot_id == self.PROT_ID_GARAGE:
166
-            if service_id == socket_protocol.SID_READ_RESPONSE and data_id == garage_protocol.GATE_POSITION:
167
-                rv = isinstance(data, numbers.Number)
168
-            elif service_id == socket_protocol.SID_EXECUTE_RESPONSE and data_id == garage_protocol.OPEN_CLOSE_GATE:
169
-                if data is True:
170
-                    rv = True
171
-        elif prot_id in [self.PROT_ID_IN, self.PROT_ID_OUT]:
172
-            if service_id == socket_protocol.SID_READ_RESPONSE and data_id == wetation_protocol.ENVDATA_STATISTIC_BMP:
173
-                rv = True
174
-                for main_key in [bmp_180.KEY_PRESSURE, bmp_180.KEY_TEMPERATURE, bmp_180.KEY_TIME]:
175
-                    if main_key not in data:
176
-                        rv = False
177
-                        break
178
-                    else:
179
-                        for sub_key in ['mean', 'min_val', 'max_val', 'quantifier']:
180
-                            if not isinstance(data[main_key].get(sub_key), numbers.Number):
181
-                                rv = False
182
-                                break
183
-            elif service_id == socket_protocol.SID_READ_RESPONSE and data_id == wetation_protocol.ENVDATA_STATISTIC_DHT:
184
-                rv = True
185
-                for main_key in [dht_22.KEY_HUMIDITY, dht_22.KEY_TEMPERATURE, dht_22.KEY_TIME]:
186
-                    if main_key not in data:
187
-                        rv = False
188
-                        break
189
-                    else:
190
-                        for sub_key in ['mean', 'min_val', 'max_val', 'quantifier']:
191
-                            if not isinstance(data[main_key].get(sub_key), numbers.Number):
192
-                                rv = False
193
-                                break
194
-        if rv is False:
195
-            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))
196
-        return rv
197
-
198
-    def __prot_resp_callbacks__(self, msg, prot_id):
199
-        service_id = msg.get_service_id()
200
-        data_id = msg.get_data_id()
201
-        data = msg.get_data()
202
-        if self.__validate_response_data__(prot_id, service_id, data_id, data):
203
-            wx.CallAfter(self.__data__, prot_id, service_id, data_id, data)
204
-            return socket_protocol.STATUS_OKAY, None
205
-        else:
206
-            wx.CallAfter(self.__no_data__, prot_id, service_id, data_id)
207
-            return socket_protocol.STATUS_SERVICE_OR_DATA_UNKNOWN, None
208
-
209
-    def __no_data__(self, prot_id, service_id, data_id):
210
-        cn = self.PROT_NAMES[prot_id]
211
-        logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__ + '.' + cn)
212
-        if prot_id == self.PROT_ID_GARAGE:
213
-            if service_id == socket_protocol.SID_READ_RESPONSE and data_id == garage_protocol.GATE_POSITION:
214
-                logger.warning('Resetting GUI elements for %s', cn)
215
-                self.heading_garage.Show(False)
216
-                self.gate_oc.Show(False)
217
-                self.gate_open.Show(False)
218
-                self.gate_position.Show(False)
219
-                self.gate_close.Show(False)
220
-                self.Layout()
221
-                return
222
-            elif service_id == socket_protocol.SID_EXECUTE_RESPONSE and data_id == garage_protocol.OPEN_CLOSE_GATE:
223
-                return
224
-        elif prot_id in [self.PROT_ID_IN, self.PROT_ID_OUT]:
225
-            if service_id == socket_protocol.SID_READ_RESPONSE and data_id == wetation_protocol.ENVDATA_STATISTIC_BMP:
226
-                logger.warning('Resetting GUI elements for %s', cn)
227
-                txt_pressure = '- hPa'
228
-                if prot_id == self.PROT_ID_IN:
229
-                    self.in_pressure.SetLabel(txt_pressure)
230
-                else:
231
-                    self.out_pressure.SetLabel(txt_pressure)
232
-                self.Layout()
233
-                return
234
-            elif service_id == socket_protocol.SID_READ_RESPONSE and data_id == wetation_protocol.ENVDATA_STATISTIC_DHT:
235
-                logger.warning('Resetting GUI elements for %s', cn)
236
-                txt_temperature = '-.- °C'
237
-                txt_humidity = '-.- %'
238
-                if prot_id == self.PROT_ID_IN:
239
-                    self.in_temperature.SetLabel(txt_temperature)
240
-                    self.in_humidity.SetLabel(txt_humidity)
241
-                else:
242
-                    self.out_temperature.SetLabel(txt_temperature)
243
-                    self.out_humidity.SetLabel(txt_humidity)
244
-                self.Layout()
245
-                return
246
-        logger.warning("Unknown response with no valid data for prot_id %d, service_id=%d, data_id=%d", prot_id, service_id, data_id)
247
-
248
-    def __data__(self, prot_id, service_id, data_id, data):
249
-        cn = self.PROT_NAMES[prot_id]
250
-        logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__ + '.' + cn)
251
-        if prot_id == self.PROT_ID_GARAGE:
252
-            if service_id == socket_protocol.SID_READ_RESPONSE and data_id == garage_protocol.GATE_POSITION:
253
-                logger.debug('Setting %s position to %3d', cn, int(data * 100))
254
-                self.heading_garage.Show(True)
255
-                self.gate_oc.Show(True)
256
-                self.gate_open.Show(True)
257
-                self.gate_position.Show(True)
258
-                self.gate_close.Show(True)
259
-                self.gate_position.SetValue(int(data * 100))
260
-                self.Layout()
261
-                return
262
-            elif service_id == socket_protocol.SID_EXECUTE_RESPONSE and data_id == garage_protocol.OPEN_CLOSE_GATE:
263
-                return
264
-        elif prot_id in [self.PROT_ID_IN, self.PROT_ID_OUT]:
265
-            if service_id == socket_protocol.SID_READ_RESPONSE and data_id == wetation_protocol.ENVDATA_STATISTIC_BMP:
266
-                data = helpers.continues_statistic_multivalue(**data)
267
-                logger.debug('Setting %s pressure to %4d hPa', cn, data[bmp_180.KEY_PRESSURE].mean)
268
-                #
269
-                # Current environmental data
270
-                if prot_id == self.PROT_ID_IN:
271
-                    wx.CallAfter(self.update_current_bmp_env_data, data, self.in_pressure)
272
-                else:
273
-                    wx.CallAfter(self.update_current_bmp_env_data, data, self.out_pressure)
274
-                return
275
-            elif service_id == socket_protocol.SID_READ_RESPONSE and data_id == wetation_protocol.ENVDATA_STATISTIC_DHT:
276
-                data = helpers.continues_statistic_multivalue(**data)
277
-                #
278
-                # Current environmental data
279
-                temp = data[dht_22.KEY_TEMPERATURE].mean
280
-                logger.debug('Setting %s temperature to %4.1f °C', cn, temp)
281
-                logger.debug('Setting %s humidity to %3.1f %%', cn, data[dht_22.KEY_HUMIDITY].mean)
282
-                if prot_id == self.PROT_ID_IN:
283
-                    if self.__max_temp_in__ is None or temp > self.__max_temp_in__:
284
-                        self.__max_temp_in__ = temp
285
-                    if self.__min_temp_in__ is None or temp < self.__min_temp_in__:
286
-                        self.__min_temp_in__ = temp
287
-                    wx.CallAfter(self.update_current_dht_env_data, data, self.in_temperature, self.in_humidity, self.in_temperature_min, self.in_temperature_max, self.__min_temp_in__, self.__max_temp_in__)
288
-                else:
289
-                    if self.__max_temp_out__ is None or temp > self.__max_temp_out__:
290
-                        self.__max_temp_out__ = temp
291
-                    if self.__min_temp_out__ is None or temp < self.__min_temp_out__:
292
-                        self.__min_temp_out__ = temp
293
-                    wx.CallAfter(self.update_current_dht_env_data, data, self.out_temperature, self.out_humidity, self.out_temperature_min, self.out_temperature_max, self.__min_temp_out__, self.__max_temp_out__)
294
-                return
295
-        logger.warning("Unknown response with valid data for prot_id %d, service_id=%d, data_id=%d", prot_id, service_id, data_id)
296
-
297
-    def update_current_bmp_env_data(self, env_data, pressure):
298
-        pressure.SetLabel('%.0f hPa' % env_data[bmp_180.KEY_PRESSURE].mean)
299
-        self.Layout()
300
-
301
-    def update_current_dht_env_data(self, env_data, temperature, humidity, temperature_min, temperature_max, value_min, value_max):
302
-        if isinstance(value_min, numbers.Number) and isinstance(value_max, numbers.Number):
303
-            temperature_min.SetLabel('%.1f' % value_min)
304
-            temperature_max.SetLabel('%.1f' % value_max)
305
-        temperature.SetLabel('%.1f °C' % env_data[dht_22.KEY_TEMPERATURE].mean)
306
-        humidity.SetLabel('%.1f %%' % env_data[dht_22.KEY_HUMIDITY].mean)
307
-        self.Layout()
308
-
309
-    def update_time(self):
310
-        self.time.SetLabel(time.strftime("%H:%M"))
311
-        self.date.SetLabel(time.strftime("%d.%m.%Y"))
312
-        self.Layout()
192
+            self.__src__[src_id].register_callback(None, None, self.data_receive_callback, src_id)
313 193
 
314 194
     def gate_oc_evt(self, event):
315
-        cn = self.PROT_NAMES[self.PROT_ID_GARAGE]
316
-        logger = logging.getLogger(ROOT_LOGGER_NAME).getChild(__name__ + '.' + cn)
317 195
         r = wx.MessageDialog(
318 196
             self,
319 197
             "Soll das Garagentor betätigt werden?",
@@ -321,38 +199,167 @@ class WetationFrameProt(gui.Wetation):
321 199
             wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING
322 200
         ).ShowModal()
323 201
         if r == wx.ID_YES:
324
-            logger.debug("Gate open/close request")
325
-            self.__prot__[self.PROT_ID_GARAGE].send(socket_protocol.SID_EXECUTE_REQUEST, garage_protocol.OPEN_CLOSE_GATE, None)
202
+            logger.info("Gate open/close request")
203
+            self.__src__[self.SRC_ID_GARAGE].send(socket_protocol.SID_EXECUTE_REQUEST, garage_protocol.OPEN_CLOSE_GATE, None)
326 204
         event.Skip()
327 205
 
328 206
     def reset_in_temp_minmax_evt(self, event):
329
-        self.__min_temp_in__ = None
330
-        self.__max_temp_in__ = None
331
-        self.in_temperature_min.SetLabel("-.- °C")
332
-        self.in_temperature_max.SetLabel("-.- °C")
333
-        self.Layout()
207
+        self.__value_storage__.reset_min_max(ValueStorage.DATA_KEY_TEMPERATURE_IN)
208
+        self.__value_storage__.reset_min_max(ValueStorage.DATA_KEY_2ND_TEMPERATURE_IN)
334 209
         event.Skip()
335 210
 
336 211
     def reset_out_temp_minmax_evt(self, event):
337
-        self.__min_temp_out__ = None
338
-        self.__max_temp_out__ = None
339
-        self.out_temperature_min.SetLabel("-.- °C")
340
-        self.out_temperature_max.SetLabel("-.- °C")
212
+        self.__value_storage__.reset_min_max(ValueStorage.DATA_KEY_TEMPERATURE_OUT)
213
+        self.__value_storage__.reset_min_max(ValueStorage.DATA_KEY_2ND_TEMPERATURE_OUT)
214
+        event.Skip()
215
+
216
+    def data_request_task(self, rt):
217
+        (rt)
218
+        if self.__drt_cnt__ < 9:
219
+            self.__drt_cnt__ += 1
220
+        else:
221
+            self.__drt_cnt__ = 0
222
+        logger.debug('data_request_task stated with cnt = %d', self.__drt_cnt__)
223
+        #
224
+        # Request GATE_POSITION
225
+        #
226
+        self.__src__[self.SRC_ID_GARAGE].send(service_id=socket_protocol.SID_READ_REQUEST, data_id=garage_protocol.GATE_POSITION, data=None)
227
+        #
228
+        # Request WET-OUT-DATA
229
+        #
230
+        if self.__drt_cnt__ == 0:
231
+            self.__src__[self.SRC_ID_OUT].send(service_id=socket_protocol.SID_READ_REQUEST, data_id=wetation_protocol.ENVDATA_STATISTIC_BMP, data=None)
232
+        elif self.__drt_cnt__ == 1:
233
+            self.__src__[self.SRC_ID_OUT].send(service_id=socket_protocol.SID_READ_REQUEST, data_id=wetation_protocol.ENVDATA_STATISTIC_DHT, data=None)
234
+        #
235
+        # Request WET-IN-DATA
236
+        #
237
+        elif self.__drt_cnt__ == 5:
238
+            self.__src__[self.SRC_ID_IN].send(service_id=socket_protocol.SID_READ_REQUEST, data_id=wetation_protocol.ENVDATA_STATISTIC_BMP, data=None)
239
+        elif self.__drt_cnt__ == 6:
240
+            self.__src__[self.SRC_ID_IN].send(service_id=socket_protocol.SID_READ_REQUEST, data_id=wetation_protocol.ENVDATA_STATISTIC_DHT, data=None)
241
+        elif self.__drt_cnt__ == 9:
242
+            self.__reconnect__()
243
+        #
244
+
245
+    def __reconnect__(self):
246
+        for src_id in self.ALL_SRC_IDS:
247
+            if not self.__src__[src_id].is_connected():
248
+                logger.warning("Trying to reconnect prot_id %s", self.SRC_NAMES.get(src_id, 'Unknown'))
249
+                self.__src__[src_id].reconnect()
250
+
251
+    def __data_receive_key__(self, src_id, data_id, key=None, default_value=None):
252
+        return {
253
+            self.SRC_ID_GARAGE: {
254
+                garage_protocol.GATE_POSITION: {
255
+                    None: ValueStorage.DATA_KEY_GATE_POSITION,
256
+                }
257
+            },
258
+            self.SRC_ID_IN: {
259
+                wetation_protocol.ENVDATA_STATISTIC_BMP: {
260
+                    bmp_180.KEY_PRESSURE: ValueStorage.DATA_KEY_PRESSURE_IN,
261
+                    bmp_180.KEY_TEMPERATURE: ValueStorage.DATA_KEY_2ND_TEMPERATURE_IN
262
+                },
263
+                wetation_protocol.ENVDATA_STATISTIC_DHT: {
264
+                    dht_22.KEY_HUMIDITY: ValueStorage.DATA_KEY_HUMIDITY_IN,
265
+                    dht_22.KEY_TEMPERATURE: ValueStorage.DATA_KEY_TEMPERATURE_IN
266
+                },
267
+            },
268
+            self.SRC_ID_OUT: {
269
+                wetation_protocol.ENVDATA_STATISTIC_BMP: {
270
+                    bmp_180.KEY_PRESSURE: ValueStorage.DATA_KEY_PRESSURE_OUT,
271
+                    bmp_180.KEY_TEMPERATURE: ValueStorage.DATA_KEY_2ND_TEMPERATURE_OUT
272
+                },
273
+                wetation_protocol.ENVDATA_STATISTIC_DHT: {
274
+                    dht_22.KEY_HUMIDITY: ValueStorage.DATA_KEY_HUMIDITY_OUT,
275
+                    dht_22.KEY_TEMPERATURE: ValueStorage.DATA_KEY_TEMPERATURE_OUT
276
+                },
277
+            }
278
+        }.get(src_id, {}).get(data_id, {}).get(key, default_value)
279
+
280
+    def data_receive_callback(self, data, src_id):
281
+        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]:
282
+            try:
283
+                for key in data.get_data():
284
+                    if key != bmp_180.KEY_TIME:
285
+                        drk = self.__data_receive_key__(src_id, data.get_data_id(), key)
286
+                        if drk is not None:
287
+                            self.__process_rx_data__(
288
+                                drk,
289
+                                helpers.continues_statistic(**data.get_data()[key])
290
+                            )
291
+                        else:
292
+                            self.__warning_rx_data__('Unknown RX-Data', data, src_id)
293
+            except TypeError as e:
294
+                self.__warning_rx_data__('Wront DataType in RX-Data (%s)' % e, data, src_id)
295
+        else:
296
+            drk = self.__data_receive_key__(src_id, data.get_data_id())
297
+            if drk is not None:
298
+                self.__process_rx_data__(
299
+                    drk,
300
+                    data.get_data()
301
+                )
302
+            else:
303
+                self.__warning_rx_data__('Unknown RX-Data', data, src_id)
304
+
305
+    def __process_rx_data__(self, drk, data):
306
+        self.__value_storage__.append(drk, data)
307
+
308
+    def __warning_rx_data__(self, desc, data, src_id):
309
+        logger.warning('%s from %s: %s', desc, self.SRC_NAMES.get(src_id, 'Unknown'), repr(data))
310
+
311
+    def __check_data_status__(self):
312
+        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:
313
+            self.in_temperature.SetBackgroundColour((255, 255, 200, 255))
314
+        else:
315
+            self.in_temperature.SetBackgroundColour((250, 249, 255, 255))
316
+        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:
317
+            self.out_temperature.SetBackgroundColour((255, 255, 200, 255))
318
+        else:
319
+            self.out_temperature.SetBackgroundColour((250, 249, 255, 255))
320
+        if dict.get(self.__value_storage__, ValueStorage.DATA_KEY_GATE_POSITION) is None:
321
+            self.heading_garage.SetBackgroundColour((142, 35, 35, 255))
322
+        else:
323
+            self.heading_garage.SetBackgroundColour((35, 35, 142, 255))
324
+
325
+    def gui_update(self, event):
326
+        #
327
+        # TIME and DATE
328
+        #
329
+        self.time.SetLabel(time.strftime("%H:%M"))
330
+        self.date.SetLabel(time.strftime("%d.%m.%Y"))
331
+        #
332
+        update_list = [
333
+            # WET-OUT
334
+            (self.out_humidity.SetLabel, [ValueStorage.DATA_KEY_HUMIDITY_OUT, 'mean', True]),
335
+            (self.out_pressure.SetLabel, [ValueStorage.DATA_KEY_PRESSURE_OUT, 'mean', True]),
336
+            (self.out_temperature.SetLabel, [ValueStorage.DATA_KEY_TEMPERATURE_OUT, 'mean', True]),
337
+            (self.out_temperature_max.SetLabel, [ValueStorage.DATA_KEY_TEMPERATURE_OUT, 'max', True]),
338
+            (self.out_temperature_min.SetLabel, [ValueStorage.DATA_KEY_TEMPERATURE_OUT, 'min', True]),
339
+            # WET-IN
340
+            (self.in_humidity.SetLabel, [ValueStorage.DATA_KEY_HUMIDITY_IN, 'mean', True]),
341
+            (self.in_pressure.SetLabel, [ValueStorage.DATA_KEY_PRESSURE_IN, 'mean', True]),
342
+            (self.in_temperature.SetLabel, [ValueStorage.DATA_KEY_TEMPERATURE_IN, 'mean', True]),
343
+            (self.in_temperature_max.SetLabel, [ValueStorage.DATA_KEY_TEMPERATURE_IN, 'max', True]),
344
+            (self.in_temperature_min.SetLabel, [ValueStorage.DATA_KEY_TEMPERATURE_IN, 'min', True]),
345
+            # GATE
346
+            (self.gate_position.SetValue, [ValueStorage.DATA_KEY_GATE_POSITION, None, False, 50])
347
+        ]
348
+        #
349
+        for func, args in update_list:
350
+            func(self.__value_storage__.get(*args))
351
+        self.__check_data_status__()
352
+        #
353
+        #
341 354
         self.Layout()
342 355
         event.Skip()
343 356
 
344 357
     def run(self):
345 358
         self.__task_1s__.run()
346
-        self.__task_10s__.run()
347
-        self.__task_60s__.run()
348 359
 
349 360
     def close(self):
350 361
         self.__task_1s__.stop()
351 362
         self.__task_1s__.join()
352
-        self.__task_10s__.stop()
353
-        self.__task_10s__.join()
354
-        self.__task_60s__.stop()
355
-        self.__task_60s__.join()
356 363
 
357 364
     def __del__(self):
358 365
         self.close()
@@ -360,14 +367,12 @@ class WetationFrameProt(gui.Wetation):
360 367
 
361 368
 class MyApp(wx.App):
362 369
     def OnInit(self):
363
-        self.frame = WetationFrameProt(None, wx.ID_ANY, "")
370
+        self.frame = Wetation(None, wx.ID_ANY, "")
364 371
         self.SetTopWindow(self.frame)
365 372
         self.frame.Show()
366 373
         return True
367 374
 
368 375
 
369
-
370
-
371 376
 if __name__ == "__main__":
372 377
     report.appLoggingConfigure(os.path.dirname(__file__), config.LOGTARGET, ((config.APP_NAME, config.LOGLVL), ), fmt=config.formatter, host=config.LOGHOST, port=config.LOGPORT)
373 378
     #

+ 1
- 1
socket_protocol

@@ -1 +1 @@
1
-Subproject commit b5ee20216e9215dd6555d5bae702a48e7512ecce
1
+Subproject commit a8aa15974b309b3f916fe4312538e166ae2f3691

Loading…
Peruuta
Tallenna