Browse Source

Added get_source_cap and PDO support

Now there are namedtuples for PDO types, and two functions to help with
reading PDOs from the Sink.  They're all tested, and our test coverage
is now at 98%(!).
Clara Hobbs 7 years ago
parent
commit
066f44f92f
3 changed files with 239 additions and 0 deletions
  1. 1
    0
      README.rst
  2. 128
    0
      pdbuddy/__init__.py
  3. 110
    0
      test_pdbuddy/__init__.py

+ 1
- 0
README.rst View File

@@ -12,6 +12,7 @@ Features
12 12
 -  SinkConfig objects can be manipulated locally and written to the
13 13
    device with one method call
14 14
 -  Allows control of whether or not the output is enabled
15
+-  Provides a Pythonic interface for reading advertised PDOs
15 16
 
16 17
 Examples
17 18
 --------

+ 128
- 0
pdbuddy/__init__.py View File

@@ -180,6 +180,9 @@ class Sink:
180 180
         else:
181 181
             self.send_command("output disable")
182 182
 
183
+    def get_source_cap(self):
184
+        """Gets the most recent Source_Capabilities read by the Sink"""
185
+        return read_pdo_list(self.send_command("get_source_cap"))
183 186
 
184 187
     def set_tmpcfg(self, sc):
185 188
         """Writes a SinkConfig object to the device's configuration buffer
@@ -320,3 +323,128 @@ class SinkFlags(enum.Flag):
320 323
     """Flags field of a PD Buddy Sink configuration object"""
321 324
     NONE = 0
322 325
     GIVEBACK = enum.auto()
326
+
327
+
328
+class UnknownPDO(namedtuple("UnknownPDO", "value")):
329
+    """A PDO of an unknown type
330
+
331
+    ``value`` should be a 32-bit integer representing the PDO exactly as
332
+    transmitted.
333
+    """
334
+    __slots__ = ()
335
+
336
+    pdo_type = "unknown"
337
+
338
+    def __str__(self):
339
+        """Print the UnknownPDO in the manner of the configuration shell"""
340
+        return "{:08X}".format(self.value)
341
+
342
+
343
+class SrcFixedPDO(namedtuple("SrcFixedPDO", "dual_role_pwr usb_suspend "
344
+        "unconstrained_pwr usb_comms dual_role_data peak_i v i")):
345
+    """A Source Fixed PDO
346
+
347
+    ``dual_role_pwr``, ``usb_suspend``, ``unconstrained_pwr``,
348
+    ``usb_comms``, and ``dual_role_data`` should be booleans.  ``peak_i``
349
+    should be an integer in the range [0, 3].  ``v`` is the voltage in
350
+    millivolts, and ``i`` is the maximum current in milliamperes.
351
+    """
352
+    __slots__ = ()
353
+
354
+    pdo_type = "fixed"
355
+
356
+    def __str__(self):
357
+        """Print the SrcFixedPDO in the manner of the configuration shell"""
358
+        s = self.pdo_type + "\n"
359
+
360
+        if self.dual_role_pwr:
361
+            s += "\tdual_role_pwr: 1\n"
362
+
363
+        if self.usb_suspend:
364
+            s += "\tusb_suspend: 1\n"
365
+
366
+        if self.unconstrained_pwr:
367
+            s += "\tunconstrained_pwr: 1\n"
368
+
369
+        if self.usb_comms:
370
+            s += "\tusb_comms: 1\n"
371
+
372
+        if self.dual_role_data:
373
+            s += "\tdual_role_data: 1\n"
374
+
375
+        if self.peak_i:
376
+            s += "\tpeak_i: {}\n".format(self.peak_i)
377
+
378
+        s += "\tv: {:.2f} V\n".format(self.v / 1000)
379
+        s += "\ti: {:.2f} A".format(self.i / 1000)
380
+
381
+        return s
382
+
383
+
384
+def read_pdo(text):
385
+    """Create a PDO object from partial text returned by Sink.send_command"""
386
+    # First, determine the PDO type
387
+    pdo_type = text[0].split(b":")[-1].strip().decode("utf-8")
388
+
389
+    if pdo_type == SrcFixedPDO.pdo_type:
390
+        # Set default values (n.b. there are none for v and i)
391
+        dual_role_pwr = False
392
+        usb_suspend = False
393
+        unconstrained_pwr = False
394
+        usb_comms = False
395
+        dual_role_data = False
396
+        peak_i = 0
397
+
398
+        # Load a SrcFixedPDO
399
+        for line in text[1:]:
400
+            fields = line.split(b":")
401
+            fields[0] = fields[0].strip()
402
+            fields[1] = fields[1].strip()
403
+            if fields[0] == b"dual_role_pwr":
404
+                dual_role_pwr = (fields[1] == b"1")
405
+            elif fields[0] == b"usb_suspend":
406
+                usb_suspend = (fields[1] == b"1")
407
+            elif fields[0] == b"unconstrained_pwr":
408
+                unconstrained_pwr = (fields[1] == b"1")
409
+            elif fields[0] == b"usb_comms":
410
+                usb_comms = (fields[1] == b"1")
411
+            elif fields[0] == b"dual_role_data":
412
+                dual_role_data = (fields[1] == b"1")
413
+            elif fields[0] == b"peak_i":
414
+                peak_i = int(fields[1])
415
+            elif fields[0] == b"v":
416
+                v = round(1000*float(fields[1].split()[0]))
417
+            elif fields[0] == b"i":
418
+                i = round(1000*float(fields[1].split()[0]))
419
+
420
+        # Make the SrcFixedPDO
421
+        return SrcFixedPDO(
422
+                dual_role_pwr=dual_role_pwr,
423
+                usb_suspend=usb_suspend,
424
+                unconstrained_pwr=unconstrained_pwr,
425
+                usb_comms=usb_comms,
426
+                dual_role_data=dual_role_data,
427
+                peak_i=peak_i,
428
+                v=v,
429
+                i=i)
430
+    else:
431
+        # Make an UnknownPDO
432
+        return UnknownPDO(value=int(pdo_type, 16))
433
+
434
+
435
+def read_pdo_list(text):
436
+    """Create a list of PDOs from text returned by Sink.send_command"""
437
+    # Get the lines where PDOs start
438
+    pdo_start_list = []
439
+    for index, line in enumerate(text):
440
+        if not line.startswith(b"\t"):
441
+            pdo_start_list.append(index)
442
+    # Append the number of lines so the last slice will work right
443
+    pdo_start_list.append(len(text))
444
+
445
+    # Read the PDOs
446
+    pdo_list = []
447
+    for start, end in zip(pdo_start_list[:-1], pdo_start_list[1:]):
448
+        pdo_list.append(read_pdo(text[start:end]))
449
+
450
+    return pdo_list

+ 110
- 0
test_pdbuddy/__init__.py View File

@@ -240,3 +240,113 @@ class SinkConfigTestCase(unittest.TestCase):
240 240
                 b"v: 15.00 V",
241 241
                 b"i: 3.00 A"])
242 242
         self.assertEqual(ft_valid, self.obj_valid)
243
+
244
+
245
+class UnknownPDOTestCase(unittest.TestCase):
246
+
247
+    def setUp(self):
248
+        self.obj_zero = pdbuddy.UnknownPDO(value=0x00000000)
249
+        self.obj_notzero = pdbuddy.UnknownPDO(value=0xFFFFFFFF)
250
+
251
+    def test_str_zero(self):
252
+        self.assertEqual(str(self.obj_zero), "00000000")
253
+
254
+    def test_str_notzero(self):
255
+        self.assertEqual(str(self.obj_notzero), "FFFFFFFF")
256
+
257
+
258
+class SrcFixedPDOTestCase(unittest.TestCase):
259
+
260
+    def setUp(self):
261
+        self.obj_everything = pdbuddy.SrcFixedPDO(True, True, True, True, True,
262
+                3, 20000, 5000)
263
+        self.obj_minimal = pdbuddy.SrcFixedPDO(False, False, False, False,
264
+                False, 0, 5000, 1500)
265
+
266
+    def test_str_everything(self):
267
+        self.assertEqual(str(self.obj_everything),
268
+                "fixed\n\tdual_role_pwr: 1\n\tusb_suspend: 1\n"
269
+                "\tunconstrained_pwr: 1\n\tusb_comms: 1\n\tdual_role_data: 1\n"
270
+                "\tpeak_i: 3\n\tv: 20.00 V\n\ti: 5.00 A")
271
+
272
+    def test_str_minimal(self):
273
+        self.assertEqual(str(self.obj_minimal),
274
+                "fixed\n\tv: 5.00 V\n\ti: 1.50 A")
275
+
276
+
277
+class ReadPDOTestCase(unittest.TestCase):
278
+
279
+    def setUp(self):
280
+        self.src_fixed_everything = pdbuddy.SrcFixedPDO(True, True, True, True,
281
+                True, 3, 20000, 5000)
282
+        self.src_fixed_minimal = pdbuddy.SrcFixedPDO(False, False, False,
283
+                False, False, 0, 5000, 1500)
284
+        self.unknown_zero = pdbuddy.UnknownPDO(value=0x00000000)
285
+        self.unknown_notzero = pdbuddy.UnknownPDO(value=0xFFFFFFFF)
286
+
287
+    def test_read_src_fixed_everything(self):
288
+        rp_src_fixed_everything = pdbuddy.read_pdo([b"PDO 1: fixed",
289
+                b"\tdual_role_pwr: 1",
290
+                b"\tusb_suspend: 1",
291
+                b"\tunconstrained_pwr: 1",
292
+                b"\tusb_comms: 1",
293
+                b"\tdual_role_data: 1",
294
+                b"\tpeak_i: 3",
295
+                b"\tv: 20.00 V",
296
+                b"\ti: 5.00 A"])
297
+        self.assertEqual(self.src_fixed_everything, rp_src_fixed_everything)
298
+
299
+    def test_read_src_fixed_minimal(self):
300
+        rp_src_fixed_minimal = pdbuddy.read_pdo([b"PDO 1: fixed",
301
+                b"\tv: 5.00 V",
302
+                b"\ti: 1.50 A"])
303
+        self.assertEqual(self.src_fixed_minimal, rp_src_fixed_minimal)
304
+
305
+    def test_read_src_fixed_minimal_no_index(self):
306
+        rp_src_fixed_minimal = pdbuddy.read_pdo([b"fixed",
307
+                b"\tv: 5.00 V",
308
+                b"\ti: 1.50 A"])
309
+        self.assertEqual(self.src_fixed_minimal, rp_src_fixed_minimal)
310
+
311
+    def test_read_unknown_zero(self):
312
+        rp_unknown_zero = pdbuddy.read_pdo([b"PDO 1: 00000000"])
313
+        self.assertEqual(self.unknown_zero, rp_unknown_zero)
314
+
315
+    def test_read_unknown_notzero(self):
316
+        rp_unknown_notzero = pdbuddy.read_pdo([b"PDO 1: FFFFFFFF"])
317
+        self.assertEqual(self.unknown_notzero, rp_unknown_notzero)
318
+
319
+
320
+class ReadPDOListTestCase(unittest.TestCase):
321
+
322
+    def setUp(self):
323
+        self.src_fixed_everything = pdbuddy.SrcFixedPDO(True, True, True, True,
324
+                True, 3, 20000, 5000)
325
+        self.src_fixed_minimal = pdbuddy.SrcFixedPDO(False, False, False,
326
+                False, False, 0, 5000, 1500)
327
+        self.unknown_zero = pdbuddy.UnknownPDO(value=0x00000000)
328
+        self.unknown_notzero = pdbuddy.UnknownPDO(value=0xFFFFFFFF)
329
+
330
+    def test_read_pdo_list(self):
331
+        # It's not a legal list for USB Power Delivery, but it works fine for
332
+        # testing this code
333
+        text = [b"PDO 1: fixed",
334
+                b"\tdual_role_pwr: 1",
335
+                b"\tusb_suspend: 1",
336
+                b"\tunconstrained_pwr: 1",
337
+                b"\tusb_comms: 1",
338
+                b"\tdual_role_data: 1",
339
+                b"\tpeak_i: 3",
340
+                b"\tv: 20.00 V",
341
+                b"\ti: 5.00 A",
342
+                b"PDO 2: fixed",
343
+                b"\tv: 5.00 V",
344
+                b"\ti: 1.50 A",
345
+                b"PDO 3: 00000000",
346
+                b"PDO 4: FFFFFFFF"]
347
+        pdo_list = pdbuddy.read_pdo_list(text)
348
+
349
+        self.assertEqual(pdo_list[0], self.src_fixed_everything)
350
+        self.assertEqual(pdo_list[1], self.src_fixed_minimal)
351
+        self.assertEqual(pdo_list[2], self.unknown_zero)
352
+        self.assertEqual(pdo_list[3], self.unknown_notzero)

Loading…
Cancel
Save