Mbed Host Tests
conn_proxy.py
Go to the documentation of this file.
1#!/usr/bin/env python
2"""
3mbed SDK
4Copyright (c) 2011-2016 ARM Limited
5
6Licensed under the Apache License, Version 2.0 (the "License");
7you may not use this file except in compliance with the License.
8You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12Unless required by applicable law or agreed to in writing, software
13distributed under the License is distributed on an "AS IS" BASIS,
14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15See the License for the specific language governing permissions and
16limitations under the License.
17"""
18
19import re
20import sys
21import uuid
22from time import time
23from mbed_host_tests.host_tests_logger import HtrunLogger
24from .conn_primitive_serial import SerialConnectorPrimitive
25from .conn_primitive_remote import RemoteConnectorPrimitive
26from .conn_primitive_fastmodel import FastmodelConnectorPrimitive
27
28if (sys.version_info > (3, 0)):
29 from queue import Empty as QueueEmpty # Queue here refers to the module, not a class
30else:
31 from Queue import Empty as QueueEmpty
32
34 """! Simple auxiliary class used to walk through a buffer and search for KV tokens """
35 def __init__(self):
36 self.KIVI_REGEX = r"\{\{([\w\d_-]+);([^\}]+)\}\}"
37 self.buffbuff = str()
38 self.kvl = []
39 self.re_kv = re.compile(self.KIVI_REGEX)
40
41 def append(self, payload):
42 """! Append stream buffer with payload and process. Returns non-KV strings"""
43 logger = HtrunLogger('CONN')
44 try:
45 self.buffbuff += payload.decode('utf-8')
46 except UnicodeDecodeError:
47 logger.prn_wrn("UnicodeDecodeError encountered!")
48 self.buffbuff += payload.decode('utf-8','ignore')
49 lines = self.buffbuff.split('\n')
50 self.buffbuff = lines[-1] # remaining
51 lines.pop(-1)
52 # List of line or strings that did not match K,V pair.
53 discarded = []
54
55 for line in lines:
56 m = self.re_kv.search(line)
57 if m:
58 (key, value) = m.groups()
59 self.kvl.append((key, value, time()))
60 line = line.strip()
61 match = m.group(0)
62 pos = line.find(match)
63 before = line[:pos]
64 after = line[pos + len(match):]
65 if len(before) > 0:
66 discarded.append(before)
67 if len(after) > 0:
68 # not a K,V pair part
69 discarded.append(after)
70 else:
71 # not a K,V pair
72 discarded.append(line)
73 return discarded
74
75 def search(self):
76 """! Check if there is a KV value in buffer """
77 return len(self.kvl) > 0
78
79 def pop_kv(self):
80 if len(self.kvl):
81 return self.kvl.pop(0)
82 return None, None, time()
83
84
85def conn_primitive_factory(conn_resource, config, event_queue, logger):
86 """! Factory producing connectors based on type and config
87 @param conn_resource Name of connection primitive (e.g. 'serial' for
88 local serial port connection or 'grm' for global resource manager)
89 @param event_queue Even queue of Key-Value protocol
90 @param config Global configuration for connection process
91 @param logger Host Test logger instance
92 @return Object of type <ConnectorPrimitive> or None if type of connection primitive unknown (conn_resource)
93 """
94 polling_timeout = int(config.get('polling_timeout', 60))
95 logger.prn_inf("notify event queue about extra %d sec timeout for serial port pooling"%polling_timeout)
96 event_queue.put(('__timeout', polling_timeout, time()))
97
98 if conn_resource == 'serial':
99 # Standard serial port connection
100 # Notify event queue we will wait additional time for serial port to be ready
101
102 # Get extra configuration related to serial port
103 port = config.get('port')
104 baudrate = config.get('baudrate')
105
106 logger.prn_inf("initializing serial port listener... ")
107 connector = SerialConnectorPrimitive(
108 'SERI',
109 port,
110 baudrate,
111 config=config)
112 elif conn_resource == 'grm':
113 # Start GRM (Gloabal Resource Mgr) collection
114 logger.prn_inf("initializing global resource mgr listener... ")
115 connector = RemoteConnectorPrimitive('GLRM', config=config)
116 elif conn_resource == 'fmc':
117 # Start Fast Model Connection collection
118 logger.prn_inf("initializing fast model connection")
119 connector = FastmodelConnectorPrimitive('FSMD', config=config)
120 else:
121 logger.pn_err("unknown connection resource!")
122 raise NotImplementedError("ConnectorPrimitive factory: unknown connection resource '%s'!"% conn_resource)
123
124 return connector
125
126
127def conn_process(event_queue, dut_event_queue, config):
128
129 def __notify_conn_lost():
130 error_msg = connector.error()
131 connector.finish()
132 event_queue.put(('__notify_conn_lost', error_msg, time()))
133
134 def __notify_sync_failed():
135 error_msg = connector.error()
136 connector.finish()
137 event_queue.put(('__notify_sync_failed', error_msg, time()))
138
139 logger = HtrunLogger('CONN')
140 logger.prn_inf("starting connection process...")
141
142 # Send connection process start event to host process
143 # NOTE: Do not send any other Key-Value pairs before this!
144 event_queue.put(('__conn_process_start', 1, time()))
145
146 # Configuration of conn_opriocess behaviour
147 sync_behavior = int(config.get('sync_behavior', 1))
148 sync_timeout = config.get('sync_timeout', 1.0)
149 conn_resource = config.get('conn_resource', 'serial')
150 last_sync = False
151
152 # Create connector instance with proper configuration
153 connector = conn_primitive_factory(conn_resource, config, event_queue, logger)
154
155 # If the connector failed, stop the process now
156 if not connector.connected():
157 logger.prn_err("Failed to connect to resource")
158 __notify_conn_lost()
159 return 0
160
161 # Create simple buffer we will use for Key-Value protocol data
162 kv_buffer = KiViBufferWalker()
163
164 # List of all sent to target UUIDs (if multiple found)
165 sync_uuid_list = []
166
167 # We will ignore all kv pairs before we get sync back
168 sync_uuid_discovered = False
169
170 def __send_sync(timeout=None):
171 sync_uuid = str(uuid.uuid4())
172 # Handshake, we will send {{sync;UUID}} preamble and wait for mirrored reply
173 if timeout:
174 logger.prn_inf("Reset the part and send in new preamble...")
175 connector.reset()
176 logger.prn_inf("resending new preamble '%s' after %0.2f sec"% (sync_uuid, timeout))
177 else:
178 logger.prn_inf("sending preamble '%s'"% sync_uuid)
179
180 if connector.write_kv('__sync', sync_uuid):
181 return sync_uuid
182 else:
183 return None
184
185 # Send simple string to device to 'wake up' greentea-client k-v parser
186 if not connector.write("mbed" * 10, log=True):
187 # Failed to write 'wake up' string, exit conn_process
188 __notify_conn_lost()
189 return 0
190
191
192 # Sync packet management allows us to manipulate the way htrun sends __sync packet(s)
193 # With current settings we can force on htrun to send __sync packets in this manner:
194 #
195 # * --sync=0 - No sync packets will be sent to target platform
196 # * --sync=-10 - __sync packets will be sent unless we will reach
197 # timeout or proper response is sent from target platform
198 # * --sync=N - Send up to N __sync packets to target platform. Response
199 # is sent unless we get response from target platform or
200 # timeout occur
201
202 if sync_behavior > 0:
203 # Sending up to 'n' __sync packets
204 logger.prn_inf("sending up to %s __sync packets (specified with --sync=%s)"% (sync_behavior, sync_behavior))
205 sync_uuid = __send_sync()
206
207 if sync_uuid:
208 sync_uuid_list.append(sync_uuid)
209 sync_behavior -= 1
210 else:
211 __notify_conn_lost()
212 return 0
213 elif sync_behavior == 0:
214 # No __sync packets
215 logger.prn_wrn("skipping __sync packet (specified with --sync=%s)"% sync_behavior)
216 else:
217 # Send __sync until we go reply
218 logger.prn_inf("sending multiple __sync packets (specified with --sync=%s)"% sync_behavior)
219
220 sync_uuid = __send_sync()
221 if sync_uuid:
222 sync_uuid_list.append(sync_uuid)
223 sync_behavior -= 1
224 else:
225 __notify_conn_lost()
226 return 0
227
228 loop_timer = time()
229 while True:
230
231 # Check if connection is lost to serial
232 if not connector.connected():
233 __notify_conn_lost()
234 break
235
236 # Send data to DUT
237 try:
238 (key, value, _) = dut_event_queue.get(block=False)
239 except QueueEmpty:
240 pass # Check if target sent something
241 else:
242 # Return if state machine in host_test_default has finished to end process
243 if key == '__host_test_finished' and value == True:
244 logger.prn_inf("received special event '%s' value='%s', finishing"% (key, value))
245 connector.finish()
246 return 0
247 elif key == '__reset':
248 logger.prn_inf("received special event '%s', resetting dut" % (key))
249 connector.reset()
250 event_queue.put(("reset_complete", 0, time()))
251 elif not connector.write_kv(key, value):
252 connector.write_kv(key, value)
253 __notify_conn_lost()
254 break
255
256 # Since read is done every 0.2 sec, with maximum baud rate we can receive 2304 bytes in one read in worst case.
257 data = connector.read(2304)
258 if data:
259 # Stream data stream KV parsing
260 print_lines = kv_buffer.append(data)
261 for line in print_lines:
262 logger.prn_rxd(line)
263 event_queue.put(('__rxd_line', line, time()))
264 while kv_buffer.search():
265 key, value, timestamp = kv_buffer.pop_kv()
266
267 if sync_uuid_discovered:
268 event_queue.put((key, value, timestamp))
269 logger.prn_inf("found KV pair in stream: {{%s;%s}}, queued..."% (key, value))
270 else:
271 if key == '__sync':
272 if value in sync_uuid_list:
273 sync_uuid_discovered = True
274 event_queue.put((key, value, time()))
275 idx = sync_uuid_list.index(value)
276 logger.prn_inf("found SYNC in stream: {{%s;%s}} it is #%d sent, queued..."% (key, value, idx))
277 else:
278 logger.prn_err("found faulty SYNC in stream: {{%s;%s}}, ignored..."% (key, value))
279 logger.prn_inf("Resetting the part and sync timeout to clear out the buffer...")
280 connector.reset()
281 loop_timer = time()
282 else:
283 logger.prn_wrn("found KV pair in stream: {{%s;%s}}, ignoring..."% (key, value))
284
285 if not sync_uuid_discovered:
286 # Resending __sync after 'sync_timeout' secs (default 1 sec)
287 # to target platform. If 'sync_behavior' counter is != 0 we
288 # will continue to send __sync packets to target platform.
289 # If we specify 'sync_behavior' < 0 we will send 'forever'
290 # (or until we get reply)
291
292 if sync_behavior != 0:
293 time_to_sync_again = time() - loop_timer
294 if time_to_sync_again > sync_timeout:
295 sync_uuid = __send_sync(timeout=time_to_sync_again)
296
297 if sync_uuid:
298 sync_uuid_list.append(sync_uuid)
299 sync_behavior -= 1
300 loop_timer = time()
301 #Sync behavior will be zero and if last sync fails we should report connection
302 #lost
303 if sync_behavior == 0:
304 last_sync = True
305 else:
306 __notify_conn_lost()
307 break
308 elif last_sync == True:
309 #SYNC lost connection event : Device not responding, send sync failed
310 __notify_sync_failed()
311 break
312
313 return 0
Simple auxiliary class used to walk through a buffer and search for KV tokens.
Definition conn_proxy.py:33
search(self)
Check if there is a KV value in buffer.
Definition conn_proxy.py:75
append(self, payload)
Append stream buffer with payload and process.
Definition conn_proxy.py:41
conn_primitive_factory(conn_resource, config, event_queue, logger)
Factory producing connectors based on type and config.
Definition conn_proxy.py:85
conn_process(event_queue, dut_event_queue, config)