|
40 | 40 | use_periphery = False |
41 | 41 | use_serial_lock = False |
42 | 42 |
|
43 | | -if sys.implementation.name == 'cpython': |
44 | | - if sys.platform == 'linux' or sys.platform == 'linux2': |
45 | | - use_periphery = True |
46 | | - from periphery import I2C |
| 43 | +if sys.implementation.name == 'cpython' and (sys.platform == 'linux' or |
| 44 | + sys.platform == 'linux2'): |
47 | 45 |
|
48 | | - use_serial_lock = True |
49 | | - from filelock import Timeout, FileLock |
| 46 | + use_periphery = True |
| 47 | + from periphery import I2C |
| 48 | + |
| 49 | + use_serial_lock = True |
| 50 | + from filelock import FileLock |
| 51 | + from filelock import Timeout as SerialLockTimeout |
| 52 | +else: |
| 53 | + class SerialLockTimeout(Exception): |
| 54 | + pass |
50 | 55 |
|
51 | 56 | use_i2c_lock = not use_periphery and sys.implementation.name != 'micropython' |
52 | 57 |
|
@@ -75,18 +80,28 @@ def _prepare_request(req, debug=False): |
75 | 80 | return req_json |
76 | 81 |
|
77 | 82 |
|
| 83 | +class NullContextManager: |
| 84 | + def __enter__(self): |
| 85 | + pass |
| 86 | + |
| 87 | + def __exit__(self, exc_type, exc_value, traceback): |
| 88 | + pass |
| 89 | + |
| 90 | + |
| 91 | +class NoOpSerialLock(): |
| 92 | + def acquire(*args, **kwargs): |
| 93 | + return NullContextManager() |
| 94 | + |
| 95 | + |
78 | 96 | def serial_lock(fn): |
79 | 97 | """Attempt to get a lock on the serial channel used for Notecard comms.""" |
80 | 98 |
|
81 | 99 | def decorator(self, *args, **kwargs): |
82 | | - if use_serial_lock: |
83 | | - try: |
84 | | - with self.lock.acquire(timeout=5): |
85 | | - return fn(self, *args, **kwargs) |
86 | | - except Timeout: |
87 | | - raise Exception('Notecard in use') |
88 | | - else: |
89 | | - return fn(self, *args, **kwargs) |
| 100 | + try: |
| 101 | + with self.lock.acquire(timeout=5): |
| 102 | + return fn(self, *args, **kwargs) |
| 103 | + except SerialLockTimeout: |
| 104 | + raise Exception('Notecard in use') |
90 | 105 |
|
91 | 106 | return decorator |
92 | 107 |
|
@@ -146,58 +161,67 @@ def SetTransactionPins(self, rtx_pin, ctx_pin): |
146 | 161 | class OpenSerial(Notecard): |
147 | 162 | """Notecard class for Serial communication.""" |
148 | 163 |
|
| 164 | + @serial_lock |
149 | 165 | def _transmit(self, req): |
150 | 166 | req = self._preprocess_req(req) |
151 | 167 | req_json = _prepare_request(req, self._debug) |
152 | 168 |
|
153 | | - transaction_timeout_secs = 30 |
154 | | - if self._transaction_manager: |
155 | | - self._transaction_manager.start(transaction_timeout_secs) |
| 169 | + try: |
| 170 | + transaction_timeout_secs = 30 |
| 171 | + if self._transaction_manager: |
| 172 | + self._transaction_manager.start(transaction_timeout_secs) |
156 | 173 |
|
157 | | - seg_off = 0 |
158 | | - seg_left = len(req_json) |
159 | | - while seg_left > 0: |
160 | | - seg_len = seg_left |
161 | | - if seg_len > CARD_REQUEST_SEGMENT_MAX_LEN: |
162 | | - seg_len = CARD_REQUEST_SEGMENT_MAX_LEN |
| 174 | + seg_off = 0 |
| 175 | + seg_left = len(req_json) |
| 176 | + while seg_left > 0: |
| 177 | + seg_len = seg_left |
| 178 | + if seg_len > CARD_REQUEST_SEGMENT_MAX_LEN: |
| 179 | + seg_len = CARD_REQUEST_SEGMENT_MAX_LEN |
163 | 180 |
|
164 | | - self.uart.write(req_json[seg_off:seg_off + seg_len].encode('utf-8')) |
165 | | - seg_off += seg_len |
166 | | - seg_left -= seg_len |
167 | | - time.sleep(CARD_REQUEST_SEGMENT_DELAY_MS / 1000) |
| 181 | + self.uart.write(req_json[seg_off:seg_off + seg_len].encode('utf-8')) |
| 182 | + seg_off += seg_len |
| 183 | + seg_left -= seg_len |
| 184 | + time.sleep(CARD_REQUEST_SEGMENT_DELAY_MS / 1000) |
| 185 | + finally: |
| 186 | + if self._transaction_manager: |
| 187 | + self._transaction_manager.stop() |
168 | 188 |
|
169 | | - if self._transaction_manager: |
170 | | - self._transaction_manager.stop() |
| 189 | + @serial_lock |
| 190 | + def _transmit_and_receive(self, req): |
| 191 | + self._transmit(req) |
171 | 192 |
|
172 | | - def _read_byte(self): |
173 | | - """Read a single byte from the Notecard.""" |
174 | | - if sys.implementation.name == 'micropython': |
175 | | - if not self.uart.any(): |
176 | | - return None |
177 | | - elif sys.implementation.name == 'cpython': |
178 | | - if self.uart.in_waiting == 0: |
179 | | - return None |
| 193 | + rsp_json = self.uart.readline() |
| 194 | + if self._debug: |
| 195 | + print(rsp_json.rstrip()) |
| 196 | + |
| 197 | + return json.loads(rsp_json) |
| 198 | + |
| 199 | + def _read_byte_micropython(self): |
| 200 | + """Read a single byte from the Notecard (MicroPython).""" |
| 201 | + if not self.uart.any(): |
| 202 | + return None |
| 203 | + return self.uart.read(1) |
| 204 | + |
| 205 | + def _read_byte_cpython(self): |
| 206 | + """Read a single byte from the Notecard (CPython).""" |
| 207 | + if self.uart.in_waiting == 0: |
| 208 | + return None |
| 209 | + return self.uart.read(1) |
| 210 | + |
| 211 | + def _read_byte_circuitpython(self): |
| 212 | + """Read a single byte from the Notecard (CircuitPython).""" |
180 | 213 | return self.uart.read(1) |
181 | 214 |
|
182 | | - @serial_lock |
183 | 215 | def Command(self, req): |
184 | 216 | """Send a command to the Notecard. The Notecard response is ignored.""" |
185 | 217 | if 'cmd' not in req: |
186 | 218 | raise Exception("Please use 'cmd' instead of 'req'") |
187 | 219 |
|
188 | 220 | self._transmit(req) |
189 | 221 |
|
190 | | - @serial_lock |
191 | 222 | def Transaction(self, req): |
192 | 223 | """Perform a Notecard transaction and return the result.""" |
193 | | - self._transmit(req) |
194 | | - |
195 | | - rsp_json = self.uart.readline() |
196 | | - if self._debug: |
197 | | - print(rsp_json.rstrip()) |
198 | | - |
199 | | - rsp = json.loads(rsp_json) |
200 | | - return rsp |
| 224 | + return self._transmit_and_receive(req) |
201 | 225 |
|
202 | 226 | @serial_lock |
203 | 227 | def Reset(self): |
@@ -232,7 +256,18 @@ def __init__(self, uart_id, debug=False): |
232 | 256 | self._debug = debug |
233 | 257 |
|
234 | 258 | if use_serial_lock: |
235 | | - self.lock = FileLock('serial.lock', timeout=1) |
| 259 | + self.lock = FileLock('serial.lock') |
| 260 | + else: |
| 261 | + self.lock = NoOpSerialLock() |
| 262 | + |
| 263 | + if sys.implementation.name == 'micropython': |
| 264 | + self._read_byte = self._read_byte_micropython |
| 265 | + elif sys.implementation.name == 'cpython': |
| 266 | + self._read_byte = self._read_byte_cpython |
| 267 | + elif sys.implementation.name == 'circuitpython': |
| 268 | + self._read_byte = self._read_byte_circuitpython |
| 269 | + else: |
| 270 | + raise NotImplementedError(f'Unsupported platform: {sys.implementation.name}') |
236 | 271 |
|
237 | 272 | self.Reset() |
238 | 273 |
|
|
0 commit comments