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)
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
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)
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)
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
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 __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 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)
def test_send(self, mock_loader): g = GPIO(channel=0, frequency=0, library='None') g.send_data('abc'.encode('UTF-16'))
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
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
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)
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
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
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)
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)
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)
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)
def test_constructor(self, mock_loader): g = GPIO(channel=0, frequency=0, library='wiringPi') self.assertTrue(g)
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
def test_initialization_wrong_channel(): with pytest.raises(SPISetupException): GPIO(channel=2, frequency=25000, library='wiringPi')
def test_initialization(mock_class): GPIO(channel=0, frequency=25000, library='wiringPi')
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)
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)
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)
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)