|
@@ -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
|
#
|