Просмотр исходного кода

New Functions and Restructuring

mod_update
Dirk Alders 4 лет назад
Родитель
Сommit
0bb85004d0
7 измененных файлов: 182 добавлений и 137 удалений
  1. 3
    0
      .gitmodules
  2. 1
    0
      helpers
  3. 1
    1
      rpi_envsens
  4. 174
    133
      smarthome.py
  5. 1
    1
      socket_protocol
  6. 1
    1
      tcp_socket
  7. 1
    1
      wetation_protocol

+ 3
- 0
.gitmodules Просмотреть файл

@@ -22,3 +22,6 @@
22 22
 [submodule "garage_protocol"]
23 23
 	path = garage_protocol
24 24
 	url = https://git.mount-mockery.de/application/garage_protocol.git
25
+[submodule "helpers"]
26
+	path = helpers
27
+	url = https://git.mount-mockery.de/pylib/helpers.git

+ 1
- 0
helpers

@@ -0,0 +1 @@
1
+Subproject commit 1819df790c0784473480dffe9aca8ca774d46343

+ 1
- 1
rpi_envsens

@@ -1 +1 @@
1
-Subproject commit fd86c70c83f75bcbbfc60ed69a99252bd7fb62b9
1
+Subproject commit 5ffef8254a0a035bc11ccefbd19497dd3969ac10

+ 174
- 133
smarthome.py Просмотреть файл

@@ -2,13 +2,13 @@
2 2
 # -*- coding: UTF-8 -*-
3 3
 #
4 4
 
5
-#TODO: Zyklischer reconnect versuch (ggf. inkl. herstellen der Schnittstelle für ein reconnect beim tcp_client)
6
-
7 5
 import config
8
-import rpi_envsens as envsens
6
+from rpi_envsens.dht import dht_22
7
+from rpi_envsens.bmp import bmp_180
9 8
 import garage_protocol
10 9
 import gui
11 10
 import logging
11
+import numbers
12 12
 import os
13 13
 import report
14 14
 import task
@@ -25,16 +25,32 @@ class WetationFrameProt(gui.Wetation):
25 25
     PROT_ID_IN = 1
26 26
     PROT_ID_OUT = 2
27 27
     ALL_PROT_IDS = [PROT_ID_GARAGE, PROT_ID_IN, PROT_ID_OUT, ]
28
-    SLOW_PROT_IDS = [PROT_ID_IN, PROT_ID_OUT, ]
29
-    FAST_PROT_IDS = [PROT_ID_GARAGE, ]
28
+
29
+    PROT_IPS = {
30
+        PROT_ID_GARAGE: config.server_garage_ip,
31
+        PROT_ID_IN: config.server_in_ip,
32
+        PROT_ID_OUT: config.server_out_ip,
33
+    }
34
+
35
+    PROT_PORTS = {
36
+        PROT_ID_GARAGE: config.server_garage_port,
37
+        PROT_ID_IN: config.server_in_port,
38
+        PROT_ID_OUT: config.server_out_port,
39
+    }
40
+
41
+    PROT_SECRETS = {
42
+        PROT_ID_GARAGE: config.server_garage_secret,
43
+        PROT_ID_IN: config.server_in_secret,
44
+        PROT_ID_OUT: config.server_out_secret,
45
+    }
30 46
     
31
-    REQUEST_MSGS = {
32
-        PROT_ID_GARAGE: [
33
-            {
34
-                'service_id': garage_protocol.my_base_protocol_tcp.SID_READ_REQUEST,
35
-                'data_id': garage_protocol.my_base_protocol_tcp.GATE_POSITION,
36
-            },
37
-        ],
47
+    PROT_CLASSES = {
48
+        PROT_ID_GARAGE: garage_protocol.my_base_protocol_tcp,
49
+        PROT_ID_IN: wetation_protocol.my_base_protocol_tcp,
50
+        PROT_ID_OUT: wetation_protocol.my_base_protocol_tcp,
51
+    }
52
+
53
+    REQUEST_MSGS_SLOW = {
38 54
         PROT_ID_IN: [
39 55
             {
40 56
                 'service_id': wetation_protocol.my_base_protocol_tcp.SID_READ_REQUEST,
@@ -49,160 +65,169 @@ class WetationFrameProt(gui.Wetation):
49 65
         ],
50 66
     }
51 67
 
68
+    REQUEST_MSGS_FAST = {
69
+        PROT_ID_GARAGE: [
70
+            {
71
+                'service_id': garage_protocol.my_base_protocol_tcp.SID_READ_REQUEST,
72
+                'data_id': garage_protocol.my_base_protocol_tcp.GATE_POSITION,
73
+            },
74
+        ],
75
+    }
76
+
52 77
 
53 78
     def __init__(self, *args, **kwds):
79
+        self.__min_temp_in__ = None
80
+        self.__min_temp_out__ = None
81
+        self.__max_temp_in__ = None
82
+        self.__max_temp_out__ = None
54 83
         self.__prot__ = {}
55
-        self.no_data = {
56
-            self.PROT_ID_GARAGE: self.update_gate_position,
57
-            self.PROT_ID_IN: self.update_current_env_data_in,
58
-            self.PROT_ID_OUT: self.update_current_env_data_out,
59
-        }
60 84
         gui.Wetation.__init__(self, *args, **kwds)
85
+        self.in_temperature_min.Bind(wx.EVT_LEFT_DOWN, self.reset_in_temp_minmax_evt)
86
+        self.in_temperature_max.Bind(wx.EVT_LEFT_DOWN, self.reset_in_temp_minmax_evt)
87
+        self.out_temperature_min.Bind(wx.EVT_LEFT_DOWN, self.reset_out_temp_minmax_evt)
88
+        self.out_temperature_max.Bind(wx.EVT_LEFT_DOWN, self.reset_out_temp_minmax_evt)
61 89
         self.ShowFullScreen(config.FULL_SCREEN)
62 90
 
63 91
         self.__init_communication__()
92
+
64 93
         self.__task_1s__ = task.periodic(1, self.__task_1s_callback__)
65 94
         self.__task_10s__ = task.periodic(10, self.__task_10s_callback__)
66 95
         self.__task_60s__ = task.periodic(60, self.__task_60s_callback__)
67 96
 
68
-    def __init_protocol__(self, prot_id, ip, port, prot_class, secret):
69
-        c_tcp = tcp_socket.tcp_client_stp(ip, port, rx_log_lvl=logging.DEBUG)
70
-        self.__prot__[prot_id] = prot_class(c_tcp, secret)
71
-        if config.server_garage_secret is not None:
72
-            self.__prot__[prot_id].authentificate()
73
-
74 97
     def __init_communication__(self):
75 98
         #
76 99
         # Start TCP-Clients
77
-        self.__init_protocol__(
78
-            self.PROT_ID_GARAGE,
79
-            config.server_garage_ip,
80
-            config.server_garage_port,
81
-            garage_protocol.my_base_protocol_tcp,
82
-            config.server_garage_secret
83
-        )
84
-        self.__prot__[self.PROT_ID_GARAGE].register_callback(garage_protocol.my_base_protocol_tcp.SID_READ_RESPONSE, None, self.__garage_data__)
85
-
86
-        self.__init_protocol__(
87
-            self.PROT_ID_IN,
88
-            config.server_in_ip,
89
-            config.server_in_port,
90
-            wetation_protocol.my_base_protocol_tcp,
91
-            config.server_in_secret
92
-        )
93
-        self.__prot__[self.PROT_ID_IN].register_callback(wetation_protocol.my_base_protocol_tcp.SID_READ_RESPONSE, None, self.__in_data__)
94
-
95
-        self.__init_protocol__(
96
-            self.PROT_ID_OUT,
97
-            config.server_out_ip,
98
-            config.server_out_port,
99
-            wetation_protocol.my_base_protocol_tcp,
100
-            config.server_out_secret
101
-        )
102
-        self.__prot__[self.PROT_ID_OUT].register_callback(wetation_protocol.my_base_protocol_tcp.SID_READ_RESPONSE, None, self.__out_data__)
100
+        for prot_id in self.ALL_PROT_IDS:
101
+            logger.debug('Initiating communication channel for prot_id %d', prot_id)
102
+            c_tcp = tcp_socket.tcp_client_stp(self.PROT_IPS[prot_id], self.PROT_PORTS[prot_id], rx_log_lvl=logging.DEBUG)
103
+            self.__prot__[prot_id] = self.PROT_CLASSES[prot_id](c_tcp, secret=self.PROT_SECRETS[prot_id], auto_auth=True)
104
+            #
105
+            self.__prot__[prot_id].register_callback(None, None, self.__prot_resp_callbacks__, prot_id)
106
+
107
+    def __initiate_data_request__(self, rt, request_msgs):
108
+        for prot_id in request_msgs:
109
+            for request_msg in request_msgs.get(prot_id, []):
110
+                service_id = request_msg['service_id']
111
+                data_id = request_msg['data_id']
112
+                if self.__prot__[prot_id].connection_established():
113
+                    logger.debug('Sending data request for prot_id %d, service_id %d and data_id %d', prot_id, service_id, data_id)
114
+                    self.__prot__[prot_id].send(service_id, data_id, None)
115
+                else:
116
+                    wx.CallAfter(self.__no_data__, prot_id, service_id + 1, data_id)
103 117
 
104 118
     def __task_1s_callback__(self, rt):
105 119
         # request data from fast prot ids
106
-        self.__initiate_data_request__(rt, self.FAST_PROT_IDS)
120
+        self.__initiate_data_request__(rt, self.REQUEST_MSGS_FAST)
107 121
         # set date and time
108 122
         wx.CallAfter(self.update_time)
109 123
 
110 124
     def __task_10s_callback__(self, rt):
111 125
         # request data from slow prot ids
112
-        self.__initiate_data_request__(rt, self.SLOW_PROT_IDS)
126
+        self.__initiate_data_request__(rt, self.REQUEST_MSGS_SLOW)
113 127
 
114 128
     def __task_60s_callback__(self, rt):
115 129
         # reconnect prots if needed
116 130
         for prot_id in self.ALL_PROT_IDS:
117
-            if not self.__prot__[prot_id].is_connected():
131
+            if not self.__prot__[prot_id].connected():
118 132
                 logger.debug("Trying to reconnect prot_id %d", prot_id)
119 133
                 self.__prot__[prot_id].reconnect()
120 134
 
121
-    def __initiate_data_request__(self, rt, prot_ids):
122
-        for prot_id in prot_ids:
123
-            if self.__prot__[prot_id].is_connected():
124
-                for request_msg in self.REQUEST_MSGS.get(prot_id, []):
125
-                    service_id = request_msg['service_id']
126
-                    data_id = request_msg['data_id']
127
-                    logger.debug('Sending data request for prot_id %d, service_id %d and data_id %d', prot_id, service_id, data_id)
128
-                    self.__prot__[prot_id].send(service_id, data_id, None)
129
-            else:
130
-                logger.debug('Resetting GUI for prot_id %d', prot_id)
131
-                wx.CallAfter(self.no_data[prot_id], None)
132
-
133
-    def __in_data__(self, msg):
134
-        return self.__env_data__(msg, self.PROT_ID_IN)
135
-
136
-    def __out_data__(self, msg):
137
-        return self.__env_data__(msg, self.PROT_ID_OUT)
138
-
139
-    def __env_data__(self, msg, prot_id):
140
-        logger.debug('Received data for prot_id %d, service_id %d, data_id %d', prot_id, msg.get_service_id(), msg.get_data_id())
141
-        if msg.get_status() == wetation_protocol.my_base_protocol_tcp.STATUS_OKAY:
142
-            if msg.get_data_id() == wetation_protocol.my_base_protocol_tcp.CURRENT_ENVDATA:
143
-                if prot_id == self.PROT_ID_IN:
144
-                    wx.CallAfter(self.update_current_env_data_in, msg)
145
-                else:
146
-                    wx.CallAfter(self.update_current_env_data_out, msg)
147
-                return wetation_protocol.my_base_protocol_tcp.STATUS_OKAY, None
148
-            else:
149
-                logger.warning('Received unknown data for prot_id %d, service_id %d, data_id %d', self.PROT_ID_GARAGE, msg.get_service_id(), msg.get_data_id())
135
+    def __validate_response_data__(self, prot_id, service_id, data_id, data):
136
+        rv = False
137
+        if prot_id == self.PROT_ID_GARAGE:
138
+            if service_id == garage_protocol.my_base_protocol_tcp.SID_READ_RESPONSE and data_id == garage_protocol.my_base_protocol_tcp.GATE_POSITION:
139
+                rv = isinstance(data, numbers.Number)
140
+            elif service_id == garage_protocol.my_base_protocol_tcp.SID_EXECUTE_RESPONSE and data_id == garage_protocol.my_base_protocol_tcp.OPEN_CLOSE_GATE:
141
+                if data is True:
142
+                    rv = True
143
+        elif prot_id in [self.PROT_ID_IN, self.PROT_ID_OUT]:
144
+            if service_id == wetation_protocol.my_base_protocol_tcp.SID_READ_RESPONSE and data_id == wetation_protocol.my_base_protocol_tcp.CURRENT_ENVDATA:
145
+                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)
146
+        if rv is False:
147
+            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))
148
+        return rv
149
+
150
+    def __prot_resp_callbacks__(self, msg, prot_id):
151
+        service_id = msg.get_service_id()
152
+        data_id = msg.get_data_id()
153
+        data = msg.get_data()
154
+        if self.__validate_response_data__(prot_id, service_id, data_id, data):
155
+            wx.CallAfter(self.__data__, prot_id, service_id, data_id, data)
150 156
             return wetation_protocol.my_base_protocol_tcp.STATUS_OKAY, None
151 157
         else:
152
-            logger.error('Error, receiving environmental data! MSG_STATUS was %s with PROT_ID %d', garage_protocol.my_base_protocol_tcp.STATUS_NAMES.get(msg.get_status(), repr(msg.get_status())), prot_id)
158
+            wx.CallAfter(self.__no_data__, prot_id, service_id, data_id)
153 159
             return wetation_protocol.my_base_protocol_tcp.STATUS_SERVICE_OR_DATA_UNKNOWN, None
154 160
 
155
-    def __garage_data__(self, msg):
156
-        logger.debug('Received data for prot_id %d, service_id %d, data_id %d', self.PROT_ID_GARAGE, msg.get_service_id(), msg.get_data_id())
157
-        if msg.get_status() == garage_protocol.my_base_protocol_tcp.STATUS_OKAY:
158
-            if msg.get_data_id() == garage_protocol.my_base_protocol_tcp.GATE_POSITION:
159
-                wx.CallAfter(self.update_gate_position, msg)
161
+    def __no_data__(self, prot_id, service_id, data_id):
162
+        if prot_id == self.PROT_ID_GARAGE:
163
+            if service_id == garage_protocol.my_base_protocol_tcp.SID_READ_RESPONSE and data_id == garage_protocol.my_base_protocol_tcp.GATE_POSITION:
164
+                logger.debug('Resetting GUI for prot_id %d, service_id=%d, data_id=%d', prot_id, service_id, data_id)
165
+                self.heading_garage.Show(False)
166
+                self.gate_oc.Show(False)
167
+                self.gate_open.Show(False)
168
+                self.gate_position.Show(False)
169
+                self.gate_close.Show(False)
170
+                self.Layout()
171
+                return
172
+            elif service_id == garage_protocol.my_base_protocol_tcp.SID_EXECUTE_RESPONSE and data_id == garage_protocol.my_base_protocol_tcp.OPEN_CLOSE_GATE:
173
+                return
174
+        elif prot_id in [self.PROT_ID_IN, self.PROT_ID_OUT]:
175
+            if service_id == wetation_protocol.my_base_protocol_tcp.SID_READ_RESPONSE and data_id == wetation_protocol.my_base_protocol_tcp.CURRENT_ENVDATA:
176
+                logger.debug('Resetting GUI for prot_id %d, service_id=%d, data_id=%d', prot_id, service_id, data_id)
177
+                txt_temperature = '-.- °C'
178
+                txt_humidity = '-.- %'
179
+                txt_pressure = '- hPa'
180
+                if prot_id == self.PROT_ID_IN:
181
+                    self.in_temperature.SetLabel(txt_temperature)
182
+                    self.in_humidity.SetLabel(txt_humidity)
183
+                    self.in_pressure.SetLabel(txt_pressure)
184
+                else:
185
+                    self.out_temperature.SetLabel(txt_temperature)
186
+                    self.out_humidity.SetLabel(txt_humidity)
187
+                    self.out_pressure.SetLabel(txt_pressure)
188
+                self.Layout()
189
+                return
190
+        logger.warning("Unknown response with no valid data for prot_id %d, service_id=%d, data_id=%d", prot_id, service_id, data_id)
191
+
192
+    def __data__(self, prot_id, service_id, data_id, data):
193
+        if prot_id == self.PROT_ID_GARAGE:
194
+            if service_id == garage_protocol.my_base_protocol_tcp.SID_READ_RESPONSE and data_id == garage_protocol.my_base_protocol_tcp.GATE_POSITION:
195
+                self.heading_garage.Show(True)
196
+                self.gate_oc.Show(True)
197
+                self.gate_open.Show(True)
198
+                self.gate_position.Show(True)
199
+                self.gate_close.Show(True)
200
+                self.gate_position.SetValue(int(data * 100))
201
+                self.Layout()
160 202
                 return garage_protocol.my_base_protocol_tcp.STATUS_OKAY, None
161
-            else:
162
-                logger.warning('Received unknown data for prot_id %d, service_id %d, data_id %d', self.PROT_ID_GARAGE, msg.get_service_id(), msg.get_data_id())
163
-        else:
164
-            logger.error('Error, receiving garage data! MSG_STATUS was %s', garage_protocol.my_base_protocol_tcp.STATUS_NAMES.get(msg.get_status(), repr(msg.get_status())))
165
-            return garage_protocol.my_base_protocol_tcp.STATUS_SERVICE_OR_DATA_UNKNOWN, None
166
-
167
-
168
-    def update_current_env_data_in(self, msg):
169
-        if msg is None:
170
-            temperature = '-.-'
171
-            humidity = '-.-'
172
-            pressure = '-'
173
-        else:
174
-            env_data = msg.get_data()
175
-            temperature = '%.1f' % env_data[envsens.KEY_TEMPERATURE]
176
-            humidity = '%.1f' % env_data[envsens.KEY_HUMIDITY]
177
-            pressure = '%.0f' % env_data[envsens.KEY_PRESSURE]
178
-        self.in_temperature.SetLabel("%s °C" % temperature)
179
-        self.in_humidity.SetLabel("%s %%" % humidity)
180
-        self.in_pressure.SetLabel("%s hPa" % pressure)
181
-        self.Layout()
182
-
183
-    def update_current_env_data_out(self, msg):
184
-        if msg is None:
185
-            temperature = '-.-'
186
-            humidity = '-.-'
187
-            pressure = '-'
188
-        else:
189
-            env_data = msg.get_data()
190
-            temperature = "%.1f" % env_data[envsens.KEY_TEMPERATURE]
191
-            humidity = "%.1f" % env_data[envsens.KEY_HUMIDITY]
192
-            pressure = "%.0f" % env_data[envsens.KEY_PRESSURE]
193
-        self.out_temperature.SetLabel("%s °C" % temperature)
194
-        self.out_humidity.SetLabel("%s %%" % humidity)
195
-        self.out_pressure.SetLabel("%s hPa" % pressure)
196
-        self.Layout()
197
-
198
-    def update_gate_position(self, msg):
199
-        self.heading_garage.Show(msg is not None)
200
-        self.gate_oc.Show(msg is not None)
201
-        self.gate_open.Show(msg is not None)
202
-        self.gate_position.Show(msg is not None)
203
-        self.gate_close.Show(msg is not None)
204
-        if msg is not None:
205
-            self.gate_position.SetValue(msg.get_data() * 100)
203
+            elif service_id == garage_protocol.my_base_protocol_tcp.SID_EXECUTE_RESPONSE and data_id == garage_protocol.my_base_protocol_tcp.OPEN_CLOSE_GATE:
204
+                return garage_protocol.my_base_protocol_tcp.STATUS_OKAY, None
205
+        elif prot_id in [self.PROT_ID_IN, self.PROT_ID_OUT]:
206
+            if service_id == wetation_protocol.my_base_protocol_tcp.SID_READ_RESPONSE and data_id == wetation_protocol.my_base_protocol_tcp.CURRENT_ENVDATA:
207
+                temp = data[dht_22.KEY_TEMPERATURE]
208
+                if prot_id == self.PROT_ID_IN:
209
+                    if self.__max_temp_in__ is None or temp > self.__max_temp_in__:
210
+                        self.__max_temp_in__ = temp
211
+                    if self.__min_temp_in__ is None or temp < self.__min_temp_in__:
212
+                        self.__min_temp_in__ = temp
213
+                    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__)
214
+                else:
215
+                    if self.__max_temp_out__ is None or temp > self.__max_temp_out__:
216
+                        self.__max_temp_out__ = temp
217
+                    if self.__min_temp_out__ is None or temp < self.__min_temp_out__:
218
+                        self.__min_temp_out__ = temp
219
+                    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__)
220
+                return wetation_protocol.my_base_protocol_tcp.STATUS_OKAY, None
221
+        logger.warning("Unknown response with valid data for prot_id %d, service_id=%d, data_id=%d", prot_id, service_id, data_id)
222
+        return wetation_protocol.my_base_protocol_tcp.STATUS_SERVICE_OR_DATA_UNKNOWN, None
223
+
224
+    def update_current_env_data(self, env_data, temperature, humidity, pressure, temperature_min, temperature_max, value_min, value_max):
225
+        if isinstance(value_min, numbers.Number) and isinstance(value_max, numbers.Number):
226
+            temperature_min.SetLabel('%.1f' % value_min)
227
+            temperature_max.SetLabel('%.1f' % value_max)
228
+        temperature.SetLabel('%.1f °C' % env_data[dht_22.KEY_TEMPERATURE])
229
+        humidity.SetLabel('%.1f %%' % env_data[dht_22.KEY_HUMIDITY])
230
+        pressure.SetLabel('%.0f hPa' % env_data[bmp_180.KEY_PRESSURE])
206 231
         self.Layout()
207 232
 
208 233
     def update_time(self):
@@ -210,11 +235,27 @@ class WetationFrameProt(gui.Wetation):
210 235
         self.date.SetLabel(time.strftime("%d.%m.%Y"))
211 236
         self.Layout()
212 237
 
213
-    def gate_oc_evt(self, event):  # wxGlade: Wetation.<event_handler>
238
+    def gate_oc_evt(self, event):
214 239
         logger.debug("Gate open/close request")
215 240
         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)
216 241
         event.Skip()
217 242
 
243
+    def reset_in_temp_minmax_evt(self, event):
244
+        self.__min_temp_in__ = None
245
+        self.__max_temp_in__ = None
246
+        self.in_temperature_min.SetLabel("-.- °C")
247
+        self.in_temperature_max.SetLabel("-.- °C")
248
+        self.Layout()
249
+        event.Skip()
250
+
251
+    def reset_out_temp_minmax_evt(self, event):
252
+        self.__min_temp_out__ = None
253
+        self.__max_temp_out__ = None
254
+        self.out_temperature_min.SetLabel("-.- °C")
255
+        self.out_temperature_max.SetLabel("-.- °C")
256
+        self.Layout()
257
+        event.Skip()
258
+
218 259
     def run(self):
219 260
         self.__task_1s__.run()
220 261
         self.__task_10s__.run()

+ 1
- 1
socket_protocol

@@ -1 +1 @@
1
-Subproject commit f4a7f1e7a4156f397ee76bee01b0fdab88a727d1
1
+Subproject commit cd508340e390fc01ebdf81eae3b1e221aa1b658a

+ 1
- 1
tcp_socket

@@ -1 +1 @@
1
-Subproject commit 3e6e74f976a1322d97f1d50480df133455486fd6
1
+Subproject commit d9bdc909e98f627fea4417d027ae5d9cb9e2ed81

+ 1
- 1
wetation_protocol

@@ -1 +1 @@
1
-Subproject commit 53e4523c140b5f51dedb3b128048eb59c1a5da4c
1
+Subproject commit bc4808b313ecebba6511b82c801eeadd95e8a503

Загрузка…
Отмена
Сохранить