from collections import Iterable from decimal import * import config as cfg from data import LedState, BatteryStatus # trick the pycharm type-checker into thinking Callable is in scope, not used at runtime # noinspection PyUnreachableCode if False: from typing import Callable def read_bool(register, bit): # type: (int, int) -> Callable[[BatteryStatus], bool] def get_value(status): # type: (BatteryStatus) -> bool value = status.modbus_data[register - cfg.BASE_ADDRESS] return value & (1 << bit) > 0 return get_value def read_float(register, scale_factor=1.0, offset=0.0, places=2): # type: (int, float, float) -> Callable[[BatteryStatus], float] def get_value(status): # type: (BatteryStatus) -> float value = status.modbus_data[register - cfg.BASE_ADDRESS] if value >= 0x8000: # convert to signed int16 value -= 0x10000 # fiamm stores their integers signed AND with sign-offset @#%^&! result = (value+offset)*scale_factor return round(result,places) return get_value def read_hex_string(register, count): # type: (int, int) -> Callable[[BatteryStatus], str] """ reads count consecutive modbus registers from start_address, and returns a hex representation of it: e.g. for count=4: DEAD BEEF DEAD BEEF. """ start = register - cfg.BASE_ADDRESS end = start + count def get_value(status): # type: (BatteryStatus) -> str return ' '.join(['{0:0>4X}'.format(x) for x in status.modbus_data[start:end]]) return get_value def read_led_state(register, led): # type: (int, int) -> Callable[[BatteryStatus], int] read_lo = read_bool(register, led * 2) read_hi = read_bool(register, led * 2 + 1) def get_value(status): # type: (BatteryStatus) -> int lo = read_lo(status) hi = read_hi(status) if hi: if lo: return LedState.blinking_fast else: return LedState.blinking_slow else: if lo: return LedState.on else: return LedState.off return get_value def read_bitmap(register): # type: (int) -> Callable[[BatteryStatus], bitmap] def get_value(status): # type: (BatteryStatus) -> bitmap value = status.modbus_data[register - cfg.BASE_ADDRESS] return value return get_value def append_unit(unit): # type: (unicode) -> Callable[[unicode], unicode] def get_text(v): # type: (unicode) -> unicode return "{0}{1}".format(str(v), unit) return get_text def mean(numbers): # type: (Iterable[float] | Iterable[int]) -> float return float("{:.2f}".format(float(sum(numbers)) / len(numbers))) def ssum(numbers): # type: (Iterable[float] | Iterable[int]) -> float return float("{:.2f}".format(float(sum(numbers)))) def first(ts): return next(t for t in ts) def return_in_list(ts): return ts