예제 #1
0
 def setUpClass(cls):
     FtdiTestCase.setUpClass()
     if VirtLoader:
         cls.loader = VirtLoader()
         with open('pyftdi/tests/resources/ft232r.yaml', 'rb') as yfp:
             cls.loader.load(yfp)
         vftdi = cls.loader.get_virtual_ftdi(1, 1)
         vport = vftdi.get_port(1)
         # create virtual connections as real HW
         in_pins = [vport[pos] for pos in range(4)]
         out_pins = [vport[pos] for pos in range(4, 8)]
         for in_pin, out_pin in zip(in_pins, out_pins):
             out_pin.connect_to(in_pin)
     if cls.url == 'ftdi:///1':
         # assumes that if not specific device is used, and a multiport
         # device is connected, there is no loopback wires between pins of
         # the same port. This hack allows to run the same test with a
         # FT232H, then a FT2232H for ex, to that with two test sessions
         # the whole test set is run. If a specific device is selected
         # assume the HW always match the expected configuration.
         ftdi = Ftdi()
         ftdi.open_from_url(cls.url)
         count = ftdi.device_port_count
         ftdi.close()
         cls.skip_loopback = count > 1
     else:
         cls.skip_loopback = False
예제 #2
0
 def setUpClass(cls):
     FtdiTestCase.setUpClass()
     if VirtLoader:
         cls.loader = VirtLoader()
         with open('pyftdi/tests/resources/ft2232h.yaml', 'rb') as yfp:
             cls.loader.load(yfp)
         vftdi = cls.loader.get_virtual_ftdi(1, 1)
         vport1 = vftdi.get_port(1)
         vport2 = vftdi.get_port(2)
         # create virtual connections as real HW
         in_pins = [vport1[pos] for pos in range(8)]
         out_pins = [vport2[pos] for pos in range(8)]
         for in_pin, out_pin in zip(in_pins, out_pins):
             out_pin.connect_to(in_pin)
     ftdi = Ftdi()
     ftdi.open_from_url(cls.url)
     count = ftdi.device_port_count
     pos = ftdi.port_index
     ftdi.close()
     if pos != 1:
         raise ValueError("FTDI interface should be the device's first")
     if count < 2:
         raise SkipTest('FTDI device is not a multi-port device')
     url = cls.url[:-1]
     cls.urls = [f'{url}1', f'{url}2']
예제 #3
0
 def setUpClass(cls):
     FtdiTestCase.setUpClass()
     if VirtLoader:
         cls.loader = VirtLoader()
         with open('pyftdi/tests/resources/ft2232h.yaml', 'rb') as yfp:
             cls.loader.load(yfp)
         vftdi = cls.loader.get_virtual_ftdi(1, 1)
         vport1 = vftdi.get_port(1)
         vport2 = vftdi.get_port(2)
         # create virtual connections as real HW
         in_pins = [vport1[pos] for pos in range(16)]
         out_pins = [vport2[pos] for pos in range(16)]
         for in_pin, out_pin in zip(in_pins, out_pins):
             out_pin.connect_to(in_pin)
         # prevent from using the tracer twice (Ftdi & VirtualFtdi)
         cls.debug_mpsse = False
     else:
         cls.debug_mpsse = cls.debug
     url = environ.get('FTDI_DEVICE', 'ftdi:///1')
     ftdi = Ftdi()
     ftdi.open_from_url(url)
     count = ftdi.device_port_count
     width = ftdi.port_width
     ftdi.close()
     if count < 2:
         raise SkipTest('FTDI device is not a multi-port device')
     if width < 2:
         raise SkipTest('FTDI device does not support wide ports')
     url = url[:-1]
     cls.urls = [f'{url}1', f'{url}2']
예제 #4
0
    def test_output_gpio(self):
        """Simple test to demonstrate ouput bit-banging on CBUS.

           You need a CBUS-capable FTDI (FT232R/FT232H/FT230X/FT231X), whose
           EEPROM has been configured to support GPIOs on CBUS0 and CBUS3.

           Hard-wiring is required to run this test:
           * CBUS0 (output) should be connected to CTS (input)
           * CBUS3 (output) should be connected to DSR (input)
        """
        ftdi = Ftdi()
        ftdi.open_from_url(self.url)
        # sanity check: device should support CBUS feature
        self.assertEqual(ftdi.has_cbus, True)
        eeprom = FtdiEeprom()
        eeprom.connect(ftdi)
        # sanity check: device should have been configured for CBUS GPIOs
        self.assertEqual(eeprom.cbus_mask & 0b1001, 0b1001)
        # configure CBUS0 and CBUS3 as output
        ftdi.set_cbus_direction(0b1001, 0b1001)
        # no input pin available
        self.assertRaises(FtdiError, ftdi.get_cbus_gpio)
        for cycle in range(40):
            value = cycle & 0x3
            # CBUS0 and CBUS3
            cbus = ((value & 0x2) << 2) | value & 0x1
            # for now, need a digital/logic analyzer to validate output
            ftdi.set_cbus_gpio(cbus)
            # CBUS0 is connected to CTS, CBUS3 to DSR
            # need to inverse logical level as RS232 uses negative logic
            sig = int(not ftdi.get_cts()) | (int(not ftdi.get_dsr()) << 1)
            self.assertEqual(value, sig)
예제 #5
0
    def test_input_gpio(self):
        """Simple test to demonstrate input bit-banging on CBUS.

           You need a CBUS-capable FTDI (FT232R/FT232H/FT230X/FT231X), whose
           EEPROM has been configured to support GPIOs on CBUS0 and CBUS3.

           Hard-wiring is required to run this test:
           * CBUS0 (input) should be connected to RTS (output)
           * CBUS3 (input) should be connected to DTR (output)
        """
        ftdi = Ftdi()
        ftdi.open_from_url(self.url)
        # sanity check: device should support CBUS feature
        self.assertEqual(ftdi.has_cbus, True)
        eeprom = FtdiEeprom()
        eeprom.connect(ftdi)
        # sanity check: device should have been configured for CBUS GPIOs
        self.assertEqual(eeprom.cbus_mask & 0b1001, 0b1001)
        # configure CBUS0 and CBUS3 as input
        ftdi.set_cbus_direction(0b1001, 0b0000)
        # no output pin available
        self.assertRaises(FtdiError, ftdi.set_cbus_gpio, 0)
        for cycle in range(40):
            rts = bool(cycle & 0x1)
            dtr = bool(cycle & 0x2)
            ftdi.set_rts(rts)
            ftdi.set_dtr(dtr)
            # need to inverse logical level as RS232 uses negative logic
            cbus = ~ftdi.get_cbus_gpio()
            sig = (cbus & 0x1) | ((cbus & 0x8) >> 2)
            value = cycle & 0x3
            self.assertEqual(value, sig)
예제 #6
0
파일: ftdi.py 프로젝트: zhang-xuting/pyftdi
 def test_close_on_disconnect(self):
     """Validate close after disconnect."""
     log = logging.getLogger('pyftdi.tests.ftdi')
     url = environ.get('FTDI_DEVICE', 'ftdi:///1')
     ftdi = Ftdi()
     ftdi.open_from_url(url)
     self.assertTrue(ftdi.is_connected, 'Unable to connect to FTDI')
     print('Please disconnect FTDI device')
     while ftdi.is_connected:
         try:
             ftdi.poll_modem_status()
         except FtdiError:
             break
         sleep(0.1)
     ftdi.close()
     print('Please reconnect FTDI device')
     while True:
         UsbTools.flush_cache()
         try:
             ftdi.open_from_url(url)
         except (FtdiError, UsbToolsError):
             log.debug('FTDI device not detected')
             sleep(0.1)
         except ValueError:
             log.warning('FTDI device not initialized')
             ftdi.close()
             sleep(0.1)
         else:
             log.info('FTDI device detected')
             break
     ftdi.poll_modem_status()
     ftdi.close()
예제 #7
0
파일: uart.py 프로젝트: naushir/pyftdi
 def test(self):
     ftdi = Ftdi()
     ftdi.open_from_url(URL)
     for baudrate in self.BAUDRATES:
         actual, _, _ = ftdi._convert_baudrate(baudrate)
         ratio = baudrate / actual
         self.assertTrue(0.97 <= ratio <= 1.03, "Invalid baudrate")
예제 #8
0
 def test_eeprom_read(self):
     """Check full read sequence."""
     ftdi = Ftdi()
     ftdi.open_from_url('ftdi:///1')
     data = ftdi.read_eeprom()
     self.assertEqual(len(data), 0x400)
     ftdi.close()
예제 #9
0
 def test_dump(self):
     """Check EEPROM full content."""
     ftdi = Ftdi()
     ftdi.open_from_url('ftdi:///1')
     self._restore_eeprom(ftdi)
     ref_data = bytes(list(range(256)))
     size = len(ref_data)
     data = ftdi.read_eeprom()
     self.assertEqual(len(data), size)
     self.assertEqual(ref_data, data)
     ftdi.close()
예제 #10
0
 def setUpClass(cls):
     FtdiTestCase.setUpClass()
     if VirtLoader:
         cls.loader = VirtLoader()
         with open(cls.TEST_CONFIG_FILENAME, 'rb') as yfp:
             cls.loader.load(yfp)
     if cls.url == 'ftdi:///1':
         ftdi = Ftdi()
         ftdi.open_from_url(cls.url)
         count = ftdi.device_port_count
         ftdi.close()
예제 #11
0
파일: ftdi.py 프로젝트: zhang-xuting/pyftdi
 def test_simple_reset(self):
     """Demonstrate how to connect to an hotplugged FTDI device, i.e.
        an FTDI device that is connected after the initial attempt to
        enumerate it on the USB bus."""
     url = environ.get('FTDI_DEVICE', 'ftdi:///1')
     ftdi = Ftdi()
     ftdi.open_from_url(url)
     self.assertTrue(ftdi.is_connected, 'Unable to connect to FTDI')
     ftdi.close()
     self.assertFalse(ftdi.is_connected, 'Unable to close connection')
     ftdi.open_from_url(url)
     self.assertTrue(ftdi.is_connected, 'Unable to connect to FTDI')
     ftdi.reset(False)
예제 #12
0
파일: ftdi.py 프로젝트: zhang-xuting/pyftdi
 def test_hotplug_discovery(self):
     """Demonstrate how to connect to an hotplugged FTDI device, i.e.
        an FTDI device that is connected after the initial attempt to
        enumerate it on the USB bus."""
     url = environ.get('FTDI_DEVICE', 'ftdi:///1')
     ftdi = Ftdi()
     timeout = now() + 5.0  # sanity check: bail out after 10 seconds
     while now() < timeout:
         try:
             ftdi.open_from_url(url)
             break
         except UsbToolsError:
             UsbTools.flush_cache()
             sleep(0.05)
             continue
     self.assertTrue(ftdi.is_connected, 'Unable to connect to FTDI')
     print('Connected to FTDI', url)
예제 #13
0
 def test_230x(self):
     """Check simple GPIO write and read sequence."""
     # load custom CBUS config, with:
     # CBUS0: GPIO (gpio)
     # CBUS1: GPIO (gpio)
     # CBUS0: DRIVE1 (forced to high level)
     # CBUS0: TXLED  (eq. to highz for tests)
     with open('pyftdi/tests/resources/ft230x_io.yaml', 'rb') as yfp:
         self.loader.load(yfp)
     ftdi = Ftdi()
     ftdi.open_from_url('ftdi:///1')
     self.assertEqual(ftdi.has_cbus, True)
     vftdi = self.loader.get_virtual_ftdi(1, 1)
     vport = vftdi.get_port(1)
     # CBUS0: in, CBUS1: out, CBUS2: in, CBUS3: out
     #   however, only CBUS0 and CBUS1 are mapped as GPIO,
     #   CBUS2 forced to 1 and CBUS3 not usable as IO
     #   even if use mask is 1111
     eeprom_mask = 0b0011
     eeprom_force = 0b0100
     cbus_mask = 0b1111
     cbus_dir = 0b1010
     ftdi.set_cbus_direction(cbus_mask, cbus_dir)
     cbus_out = 0b0011
     # CBUS0: 1, CBUS1: 1
     #   however, only CBUS1 is out, so CBUS0 output value should be ignored
     ftdi.set_cbus_gpio(cbus_out)
     exp_out = cbus_dir & cbus_out
     exp_out &= eeprom_mask
     exp_out |= eeprom_force
     vcbus, vactive = vport.cbus
     self.assertEqual(vcbus, exp_out)
     self.assertEqual(vactive, eeprom_mask | eeprom_force)
     cbus_out = 0b0000
     ftdi.set_cbus_gpio(cbus_out)
     exp_out = cbus_dir & cbus_out
     exp_out &= eeprom_mask
     exp_out |= eeprom_force
     vcbus, vactive = vport.cbus
     self.assertEqual(vcbus, exp_out)
     cbus_in = 0b0101
     vport.cbus = cbus_in
     cbus = ftdi.get_cbus_gpio()
     exp_in = cbus_in & eeprom_mask
     self.assertEqual(cbus, exp_in)
     ftdi.close()
예제 #14
0
 def test_lc231x(self):
     """Check simple GPIO write and read sequence."""
     # load custom CBUS config, with:
     # CBUS0: GPIO (gpio)
     # CBUS1: TXLED
     # CBUS2: DRIVE0 (to light up RX green led)
     # CBUS3: GPIO (gpio)
     # only CBUS0 and CBUS3 are available on LC231X
     # CBUS1 is connected to TX led, CBUS2 to RX led
     with open('pyftdi/tests/resources/ft231x_cbus.yaml', 'rb') as yfp:
         self.loader.load(yfp)
     ftdi = Ftdi()
     ftdi.open_from_url('ftdi:///1')
     self.assertEqual(ftdi.has_cbus, True)
     vftdi = self.loader.get_virtual_ftdi(1, 1)
     vport = vftdi.get_port(1)
     # CBUS0: in, CBUS1: out, CBUS2: in, CBUS3: out
     #   however, only CBUS0 and CBUS3 are mapped as GPIO,
     #   CBUS1 not usable as IO, CBUS2 is fixed to low
     #   even if use mask is 1111
     eeprom_mask = 0b1001
     eeprom_force_low = 0b0100
     cbus_mask = 0b1111
     cbus_dir = 0b1010
     ftdi.set_cbus_direction(cbus_mask, cbus_dir)
     cbus_out = 0b1111
     # however, only CBUS0 & 3 are out, so CBUS1/CBUS2 should be ignored
     ftdi.set_cbus_gpio(cbus_out)
     exp_out = cbus_dir & cbus_out
     exp_out &= eeprom_mask
     vcbus, vactive = vport.cbus
     self.assertEqual(vcbus, exp_out)
     self.assertEqual(vactive, eeprom_mask | eeprom_force_low)
     cbus_out = 0b0000
     ftdi.set_cbus_gpio(cbus_out)
     exp_out = cbus_dir & cbus_out
     exp_out &= eeprom_mask
     vcbus, vactive = vport.cbus
     self.assertEqual(vcbus, exp_out)
     cbus_in = 0b0101
     vport.cbus = cbus_in
     cbus = ftdi.get_cbus_gpio()
     exp_in = cbus_in & eeprom_mask
     self.assertEqual(cbus, exp_in)
     ftdi.close()
예제 #15
0
 def test_randow_access_write(self):
     """Check EEPROM random write access."""
     ftdi = Ftdi()
     ftdi.open_from_url('ftdi:///1')
     bus, address, _ = ftdi.usb_path
     vftdi = self.loader.get_virtual_ftdi(bus, address)
     self._restore_eeprom(ftdi)
     checksum1 = vftdi.eeprom[-2:]
     orig_data = vftdi.eeprom[:8]
     ref_data = b'ABCD'
     ftdi.write_eeprom(0, ref_data, dry_run=False)
     checksum2 = vftdi.eeprom[-2:]
     # verify the data have been written
     self.assertEqual(vftdi.eeprom[:4], ref_data)
     # verify the data have not been overwritten
     self.assertEqual(vftdi.eeprom[4:8], orig_data[4:])
     # verify the checksum has been updated
     # TODO compute the expected checksum
     self.assertNotEqual(checksum1, checksum2)
     checksum1 = vftdi.eeprom[-2:]
     orig_data = vftdi.eeprom[:24]
     ftdi.write_eeprom(9, ref_data, dry_run=False)
     checksum2 = vftdi.eeprom[-2:]
     # verify the unaligned data have been written
     self.assertEqual(vftdi.eeprom[9:13], ref_data)
     # verify the data have not been overwritten
     self.assertEqual(vftdi.eeprom[:9], orig_data[:9])
     self.assertEqual(vftdi.eeprom[13:24], orig_data[13:])
     # verify the checksum has been updated
     self.assertNotEqual(checksum1, checksum2)
     checksum1 = vftdi.eeprom[-2:]
     orig_data = vftdi.eeprom[:48]
     ftdi.write_eeprom(33, ref_data[:3], dry_run=False)
     checksum2 = vftdi.eeprom[-2:]
     # verify the unaligned data have been written
     self.assertEqual(vftdi.eeprom[33:36], ref_data[:3])
     # verify the data have not been overwritten
     self.assertEqual(vftdi.eeprom[:33], orig_data[:33])
     self.assertEqual(vftdi.eeprom[36:48], orig_data[36:])
     # verify the checksum has been updated
     self.assertNotEqual(checksum1, checksum2)
예제 #16
0
파일: ftdi.py 프로젝트: zhang-xuting/pyftdi
 def test_dual_if_reset(self):
     """Demonstrate how to connect to an hotplugged FTDI device, i.e.
        an FTDI device that is connected after the initial attempt to
        enumerate it on the USB bus."""
     url1 = environ.get('FTDI_DEVICE', 'ftdi:///1')
     ftdi1 = Ftdi()
     ftdi1.open_from_url(url1)
     count = ftdi1.device_port_count
     if count < 2:
         ftdi1.close()
         raise SkipTest('FTDI device is not a multi-port device')
     next_port = (int(url1[-1]) % count) + 1
     url2 = 'ftdi:///%d' % next_port
     ftdi2 = Ftdi()
     self.assertTrue(ftdi1.is_connected, 'Unable to connect to FTDI')
     ftdi2.open_from_url(url2)
     # use latenty setting to set/test configuration is preserved
     ftdi2.set_latency_timer(128)
     # should be the same value
     self.assertEqual(ftdi2.get_latency_timer(), 128)
     self.assertTrue(ftdi2.is_connected, 'Unable to connect to FTDI')
     ftdi1.close()
     self.assertFalse(ftdi1.is_connected, 'Unable to close connection')
     # closing first connection should not alter second interface
     self.assertEqual(ftdi2.get_latency_timer(), 128)
     ftdi1.open_from_url(url1)
     self.assertTrue(ftdi1.is_connected, 'Unable to connect to FTDI')
     # a FTDI reset should not alter settings...
     ftdi1.reset(False)
     self.assertEqual(ftdi2.get_latency_timer(), 128)
     # ... however performing a USB reset through any interface should alter
     # any previous settings made to all interfaces
     ftdi1.reset(True)
     self.assertNotEqual(ftdi2.get_latency_timer(), 128)
예제 #17
0
 def test_random_access_read(self):
     """Check EEPROM random read access."""
     ftdi = Ftdi()
     ftdi.open_from_url('ftdi:///1')
     self._restore_eeprom(ftdi)
     ref_data = bytes(list(range(256)))
     size = len(ref_data)
     # out of bound
     self.assertRaises(ValueError, ftdi.read_eeprom, size, 2)
     # last bytes
     buf = ftdi.read_eeprom(size - 2, 2)
     self.assertEqual(buf[0:2], ref_data[-2:])
     self.assertEqual(buf[0:2], b'\xfe\xff')
     # out of bound
     self.assertRaises(ValueError, ftdi.read_eeprom, size - 2, 4)
     # unaligned access
     buf = ftdi.read_eeprom(1, 2)
     self.assertEqual(buf[0:2], ref_data[1:3])
     self.assertEqual(buf[0:2], b'\x01\x02')
     # long read, unaligned access, unaligned size
     buf = ftdi.read_eeprom(43, 43)
     self.assertEqual(len(buf), 43)
     self.assertEqual(buf, ref_data[43:86])
     ftdi.close()
예제 #18
0
파일: mockusb.py 프로젝트: cruizdeg/pyftdi
 def test_open_close(self):
     """Check simple open/close sequence."""
     ftdi = Ftdi()
     ftdi.open_from_url('ftdi:///1')
     self.assertEqual(ftdi.usb_path, (4, 5, 0))
     ftdi.close()
예제 #19
0
class ReaderBoard:
    _unlockTimeouts: Dict[int, List[LockTimeout]]

    def __init__(self, deviceid):
        """

        :rtype: object
        """
        # self.input = queue.Queue(20)
        self.packetCallback = None
        self.errorCallback = None

        self._commandLock = Lock()
        self._packetReadEvent = Event()
        self._startedEvent = Event()
        self._stoppedEvent = Event()
        self._run = True
        self.device_id = deviceid
        self._ftdi = Ftdi()
        self.numScanners = 0
        self.numRelays = 0
        self.relaystatus = {}
        self.version = "0.00"
        self.model = "unknown"
        self._unlockTimeouts = {}

        self._parserState = ParserState.BEGIN
        self._firstChar = ' '
        self._body = bytearray()

        # self.ftdi.open(vendor=0x0403,product=0x6001, serial=self.device)
        self._ftdi.open_from_url(self.device_id)
        self._ftdi.set_baudrate(57600)

        self._backgroundThread = Thread(target=self.background)
        self._backgroundThread.start()
        self._otherThread = Thread(target=self.lock_watch_loop)
        self._otherThread.start()

        if not self._startedEvent.wait(3.0):
            raise RuntimeError("Unable to startup board!")
        else:
            logger.log(logging.DEBUG, "Started up, disabling echo")
            self.send_command('e', '0')

            def get_device_info(fc, b, me):
                if fc == 'I':
                    info = {i.split(':')[0]: i.split(':')[1] for i in b.split(',')}
                    self.model = info['m']
                    self.version = info['v']
                    self.numRelays = int(info['r'])
                    self.numScanners = int(info['s'])
                    for a in range(self.numRelays):
                        self._unlockTimeouts[a] = []
                        self.relaystatus[a] = False

            self.packetCallback = get_device_info
            logger.info("calling getting device info")
            self.send_command('i', '')
            logger.info("done interrogating")

            self.packetCallback = None

    def shutdown(self):
        self._run = False
        self._commandLock.acquire()
        if not self._stoppedEvent.wait(3.0):
            raise TimeoutError("Failed to shut down board in a timely manner")
        self._backgroundThread.join()

    def background(self):
        self._ftdi.set_dtr(False)
        sleep(0.1)
        self._ftdi.set_dtr(True)  # Reset the device
        totaltime = 0
        ready_bytes = bytearray()
        started_up = False
        while totaltime < 5:
            sleep(0.1)
            totaltime += 0.1
            in_bytes = self._ftdi.read_data_bytes(50)
            if len(in_bytes) > 0:
                ready_bytes.extend(in_bytes)
                s = ready_bytes.decode(encoding="utf-8")
                if '? for help' in s:
                    totaltime = 1000
                    started_up = True
                    self._startedEvent.set()
                    try:
                        self.parse_loop()
                    except pyftdi.ftdi.FtdiError as error:
                        logger.fatal(f"USB ERROR! {error}")
                        if self.errorCallback is not None:
                            self.errorCallback(error)

        if not started_up:
            print("Failed to startup device!")
        logger.log(logging.DEBUG, "Shutting down FTDI device...")
        self._ftdi.close()
        self._commandLock.release()
        logger.log(logging.DEBUG, "Done shutting down hardware interface")

    def lock_watch_loop(self):
        if not self._startedEvent.wait(5000):
            logger.fatal("Didn't start up!")
        else:
            while self._run:
                now = time()
                tos: List[LockTimeout]
                for (r, tos) in self._unlockTimeouts.items():
                    if any(tos):
                        for t in tos:
                            if now >= t.timeout:
                                tos.remove(t)
                                if len(tos) == 0:
                                    # close the door
                                    self.send_command('o', str(r))
                                    self.relaystatus[r] = False
                    elif self.relaystatus[r]:
                        self.send_command('o', str(r))
                        self.relaystatus[r] = False
                sleep(0.25)

    def parse_loop(self):
        totaltime = 0
        while self._run:
            sleep(0.1)
            totaltime += 0.1
            in_bytes = self._ftdi.read_data_bytes(50)
            self.parse(in_bytes)

            # Check out open/close queue
        self._stoppedEvent.set()

    def parse(self, in_bytes):
        if len(in_bytes) > 0:
            for b in in_bytes:
                c = chr(b)

                if self._parserState == ParserState.BEGIN or self._parserState == ParserState.FOUND_NEWLINE:
                    self._body.clear()
                    self._firstChar = c
                    self._parserState = ParserState.FOUND_FIRST
                elif self._parserState == ParserState.FOUND_FIRST:
                    if b != 0x0D:  # '\r'
                        self._body.append(b)
                    else:
                        self._parserState = ParserState.FOUND_CARRIAGE
                elif self._parserState == ParserState.FOUND_CARRIAGE:
                    if b != 0x0A:  # \n
                        # RAISE PARSING ERROR
                        self._parserState = ParserState.BEGIN
                    else:
                        self._parserState = ParserState.FOUND_NEWLINE
                        logger.log(logging.DEBUG, f"Read a packet: {self._firstChar}, {self._body}")
                        if self.packetCallback is not None:
                            try:
                                self.packetCallback(self._firstChar, self._body.decode("utf-8"), self.device_id)
                            except Exception as e:
                                logger.log(logging.FATAL, f"Failed calling packet callback: {e}")
                        if not (self._firstChar == 'e' and len(self._body) == 1 and self._body[0] == ord('0')):
                            self._packetReadEvent.set()

    def send_command(self, c: str, data: str):
        if len(c) != 1 or not c.isalpha():
            raise ValueError("c must be a single character!")

        message = f"{c}{data}\r"  # we won't add a \n, serial comms don't do that normally
        self._packetReadEvent.clear()
        d = message.encode("utf-8")
        if self._commandLock.acquire(timeout=1.0):
            if self._run:
                self._ftdi.write_data(d)
                if self._packetReadEvent.wait(3.0):
                    self._commandLock.release()
                    return self._body
                else:
                    logger.log(logging.ERROR, "Failure to get response from board, it's down?")
                    # raise RuntimeError()
                    self._commandLock.release()
            else:
                self._commandLock.release()
        else:
            if self._run:
                raise SystemError("Sync Error")

    def __repr__(self):
        return f"{self.device_id} - {self.model} v{self.version}: {self.numScanners} scanners, {self.numRelays} relays"

    def Unlock(self, relay, duration, credential=None):
        if relay > self.numRelays:
            logger.error(f"Attempt to activate a relay board {self.device_id} doesn't have. {relay}")
            return
        now = time()
        self._unlockTimeouts[relay].append(LockTimeout(now + duration, credential))
        self.send_command('c', str(relay))
        self.relaystatus[relay] = True

    def Lock(self, relay, credential=None):
        if relay > self.numRelays:
            logger.error(f"Attempt to activate a relay board {self.device_id} doesn't have. {relay}")
            return
        if credential is None: #This only is raised by the webpanel, clear our all entries, and the relay
            # will be relocked by the loop
            self._unlockTimeouts[relay].clear()
        else:
            for tos in self._unlockTimeouts[relay]:
                if tos.credential == credential: # Clear all pending timeouts for the credential
                    self._unlockTimeouts[relay].remove(tos)
예제 #20
0
def main():
    ftdi = Ftdi()
    ftdi.open_from_url('ftdi:///?')