コード例 #1
0
 def test_pins_read(self, mock_class):
     interface = GPIO(channel=0, frequency=25000, library='wiringPi')
     interface.read_pin([0, 1, 2])
     setup_calls = [call(0, 0), call(1, 0), call(2, 0)]
     interface.handle.pinMode.assert_has_calls(setup_calls)
     read_calls = [call(0), call(1), call(2)]
     interface.handle.digitalRead.assert_has_calls(read_calls)
コード例 #2
0
def test_transmission(mock_class):
    spi = GPIO(channel=0, frequency=25000, library='wiringPi')
    spi.handle.wiringPiSPIDataRW = MagicMock()
    spi.handle.wiringPiSPIDataRW.return_value = 0
    data = [1]
    spi.send_data(data)
    assert spi.handle.wiringPiSPIDataRW.called
コード例 #3
0
def test_incorrect_return_code(mock_class):
    spi = GPIO(channel=0, frequency=25000, library='wiringPi')
    spi.handle.wiringPiSPIDataRW = MagicMock()
    spi.handle.wiringPiSPIDataRW.return_value = -1
    data = bitstring.pack('uint:4', 1)
    with pytest.raises(SPIDataTransmissionError):
        spi.send_data(data)
コード例 #4
0
 def test_pins_read(self, mock_class):
     interface = GPIO(channel=0, frequency=25000, library="wiringPi")
     interface.read_pin([0, 1, 2])
     setup_calls = [call(0, 0), call(1, 0), call(2, 0)]
     interface.handle.pinMode.assert_has_calls(setup_calls)
     read_calls = [call(0), call(1), call(2)]
     interface.handle.digitalRead.assert_has_calls(read_calls)
コード例 #5
0
ファイル: spi_test.py プロジェクト: marcoplaisier/weathervane
 def test_transmission(self, mock_class):
     spi = GPIO(channel=0, frequency=25000, library='wiringPi')
     spi.handle.wiringPiSPIDataRW = MagicMock()
     spi.handle.wiringPiSPIDataRW.return_value = 0
     data = [1]
     spi.send_data(data)
     assert spi.handle.wiringPiSPIDataRW.called
コード例 #6
0
 def __init__(self, *args, **kwargs):
     self.channel = kwargs['channel']
     self.frequency = kwargs['frequency']
     self.gpio = GPIO(**kwargs)
     self.old_bit_string = None
     self.new_bit_string = None
     self.weather_data = {}
     self.bits: List[dict] = kwargs['bits']
     self.stations = kwargs['stations']
コード例 #7
0
 def __init__(self, *args, **kwargs):
     self.channel = kwargs['channel']
     self.frequency = kwargs['frequency']
     self.gpio = GPIO(**kwargs)
     # self.gpio.__init__(channel=self.channel, frequency=self.frequency)
     # TODO: check why I put the call to __init__ separately initially
     self.old_bit_string = None
     self.new_bit_string = None
     self.weather_data = {}
     self.requested_data = kwargs['bits']
     self.stations = kwargs['stations']
コード例 #8
0
def test_pack_with_large_bytes(mock_class):
    spi = GPIO(channel=0, frequency=25000, library='wiringPi')
    data = [-10, -1, 0, 255, 256, 266]
    with pytest.raises(ValueError):
        spi.pack(data=data)
コード例 #9
0
 def test_send(self, mock_loader):
     g = GPIO(channel=0, frequency=0, library='None')
     g.send_data('abc'.encode('UTF-16'))
コード例 #10
0
def test_pack_with_two_elements(mock_class):
    spi = GPIO(channel=0, frequency=25000, library='wiringPi')
    data = [1, 2]
    data_packet, length = spi.pack(data)
    assert [1, 2] == list(data_packet)
    assert 2 == length
コード例 #11
0
def test_pack_with_iterable(mock_class):
    spi = GPIO(channel=0, frequency=25000, library='wiringPi')
    it = list(range(0, 10))
    data_packet, length = spi.pack(data=it)
    assert [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] == list(data_packet)
    assert len(it) == length
コード例 #12
0
    def test_gpio_context_manager(self, mock_loader):
        g = GPIO(channel=0, frequency=10000, library='None', interrupt=0)
        l = [128]
        result, length = g.pack(l)

        self.assertEqual(list(result), l)
コード例 #13
0
def test_pack_with_empty_list(mock_class):
    spi = GPIO(channel=0, frequency=25000, library='wiringPi')
    data = []
    data_packet, length = spi.pack(data)
    assert [] == list(data_packet)
    assert length == 0
コード例 #14
0
class WeatherVaneInterface(object):
    WIND_DIRECTIONS = {'N': 0x00, 'NNO': 0x01, 'NO': 0x02, 'ONO': 0x03,
                       'O': 0x04, 'OZO': 0x05, 'ZO': 0x06, 'ZZO': 0x07,
                       'Z': 0x08, 'ZZW': 0x09, 'ZW': 0x0A, 'WZW': 0x0B,
                       'W': 0x0C, 'WNW': 0x0D, 'NW': 0x0E, 'NNW': 0x0F}

    def __init__(self, *args, **kwargs):
        self.channel = kwargs['channel']
        self.frequency = kwargs['frequency']
        self.gpio = GPIO(**kwargs)
        # self.gpio.__init__(channel=self.channel, frequency=self.frequency)
        # TODO: check why I put the call to __init__ separately initially
        self.old_bit_string = None
        self.new_bit_string = None
        self.weather_data = {}
        self.requested_data = kwargs['bits']
        self.stations = kwargs['stations']

    def __repr__(self):
        return "WeatherVaneInterface(channel=%d, frequency=%d)" % (self.channel, self.frequency)

    @property
    def data_changed(self):
        """Return whether or not the data was different the last time it was sent.

        @return: a boolean indicating whether the data has changed
        """
        return self.old_bit_string != self.new_bit_string

    def convert_data(self, weather_data):
        """Converts the weather data into a string of bits

        The display is based on a relatively simple programmable interrupt controller (or PIC) when compared to a
        Raspberry PI. It only speaks binary, which means that we need to convert the dictionary with weather data into a
        sequence of bits, before it can be transmitted.
        This conversion is based on four principles:
        # The amount of bits available for each data element is fixed
        # Each element in the data has a minimum value
        # Each element has a maximum value
        # Each element can vary only in discrete steps

        @precondition: the member 'requested data' is properly set
        @param weather_data: a dictionary containing the weatherdata
        @return: a bitstring with the data in bits according to the configuration
        """
        s = None
        t_data = self.transmittable_data(weather_data, self.requested_data)

        for i, data in enumerate(self.requested_data):
            formatting = self.requested_data[str(i)]
            bit_length = int(formatting['length'])
            bit_key = formatting['key']
            bit_value = t_data[bit_key]
            padding_string = '#0{0}b'.format(bit_length + 2)  # don't forget to account for '0b' in the length
            padded_bit_value = format(bit_value, padding_string)
            if s is not None:
                s += bitstring.pack("bin:{}={}".format(bit_length, padded_bit_value))
            else:
                s = bitstring.pack("bin:{}={}".format(bit_length, padded_bit_value))

        return s

    def send(self, weather_data):
        """Send data to the connected SPI device.

        Keyword arguments:
        weather_data -- a dictionary with the data
        """
        data_array = self.convert_data(weather_data)
        self.gpio.send_data(data_array.tobytes())
        self.old_bit_string, self.new_bit_string = self.new_bit_string, data_array

    @property
    def data(self):
        """Return the data sent by the spi device.

        This function returns the data that was sent by the connected SPI device. To get the data that was originally
        sent to that device, use get_sent_data.

        Returns:
        array of bytes
                """
        return self.gpio.data

    @property
    def sent_data(self):
        """Return the original data sent to the spi device.

        This function returns the data that was sent to the connected SPI device. To get the data that was returned by
        that device, use get_data().

        Note that the data that actually reached the connected SPI device may have been altered due to all kinds of
        transmission errors. This function does not actually return the data that reached the device.

        Returns:
        array of bytes
        """

        return self.weather_data

    def transmittable_data(self, weather_data, requested_data):
        result = {}

        for key, fmt in list(requested_data.items()):
            measurement_name = requested_data[key]['key']
            value = weather_data.get(fmt['key'], 0)
            if measurement_name == 'random':
                length = int(requested_data[key]['length'])
                value = randint(0, 2 ** length - 1)

            result[measurement_name] = self.value_to_bits(measurement_name, value, fmt)
            result = self.compensate_wind(result)

        return result

    def value_to_bits(self, measurement_name, value, fmt):
        if measurement_name == 'wind_direction':
            if value in self.WIND_DIRECTIONS:
                return self.WIND_DIRECTIONS[value]
            else:
                logging.debug('Wind direction {} not found. Using North as substitute.'.format(value))
                return 0
        elif measurement_name == 'rain':
            if value > 0:
                return 1
            else:
                return 0
        else:
            step_value = float(fmt.get('step', 1))
            min_value = float(fmt.get('min', 0))
            max_value = float(fmt.get('max', 255))

            if value < min_value:
                logging.debug('Value {} for {} is smaller than minimum {}'.format(value, measurement_name, min_value))
                value = min_value
            if max_value < value:
                logging.debug('Value {} for {} is larger than maximum {}'.format(value, measurement_name, max_value))
                value = max_value

            try:
                value -= min_value
                value /= float(step_value)
            except TypeError:
                logging.debug('Value {} for {} is not a number'.format(value, measurement_name))
                return 0

            return int(value)

    def compensate_wind(self, result):
        wind_speed = result.get('wind_speed', 0)
        wind_speed_max = result.get('wind_speed_max', wind_speed)
        if wind_speed > wind_speed_max:
            result['wind_speed'] = wind_speed_max
            logging.debug('Wind speed {} should not exceed maximum wind speed {}'.format(wind_speed, wind_speed_max))

        return result
コード例 #15
0
ファイル: spi_test.py プロジェクト: marcoplaisier/weathervane
 def test_pack_with_empty_list(self, mock_class):
     spi = GPIO(channel=0, frequency=25000, library="wiringPi")
     data = []
     data_packet, length = spi.pack(data)
     self.assertEqual([], list(data_packet))
     self.assertEqual(0, length)
コード例 #16
0
ファイル: spi_test.py プロジェクト: marcoplaisier/weathervane
 def test_pack_with_one_element(self, mock_class):
     spi = GPIO(channel=0, frequency=25000, library='wiringPi')
     data = [1]
     data_packet, length = spi.pack(data)
     self.assertEqual([1], list(data_packet))
     self.assertEqual(1, length)
コード例 #17
0
ファイル: spi_test.py プロジェクト: marcoplaisier/weathervane
 def test_pack_with_iterable(self, mock_class):
     spi = GPIO(channel=0, frequency=25000, library="wiringPi")
     it = list(range(0, 10))
     data_packet, length = spi.pack(data=it)
     self.assertEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], list(data_packet))
     self.assertEqual(len(it), length)
コード例 #18
0
 def test_init_both_pins_and_spi(self, mock_loader):
     gpio = GPIO(channel=0, frequency=500000, library='wiringPi')
     gpio.handle.wiringPiSPISetup.assert_called_once_with(0, 500000)
コード例 #19
0
 def test_send(self, mock_loader):
     g = GPIO(channel=0, frequency=0, library='None')
     g.send_data('abc'.encode('UTF-16'))
コード例 #20
0
 def test_constructor(self, mock_loader):
     g = GPIO(channel=0, frequency=0, library='wiringPi')
     self.assertTrue(g)
コード例 #21
0
    def test_gpio_context_manager(self, mock_loader):
        g = GPIO(channel=0, frequency=10000, library='None', interrupt=0)
        l = [128]
        result, length = g.pack(l)

        self.assertEqual(list(result), l)
コード例 #22
0
class WeatherVaneInterface(object):
    wind_directions = {
        'N': 0x00,
        'NNO': 0x01,
        'NO': 0x02,
        'ONO': 0x03,
        'O': 0x04,
        'OZO': 0x05,
        'ZO': 0x06,
        'ZZO': 0x07,
        'Z': 0x08,
        'ZZW': 0x09,
        'ZW': 0x0A,
        'WZW': 0x0B,
        'W': 0x0C,
        'WNW': 0x0D,
        'NW': 0x0E,
        'NNW': 0x0F
    }

    def __init__(self, *args, **kwargs):
        self.channel = kwargs['channel']
        self.frequency = kwargs['frequency']
        self.gpio = GPIO(**kwargs)
        self.old_bit_string = None
        self.new_bit_string = None
        self.weather_data = {}
        self.bits: List[dict] = kwargs['bits']
        self.stations = kwargs['stations']

    def __repr__(self):
        return "WeatherVaneInterface(channel=%d, frequency=%d)" % (
            self.channel, self.frequency)

    @property
    def data_changed(self):
        """Return whether or not the data was different the last time it was sent.

        @return: a boolean indicating whether the data has changed
        """
        return self.old_bit_string != self.new_bit_string

    def convert_data(self, weather_data):
        """Converts the weather data into a string of bits

        The display is based on a relatively simple programmable interrupt controller (or PIC) when compared to a
        Raspberry PI. It only speaks binary, which means that we need to convert the dictionary with weather data into a
        sequence of bits, before it can be transmitted.
        This conversion is based on four principles:
        # The amount of bits available for each data element is fixed
        # Each element in the data has a minimum value
        # Each element has a maximum value
        # Each element can vary only in discrete steps

        @precondition: the member 'requested data' is properly set
        @param weather_data: a dictionary containing the weatherdata
        @return: a bitstring with the data in bits according to the configuration
        """
        s = None
        t_data = self.transmittable_data(weather_data, self.bits)

        for i, data in enumerate(self.bits):
            formatting = self.bits[i]
            bit_length = int(formatting['length'])
            bit_key = formatting['key']
            bit_value = t_data[bit_key]
            padding_string = '#0{0}b'.format(
                bit_length + 2)  # account for '0b' in the length
            padded_bit_value = format(bit_value, padding_string)
            if s is not None:
                s += bitstring.pack("bin:{}={}".format(bit_length,
                                                       padded_bit_value))
            else:
                s = bitstring.pack("bin:{}={}".format(bit_length,
                                                      padded_bit_value))

        return s

    def send(self, weather_data):
        """Send data to the connected SPI device.

        Keyword arguments:
        weather_data -- a dictionary with the data
        """
        data_array = self.convert_data(weather_data)
        self.gpio.send_data(data_array.tobytes())
        self.old_bit_string, self.new_bit_string = self.new_bit_string, data_array

    @property
    def data(self):
        """Return the data sent by the spi device.

        This function returns the data that was sent by the connected SPI device. To get the data that was originally
        sent to that device, use get_sent_data.

        Returns:
        array of bytes
                """
        return self.gpio.data

    @property
    def sent_data(self):
        """Return the original data sent to the spi device.

        This function returns the data that was sent to the connected SPI device. To get the data that was returned by
        that device, use get_data().

        Note that the data that actually reached the connected SPI device may have been altered due to all kinds of
        transmission errors. This function does not actually return the data that reached the device.

        Returns:
        array of bytes
        """

        return self.weather_data

    def transmittable_data(self, weather_data, requested_data: List[dict]):
        result = {}

        for data_point in requested_data:
            measurement_name = data_point['key']
            value = weather_data.get(measurement_name, 0)
            if measurement_name == 'random':
                length = int(data_point['length'])
                value = randint(0, 2**length - 1)

            step_value = float(data_point.get('step', 1))
            min_value = float(data_point.get('min', 0))
            max_value = float(data_point.get('max', 255))
            result[measurement_name] = self.value_to_bits(
                measurement_name, value, step_value, min_value, max_value)
            result = self.compensate_wind(result)

        return result

    def value_to_bits(self, measurement_name, value, step_value, min_value,
                      max_value):
        if measurement_name == 'winddirection':
            return self.wind_directions.get(value, 0)
        elif measurement_name == 'precipitation':
            return 1 if value and value > 0 else 0
        else:
            if not value:
                logging.debug(
                    'Value {} is missing. Setting to min-value'.format(value))
            value = min(max(value, min_value), max_value)

            try:
                value -= min_value
                value /= float(step_value)
            except TypeError:
                logging.debug('Value {} for {} is not a number'.format(
                    value, measurement_name))
                return 0

            return int(value)

    def compensate_wind(self, result):
        windspeed = result.get('windspeed', 0)
        windgusts = result.get('windgusts', windspeed)
        if windspeed > windgusts:
            result['windspeed'] = windgusts
            logging.debug(
                'Wind speed {} should not exceed maximum wind speed {}'.format(
                    windspeed, windgusts))

        return result
コード例 #23
0
def test_initialization_wrong_channel():
    with pytest.raises(SPISetupException):
        GPIO(channel=2, frequency=25000, library='wiringPi')
コード例 #24
0
def test_initialization(mock_class):
    GPIO(channel=0, frequency=25000, library='wiringPi')
コード例 #25
0
ファイル: spi_test.py プロジェクト: marcoplaisier/weathervane
 def test_pack_with_two_elements(self, mock_class):
     spi = GPIO(channel=0, frequency=25000, library="wiringPi")
     data = [1, 2]
     data_packet, length = spi.pack(data)
     self.assertEqual([1, 2], list(data_packet))
     self.assertEqual(2, length)
コード例 #26
0
 def test_pin_read(self, mock_class):
     interface = GPIO(channel=0, frequency=25000, library="wiringPi")
     interface.read_pin([3])
     interface.handle.pinMode.assert_called_once_with(3, 0)
     interface.handle.digitalRead.assert_called_once_with(3)
コード例 #27
0
ファイル: spi_test.py プロジェクト: marcoplaisier/weathervane
 def test_pack_with_iterable(self, mock_class):
     spi = GPIO(channel=0, frequency=25000, library="wiringPi")
     data = list(range(0, 4))
     data_packet, length = spi.pack(data)
     self.assertEqual([0, 1, 2, 3], list(data_packet))
     self.assertEqual(4, length)
コード例 #28
0
 def test_pin_read(self, mock_class):
     interface = GPIO(channel=0, frequency=25000, library='wiringPi')
     interface.read_pin([3])
     interface.handle.pinMode.assert_called_once_with(3, 0)
     interface.handle.digitalRead.assert_called_once_with(3)