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