def test_query_hello(self): connection = create_autospec(Connection) connection.read.return_value = b'\x51\x00\x00\xa0\x00\x00\x9d\x4b\x01\x00\xd8\x19' protocol = Protocol(connection) self.assertEqual(b'\x01\0', protocol.query(Range(0xa000, 1))) connection.write.assert_called_once_with( b'\x51\x00\x00\xa0\x00\x00\x9d\x4b')
def test_read_retry(self): connection = create_autospec(Connection) connection.read.side_effect = [ b'\x51', b'\x00\x00', b'\xa0', b'\x00', b'\x00', b'\x9d\x4b\x01\x00', b'\xd8', b'\x19', ] protocol = Protocol(connection) self.assertEqual(b'\x01\0', protocol.query(Range(0xa000, 1))) connection.read.assert_has_calls([ call(12), call(11), call(9), call(8), call(7), call(6), call(2), call(1), ]) connection.write.assert_called_once_with( b'\x51\x00\x00\xa0\x00\x00\x9d\x4b')
def test_query_invalid_crc_response(self): response = b'12345678901234567890123456' connection = create_autospec(Connection) connection.read.return_value = response protocol = Protocol(connection) with self.assertRaises(ValidationException) as context: protocol.query(Range(0xa000, 8)) self.assertEqual('Incorrect CRC (0x958a)', context.exception.args[0])
def _reduce(ranges: list) -> list: if len(ranges) <= 1: return ranges r1 = ranges[0] r2 = ranges[1] words = r2.address - r1.address + r2.words if words > MAX_WORDS: return [r1] + _reduce(ranges[1:]) return _reduce([Range(r1.address, words)] + ranges[2:])
def test_query_hash(self): sent = b'Q\x07\x00\x00\x1f\x00\xcfb' read = b"Q\x07\x00\x00\x1f\x00\xcfbz\xb2\x9f\xdeh\x1a\xe0\xb1\'\'\x08\x8f\x80\xc4\xba\x8b\xa0@" connection = create_autospec(Connection) connection.read.return_value = read protocol = Protocol(connection) self.assertEqual( b'z\xb2\x9f\xdeh\x1a\xe0\xb1\'\'\x08\x8f\x80\xc4\xba\x8b', protocol.query(Range(0x1f0000, 8))) connection.write.assert_called_once_with(sent)
def test_sorted(self): datas = [ Data(Range(0x1234, 1), b'zz'), Data(Range(0x9999, 2), b'aaaa'), Data(Range(0x0000, 1), b'mm'), Data(Range(0x4567, 1), b'aa'), ] self.maxDiff = None self.assertEqual([ Data(Range(0x0000, 1), b'mm'), Data(Range(0x1234, 1), b'zz'), Data(Range(0x4567, 1), b'aa'), Data(Range(0x9999, 2), b'aaaa'), ], sorted(datas))
class DataTest(TestCase): def test_bytes_raises_before_set(self): data = Data(Mock()) with self.assertRaises(NotFoundException) as context: data.bytes self.assertEqual('Unable to read bytes before property is set', context.exception.args[0]) def test_bytes_raises_during_init(self): with self.assertRaises(ValidationException) as context: Data(Range(0x0000, 1), b'1234') self.assertEqual('Unable to store 4 bytes to range requiring 2', context.exception.args[0]) @data_provider(lambda: ( (Range(0xcafe, 1), b'ab'), (Range(0xfeed, 5), b'1234567890'), )) def test_bytes_getter(self, range: Range, bytes: bytes): data = Data(range, bytes) self.assertEqual(bytes, data.bytes) @data_provider(lambda: ( (Range(0xcafe, 1), b'ab'), (Range(0xfeed, 5), b'1234567890'), )) def test_bytes_setter(self, range: Range, bytes: bytes): data = Data(range) data.bytes = bytes self.assertEqual(bytes, data.bytes) @data_provider(lambda: ( (Range(0xcafe, 2), b'ab', 'Unable to store 2 bytes to range requiring 4'), (Range(0xfeed, 1), b'1234567890', 'Unable to store 10 bytes to range requiring 2'), )) def test_bytes_raises_setter(self, range, bytes, expected): data = Data(range) with self.assertRaises(ValidationException) as context: data.bytes = bytes self.assertEqual(expected, context.exception.args[0]) def test_sorted(self): datas = [ Data(Range(0x1234, 1), b'zz'), Data(Range(0x9999, 2), b'aaaa'), Data(Range(0x0000, 1), b'mm'), Data(Range(0x4567, 1), b'aa'), ] self.maxDiff = None self.assertEqual([ Data(Range(0x0000, 1), b'mm'), Data(Range(0x1234, 1), b'zz'), Data(Range(0x4567, 1), b'aa'), Data(Range(0x9999, 2), b'aaaa'), ], sorted(datas))
def test_query_short_response(self): connection = create_autospec(Connection) connection.read.side_effect = [ b'123456', b'', b'', b'', b'', b'', b'', b'', b'', b'', b'', ] protocol = Protocol(connection) with self.assertRaises(BufferError) as context: protocol.query(Range(0xa000, 9)) self.assertEqual('Expected 28 bytes, but only able to read 6', context.exception.args[0])
def test_update(self): protocol = create_autospec(Protocol) protocol.query.side_effect = [ b'\x12\x34\x00\x00\x00\x00\x00\x00\x56\x78' ] muster = Muster(protocol) vars = [ variable.create('CommonScaleForAcVolts'), variable.create('CommonScaleForTemperature'), ] muster.update(vars) self.assertEqual(2, len(vars)) self.assertEqual('CommonScaleForAcVolts', vars[0].get_name()) self.assertEqual('CommonScaleForTemperature', vars[1].get_name()) self.assertEqual(b'\x12\x34', vars[0].bytes) self.assertEqual(b'\x56\x78', vars[1].bytes) self.assertEqual(1, len(protocol.query.call_args_list)) self.assertEqual(call(Range(41000, 5)), protocol.query.call_args_list[0])
def run(args): protocol = Protocol(connection.create()) protocol.login() # Before 0xa001 we don't get anything back start = 0xa001 # How much to get at a time fetch_words = 0x100 line_words = 0x10 # When to stop end = 0xa301 print( ' 1 2 3 4 5 6 7 8 9 a b c d e f 0' ) for address in range(start, end, fetch_words): memory = protocol.query(Range(address, fetch_words)) for offset in range(0, fetch_words, line_words): start_bytes = offset * 2 end_bytes = (offset + line_words) * 2 sub_memory = memory[start_bytes:end_bytes] hex_memory = binascii.hexlify(sub_memory) hex_address = binascii.hexlify(struct.pack(">H", address + offset)) print(b''.join([b'0x', hex_address, b' ', hex_memory]).decode('utf-8'))
def range(self): return Range(self.__address, TYPES[self.get_type()][WORDS])
class ExtractTest(TestCase): @data_provider(lambda: ( ( Range(0x0001, 2), [ Data(Range(0x0000, 10), b'1234567890abcdefghij'), ], Data(Range(0x0001, 2), b'3456'), ), ( Range(0x0001, 2), [ Data(Range(0x0001, 4), b'12345678'), ], Data(Range(0x0001, 2), b'1234'), ), (Range(0x1234, 4), [ Data(Range(0x0000, 10), b'aaaaaaaaaaaaaaaaaaaa'), Data(Range(0x1230, 10), b'mmmmmmmm78901234mmmm'), Data(Range(0x9999, 10), b'zzzzzzzzzzzzzzzzzzzz'), ], Data(Range(0x1234, 4), b'78901234')), )) def test_extract(self, range, datas, expected): self.assertEqual(expected, extract(range, datas)) @data_provider(lambda: ( (Range(0x0000, 10), [Data(Range(0x00010, 10), b'1234567890abcdefghij')]), (Range(0x0010, 12), [Data(Range(0x00010, 10), b'1234567890abcdefghij')]), (Range(0x0011, 5), [Data(Range(0x00010, 5), b'1234567890')]), (Range(0xcafe, 1), [Data(Range(0x00010, 5), b'1234567890')]), )) def test_extract_out_of_range(self, range, datas): with self.assertRaises(NotFoundException) as context: extract(range, datas) self.assertEqual('%s was not found' % range, context.exception.args[0])
class RangeTest(TestCase): @data_provider(lambda: ( (Range(0x0000, 3), Range(0x0000, 3), True), (Range(0x0000, 3), Range(0x0000, 4), False), (Range(0xa033, 1), Range(0xa033, 1), True), (Range(0xa033, 1), Range(0xa034, 1), False), (Range(0xa033, 1), [Range(0xa033, 1)], False), )) def test___eq__(self, r1, r2, expected): self.assertEqual(expected, r1 == r2) @data_provider(lambda: ( (Range(0x0000, 3), 'Range(0x0000, 3)'), (Range(0x1234, 99), 'Range(0x1234, 99)'), (Range(0xcafe, 1), 'Range(0xcafe, 1)'), )) def test___repr__(self, range, expected): self.assertEqual(expected, repr(range)) @data_provider(lambda: ( ([ Range(0x0000, 2), Range(0x0004, 3), ], [ Range(0x0000, 2), Range(0x0004, 3), ]), ([ Range(0x0009, 1), Range(0x0004, 3), Range(0x0000, 2), Range(0x0007, 1), ], [ Range(0x0000, 2), Range(0x0004, 3), Range(0x0007, 1), Range(0x0009, 1), ]), )) def test_sorted(self, ranges, expected): self.assertEqual(expected, sorted(ranges))
class VariableTest(TestCase): @data_provider(lambda: ( # (arg, name, address) ('CommonScaleForDcVolts', 'CommonScaleForDcVolts', 0xa02a), ('Shunt1Power', 'Shunt1Power', 0xa088), ('BatteryVolts', 'BatteryVolts', 0xa05c), ('BattSocPercent', 'BattSocPercent', 0xa081), (0xa02a, 'CommonScaleForDcVolts', 0xa02a), (0xa088, 'Shunt1Power', 0xa088), (0xa05c, 'BatteryVolts', 0xa05c), (0xa081, 'BattSocPercent', 0xa081), (0xcafe, 'Unknown', 0xcafe), (0xfeed, 'Unknown', 0xfeed), )) def test_create(self, arg, name, address): var = variable.create(arg) self.assertIsInstance(var, Variable) self.assertEqual(address, var.range.address) self.assertEqual(name, var.get_name()) @data_provider(lambda: ( ('CommonScaleForDcVolts', Range(41002, 1)), ('BattOutkWhPreviousAcc', Range(41356, 2)), ('DCBatteryPower', Range(41007, 2)), (0xcafe, Range(0xcafe, 1)), (0xfeed, Range(0xfeed, 1)), )) def test_range(self, arg, range): self.assertEqual(range, variable.create(arg).range) @data_provider(lambda: ( # (arg, type) ('CommonScaleForDcVolts', 'ushort'), ('BattOutkWhPreviousAcc', 'uint'), ('DCBatteryPower', 'int'), (0xcafe, 'ushort'), (0xfeed, 'ushort'), )) def test_get_type(self, arg, type): self.assertEqual(type, variable.create(arg).get_type()) @data_provider(lambda: ( ('CommonScaleForDcVolts', b'\x1a\x04', 1050), ('BatteryVolts', b'\x41\x44', 55.989532470703125), )) def test_bytes(self, arg, bytes, value): var = variable.create(arg) var.bytes = bytes self.assertEqual(value, var.get_value(scales)) self.assertEqual(bytes, var.bytes) @data_provider(lambda: ( ('CommonScaleForDcVolts', b'\x1a\x04', 1050), ('CommonScaleForTemperature', b'\x12\x02', 530), ('Shunt1Power', b'\x46\xfe', -1699.5849609375), ('Shunt1Power', b'\x3f\xfe', -1726.50146484375), ('Shunt1Power', b'\x7a\xfe', -1499.6337890625), ('Shunt1Power', b'\xfd\xff', -11.53564453125), ('BatteryVolts', b'\x41\x44', 55.989532470703125), ('BatteryVolts', b'\x3a\x44', 55.96710205078125), ('BatteryTemperature', b'\x44\x05', 21.802978515625), ('LoadAcPower', b'\xff\x02\x00\x00', 341.1567687988281), ('LoadAcPower', b'\x1d\x03\x00\x00', 354.5005798339844), ('DCBatteryPower', b'\xd4\xff\xff\xff', -169.189453125), ('DCBatteryPower', b'\xf4\xff\xff\xff', -46.142578125), ('ACLoadkWhTotalAcc', b'\x19\xeb\x00\x00', 5139822.509765625), ('BattOutkWhPreviousAcc', b'\x29\x00\x00\x00', 3783.69140625), ('BattSocPercent', b'\xdb\x63', 99.85546875), ('Shunt1Name', b'\x01\x00', 'Solar'), ('Shunt2Name', b'\x00\x00', 'None'), ('Shunt1Name', b'\x3a\x00', 'Error'), )) def test_get_value(self, name, bytes, value): address = 0xcafe # address doesn't matter for this test var = Variable(name, address, bytes) self.assertEqual(value, var.get_value(scales)) def test_get_value_errors(self): var = variable.create(0xcafe) with self.assertRaises(Exception) as context: var.get_value([]) self.assertEqual('Can not convert value for unknown variable type', context.exception.args[0])
class ReduceTest(TestCase): @data_provider(lambda: ( ([ Range(0x1234, 2), ], [ Range(0x1234, 2), ]), ([ Range(0x0010, 1), Range(0x0014, 2), ], [ Range(0x0010, 6), ]), ([ Range(0x0004, 2), Range(0x0000, 1), ], [ Range(0x0000, 6), ]), ([ Range(0x0001, 2), Range(0x0002, 2), Range(0x0003, 2), ], [ Range(0x0001, 4), ]), # Split across MAX_WORDS (256) ([ Range(0x0000, 2), Range(0x00ff, 1), Range(0x0103, 1), Range(0x0203, 1), Range(0x0301, 2), ], [ Range(0x0000, 256), Range(0x0103, 1), Range(0x0203, 256), ]), )) def test_reduce(self, ranges, expected): self.assertEqual(expected, reduce(ranges))
def test_bytes_raises_during_init(self): with self.assertRaises(ValidationException) as context: Data(Range(0x0000, 1), b'1234') self.assertEqual('Unable to store 4 bytes to range requiring 2', context.exception.args[0])