# coding=utf-8 import config as cfg from convert import mean, read_float, read_led_state, read_bool, count_bits, comma_separated, read_bitmap, return_in_list, first, read_hex_string,read_limb_string from data import BatterySignal, Battery, LedColor, ServiceSignal, BatteryStatus, LedState, CsvSignal # noinspection PyUnreachableCode if False: from typing import List, Iterable def read_voltage(): return read_float(register=999, scale_factor=0.01, offset=0) def read_current(): return read_float(register=1000, scale_factor=0.01, offset=-10000) def read_limb_bitmap(): return read_bitmap(1059) def read_power(status): return int(read_current()(status) * read_voltage()(status)) def interpret_limb_bitmap(bitmap_value): string1_disabled = int((bitmap_value & 0b00001) != 0) string2_disabled = int((bitmap_value & 0b00010) != 0) string3_disabled = int((bitmap_value & 0b00100) != 0) string4_disabled = int((bitmap_value & 0b01000) != 0) string5_disabled = int((bitmap_value & 0b10000) != 0) n_limb_strings = string1_disabled + string2_disabled + string3_disabled + string4_disabled + string5_disabled return n_limb_strings def limp_strings_value(status): return interpret_limb_bitmap(read_limb_bitmap()(status)) def calc_power_limit_imposed_by_voltage_limit(v, i, v_limit, r_int): dv = v_limit - v di = dv / r_int p_limit = v_limit * (i + di) return p_limit def calc_power_limit_imposed_by_current_limit(v, i, i_limit, r_int): di = i_limit - i dv = di * r_int p_limit = i_limit * (v + dv) return p_limit def calc_max_charge_power(status): n_strings = cfg.NUM_OF_STRINGS_PER_BATTERY - limp_strings_value(status) i_max = n_strings * cfg.I_MAX_PER_STRING v_max = cfg.V_MAX r_int_min = cfg.R_STRING_MIN / n_strings r_int_max = cfg.R_STRING_MAX / n_strings v = read_voltage()(status) i = read_current()(status) p_limits = [ calc_power_limit_imposed_by_voltage_limit(v, i, v_max, r_int_min), calc_power_limit_imposed_by_voltage_limit(v, i, v_max, r_int_max), calc_power_limit_imposed_by_current_limit(v, i, i_max, r_int_min), calc_power_limit_imposed_by_current_limit(v, i, i_max, r_int_max), ] p_limit = min(p_limits) p_limit = max(p_limit, 0) return int(p_limit) def calc_max_discharge_power(status): n_strings = cfg.NUM_OF_STRINGS_PER_BATTERY - limp_strings_value(status) max_discharge_current = n_strings * cfg.I_MAX_PER_STRING return int(max_discharge_current * read_voltage()(status)) def read_switch_closed(status): value = read_bool(base_register=1013, bit=0)(status) if value: return False return True def read_alarm_out_active(status): value = read_bool(base_register=1013, bit=1)(status) if value: return False return True def read_aux_relay(status): value = read_bool(base_register=1013, bit=4)(status) if value: return False return True def hex_string_to_ascii(hex_string): hex_string = hex_string.replace(" ", "") ascii_string = ''.join([chr(int(hex_string[i:i+2], 16)) for i in range(0, len(hex_string), 2)]) return ascii_string def init_service_signals(batteries): print("INSIDE INIT SERVICE SIGNALS") n_batteries = len(batteries) product_name = cfg.PRODUCT_NAME + ' x' + str(n_batteries) return [ ServiceSignal('/NbOfBatteries', n_batteries), ServiceSignal('/Mgmt/ProcessName', __file__), ServiceSignal('/Mgmt/ProcessVersion', cfg.SOFTWARE_VERSION), ServiceSignal('/Mgmt/Connection', cfg.CONNECTION), ServiceSignal('/DeviceInstance', cfg.DEVICE_INSTANCE), ServiceSignal('/ProductName', product_name), ServiceSignal('/ProductId', cfg.PRODUCT_ID), ServiceSignal('/Connected', 1) ] def init_battery_signals(): print("START INIT SIGNALS") battery_status_reader = read_hex_string(1060, 2) def read_eoc_reached(status): battery_status_string = battery_status_reader(status) return hex_string_to_ascii(battery_status_string) == "EOC_" def read_battery_cold(status): return \ read_led_state(register=1004, led=LedColor.green)(status) >= LedState.blinking_slow and \ read_led_state(register=1004, led=LedColor.blue)(status) >= LedState.blinking_slow def read_soc(status): soc = read_float(register=1053, scale_factor=0.1, offset=0)(status) if soc > 99.9 and not read_eoc_reached(status): return 99.9 if soc >= 99.9 and read_eoc_reached(status): return 100 return soc def number_of_active_strings(status): return cfg.NUM_OF_STRINGS_PER_BATTERY - limp_strings_value(status) def max_discharge_current(status): return number_of_active_strings(status) * cfg.I_MAX_PER_STRING def max_charge_current(status): return status.battery.ampere_hours / 2 return [ BatterySignal('/Dc/0/Voltage', mean, get_value=read_voltage(), unit='V'), BatterySignal('/Dc/0/Current', sum, get_value=read_current(), unit='A'), BatterySignal('/Dc/0/Power', sum, get_value=read_power, unit='W'), BatterySignal('/BussVoltage', mean, read_float(register=1001, scale_factor=0.01, offset=0), unit='V'), BatterySignal('/Soc', mean, read_soc, unit='%'), BatterySignal('/Dc/0/Temperature', mean, read_float(register=1003, scale_factor=0.1, offset=-400), unit='C'), BatterySignal('/NumberOfWarningFlags', sum, count_bits(base_register=1005, nb_of_registers=4, nb_of_bits=50)), BatterySignal('/WarningFlags/TaM1', any, read_bool(base_register=1005, bit=1)), BatterySignal('/WarningFlags/TbM1', any, read_bool(base_register=1005, bit=4)), BatterySignal('/WarningFlags/VBm1', any, read_bool(base_register=1005, bit=6)), BatterySignal('/WarningFlags/VBM1', any, read_bool(base_register=1005, bit=8)), BatterySignal('/WarningFlags/IDM1', any, read_bool(base_register=1005, bit=10)), BatterySignal('/WarningFlags/vsm1', any, read_bool(base_register=1005, bit=22)), BatterySignal('/WarningFlags/vsM1', any, read_bool(base_register=1005, bit=24)), BatterySignal('/WarningFlags/iCM1', any, read_bool(base_register=1005, bit=26)), BatterySignal('/WarningFlags/iDM1', any, read_bool(base_register=1005, bit=28)), BatterySignal('/WarningFlags/MID1', any, read_bool(base_register=1005, bit=30)), BatterySignal('/WarningFlags/BLPW', any, read_bool(base_register=1005, bit=32)), BatterySignal('/WarningFlags/CCBF', any, read_bool(base_register=1005, bit=33)), BatterySignal('/WarningFlags/Ah_W', any, read_bool(base_register=1005, bit=35)), BatterySignal('/WarningFlags/MPMM', any, read_bool(base_register=1005, bit=38)), BatterySignal('/WarningFlags/TCdi', any, read_bool(base_register=1005, bit=40)), BatterySignal('/WarningFlags/LMPW', any, read_bool(base_register=1005, bit=44)), BatterySignal('/WarningFlags/TOCW', any, read_bool(base_register=1005, bit=47)), BatterySignal('/WarningFlags/BUSL', any, read_bool(base_register=1005, bit=49)), BatterySignal('/NumberOfAlarmFlags', sum, count_bits(base_register=1009, nb_of_registers=4, nb_of_bits=50)), BatterySignal('/AlarmFlags/Tam', any, read_bool(base_register=1009, bit=0)), BatterySignal('/AlarmFlags/TaM2', any, read_bool(base_register=1009, bit=2)), BatterySignal('/AlarmFlags/Tbm', any, read_bool(base_register=1009, bit=3)), BatterySignal('/AlarmFlags/TbM2', any, read_bool(base_register=1009, bit=5)), BatterySignal('/AlarmFlags/VBm2', any, read_bool(base_register=1009, bit=7)), BatterySignal('/AlarmFlags/VBM2', any, read_bool(base_register=1009, bit=9)), BatterySignal('/AlarmFlags/IDM2', any, read_bool(base_register=1009, bit=11)), BatterySignal('/AlarmFlags/ISOB', any, read_bool(base_register=1009, bit=12)), BatterySignal('/AlarmFlags/MSWE', any, read_bool(base_register=1009, bit=13)), BatterySignal('/AlarmFlags/FUSE', any, read_bool(base_register=1009, bit=14)), BatterySignal('/AlarmFlags/HTRE', any, read_bool(base_register=1009, bit=15)), BatterySignal('/AlarmFlags/TCPE', any, read_bool(base_register=1009, bit=16)), BatterySignal('/AlarmFlags/STRE', any, read_bool(base_register=1009, bit=17)), BatterySignal('/AlarmFlags/CME', any, read_bool(base_register=1009, bit=18)), BatterySignal('/AlarmFlags/HWFL', any, read_bool(base_register=1009, bit=19)), BatterySignal('/AlarmFlags/HWEM', any, read_bool(base_register=1009, bit=20)), BatterySignal('/AlarmFlags/ThM', any, read_bool(base_register=1009, bit=21)), BatterySignal('/AlarmFlags/vsm2', any, read_bool(base_register=1009, bit=23)), BatterySignal('/AlarmFlags/vsM2', any, read_bool(base_register=1009, bit=25)), BatterySignal('/AlarmFlags/iCM2', any, read_bool(base_register=1009, bit=27)), BatterySignal('/AlarmFlags/iDM2', any, read_bool(base_register=1009, bit=29)), BatterySignal('/AlarmFlags/MID2', any, read_bool(base_register=1009, bit=31)), BatterySignal('/AlarmFlags/HTFS', any, read_bool(base_register=1009, bit=42)), BatterySignal('/AlarmFlags/DATA', any, read_bool(base_register=1009, bit=43)), BatterySignal('/AlarmFlags/LMPA', any, read_bool(base_register=1009, bit=45)), BatterySignal('/AlarmFlags/HEBT', any, read_bool(base_register=1009, bit=46)), BatterySignal('/AlarmFlags/CURM', any, read_bool(base_register=1009, bit=48)), BatterySignal('/LedStatus/Red', max, read_led_state(register=1004, led=LedColor.red)), BatterySignal('/LedStatus/Blue', max, read_led_state(register=1004, led=LedColor.blue)), BatterySignal('/LedStatus/Green', max, read_led_state(register=1004, led=LedColor.green)), BatterySignal('/LedStatus/Amber', max, read_led_state(register=1004, led=LedColor.amber)), BatterySignal('/IoStatus/MainSwitchClosed', any, read_switch_closed), BatterySignal('/IoStatus/AlarmOutActive', any, read_alarm_out_active), BatterySignal('/IoStatus/InternalFanActive', any, read_bool(base_register=1013, bit=2)), BatterySignal('/IoStatus/VoltMeasurementAllowed', any, read_bool(base_register=1013, bit=3)), BatterySignal('/IoStatus/AuxRelay', any, read_aux_relay), BatterySignal('/IoStatus/RemoteState', any, read_bool(base_register=1013, bit=5)), BatterySignal('/IoStatus/RiscOn', any, read_bool(base_register=1013, bit=6)), BatterySignal('/IoStatus/BatteryCold', any, read_battery_cold), BatterySignal('/IoStatus/EocReached', any, read_eoc_reached), BatterySignal('/Info/MaxDischargeCurrent', sum, max_discharge_current, unit='A'), BatterySignal('/Info/MaxChargeCurrent', sum, max_charge_current, unit='A'), BatterySignal('/Info/MaxChargeVoltage', min, lambda bs: bs.battery.v_max, unit='V'), BatterySignal('/Info/MinDischargeVoltage', max, lambda bs: bs.battery.v_min, unit='V'), BatterySignal('/Info/BatteryLowVoltage', max, lambda bs: bs.battery.v_min - 2, unit='V'), BatterySignal('/Info/NumberOfStrings', sum, lambda bs: bs.battery.n_strings), BatterySignal('/Info/MaxChargePower', sum, calc_max_charge_power), BatterySignal('/Info/MaxDischargePower', sum, calc_max_discharge_power), BatterySignal('/FirmwareVersion', comma_separated, lambda bs: bs.battery.firmware_version), BatterySignal('/HardwareVersion', comma_separated, lambda bs: bs.battery.hardware_version), BatterySignal('/BmsVersion', comma_separated, lambda bs: bs.battery.bms_version) ] def create_csv_signals(): total_current = read_float(register=1062, scale_factor=0.01, offset=-10000) def read_total_current(status): return total_current(status) def read_heating_current(status): return total_current(status) - read_current()(status) def read_heating_power(status): return read_voltage()(status) * read_heating_current(status) soc_ah = read_float(register=1002, scale_factor=0.1, offset=-10000) def read_soc_ah(status): return soc_ah(status) def return_led_state(status, color): led_state = read_led_state(register=1004, led=color)(status) if led_state == LedState.blinking_fast or led_state == LedState.blinking_slow: return "Blinking" elif led_state == LedState.on: return "On" elif led_state == LedState.off: return "Off" return "Unknown" def return_led_state_blue(status): return return_led_state(status, LedColor.blue) def return_led_state_red(status): return return_led_state(status, LedColor.red) def return_led_state_green(status): return return_led_state(status, LedColor.green) def return_led_state_amber(status): return return_led_state(status, LedColor.amber) battery_status_reader = read_hex_string(1060, 2) def read_eoc_reached(status): battery_status_string = battery_status_reader(status) return hex_string_to_ascii(battery_status_string) == "EOC_" def read_serial_number(status): serial_regs = [1055, 1056, 1057, 1058] serial_parts = [] for reg in serial_regs: hex_value_fun = read_hex_string(reg, 1) hex_value = hex_value_fun(status) serial_parts.append(hex_value.replace(' ', '')) serial_number = ''.join(serial_parts).rstrip('0') return serial_number def time_since_toc_in_time_format(status): time_in_minutes = read_float(register=1052)(status) total_seconds = int(time_in_minutes * 60) days = total_seconds // (24 * 3600) total_seconds = total_seconds % (24 * 3600) hours = total_seconds // 3600 total_seconds %= 3600 minutes = total_seconds // 60 seconds = total_seconds % 60 return "{}.{:02}:{:02}:{:02}".format(days, hours, minutes, seconds) return [ CsvSignal('/Battery/Devices/FwVersion', lambda bs: bs.battery.firmware_version), CsvSignal('/Battery/Devices/Dc/Power', read_power, 'W'), CsvSignal('/Battery/Devices/Dc/Voltage', read_voltage(), 'V'), CsvSignal('/Battery/Devices/Soc', read_float(register=1053, scale_factor=0.1, offset=0), '%'), CsvSignal('/Battery/Devices/Temperatures/Cells/Average', read_float(register=1003, scale_factor=0.1, offset=-400), 'C'), CsvSignal('/Battery/Devices/Dc/Current', read_current(), 'A'), CsvSignal('/Battery/Devices/BusCurrent', read_total_current, 'A'), CsvSignal('/Battery/Devices/CellsCurrent', read_current(), 'A'), CsvSignal('/Battery/Devices/HeatingCurrent', read_heating_current, 'A'), CsvSignal('/Battery/Devices/HeatingPower', read_heating_power, 'W'), CsvSignal('/Battery/Devices/SOCAh', read_soc_ah), CsvSignal('/Battery/Devices/Leds/Blue', return_led_state_blue), CsvSignal('/Battery/Devices/Leds/Red', return_led_state_red), CsvSignal('/Battery/Devices/Leds/Green', return_led_state_green), CsvSignal('/Battery/Devices/Leds/Amber', return_led_state_amber), CsvSignal('/Battery/Devices/BatteryStrings/String1Active', lambda status: int((read_limb_bitmap()(status) & 0b00001) != 0)), CsvSignal('/Battery/Devices/BatteryStrings/String2Active', lambda status: int((read_limb_bitmap()(status) & 0b00010) != 0)), CsvSignal('/Battery/Devices/BatteryStrings/String3Active', lambda status: int((read_limb_bitmap()(status) & 0b00100) != 0)), CsvSignal('/Battery/Devices/BatteryStrings/String4Active', lambda status: int((read_limb_bitmap()(status) & 0b01000) != 0)), CsvSignal('/Battery/Devices/BatteryStrings/String5Active', lambda status: int((read_limb_bitmap()(status) & 0b10000) != 0)), CsvSignal('/Battery/Devices/IoStatus/ConnectedToDcBus', read_switch_closed), CsvSignal('/Battery/Devices/IoStatus/AlarmOutActive', read_alarm_out_active), CsvSignal('/Battery/Devices/IoStatus/InternalFanActive', read_bool(base_register=1013, bit=2)), CsvSignal('/Battery/Devices/IoStatus/VoltMeasurementAllowed', read_bool(base_register=1013, bit=3)), CsvSignal('/Battery/Devices/IoStatus/AuxRelayBus', read_aux_relay), CsvSignal('/Battery/Devices/IoStatus/RemoteStateActive', read_bool(base_register=1013, bit=5)), CsvSignal('/Battery/Devices/IoStatus/RiscActive', read_bool(base_register=1013, bit=6)), CsvSignal('/Battery/Devices/Eoc', read_eoc_reached), CsvSignal('/Battery/Devices/SerialNumber', read_serial_number), CsvSignal('/Battery/Devices/TimeSinceTOC', time_since_toc_in_time_format), CsvSignal('/Battery/Devices/MaxChargePower', calc_max_charge_power), CsvSignal('/Battery/Devices/MaxDischargePower', calc_max_discharge_power), ] def read_warning_and_alarm_flags(): return [ # Warnings CsvSignal('/Battery/Devices/WarningFlags/TaM1', read_bool(base_register=1005, bit=1)), CsvSignal('/Battery/Devices/WarningFlags/TbM1', read_bool(base_register=1005, bit=4)), CsvSignal('/Battery/Devices/WarningFlags/VBm1', read_bool(base_register=1005, bit=6)), CsvSignal('/Battery/Devices/WarningFlags/VBM1', read_bool(base_register=1005, bit=8)), CsvSignal('/Battery/Devices/WarningFlags/IDM1', read_bool(base_register=1005, bit=10)), CsvSignal('/Battery/Devices/WarningFlags/vsm1', read_bool(base_register=1005, bit=22)), CsvSignal('/Battery/Devices/WarningFlags/vsM1', read_bool(base_register=1005, bit=24)), CsvSignal('/Battery/Devices/WarningFlags/iCM1', read_bool(base_register=1005, bit=26)), CsvSignal('/Battery/Devices/WarningFlags/iDM1', read_bool(base_register=1005, bit=28)), CsvSignal('/Battery/Devices/WarningFlags/MID1', read_bool(base_register=1005, bit=30)), CsvSignal('/Battery/Devices/WarningFlags/BLPW', read_bool(base_register=1005, bit=32)), CsvSignal('/Battery/Devices/WarningFlags/CCBF', read_bool(base_register=1005, bit=33)), CsvSignal('/Battery/Devices/WarningFlags/Ah_W', read_bool(base_register=1005, bit=35)), CsvSignal('/Battery/Devices/WarningFlags/MPMM', read_bool(base_register=1005, bit=38)), CsvSignal('/Battery/Devices/WarningFlags/TCdi', read_bool(base_register=1005, bit=40)), CsvSignal('/Battery/Devices/WarningFlags/LMPW', read_bool(base_register=1005, bit=44)), CsvSignal('/Battery/Devices/WarningFlags/TOCW', read_bool(base_register=1005, bit=47)), CsvSignal('/Battery/Devices/WarningFlags/BUSL', read_bool(base_register=1005, bit=49)), ], [ # Alarms CsvSignal('/Battery/Devices/AlarmFlags/Tam', read_bool(base_register=1009, bit=0)), CsvSignal('/Battery/Devices/AlarmFlags/TaM2', read_bool(base_register=1009, bit=2)), CsvSignal('/Battery/Devices/AlarmFlags/Tbm', read_bool(base_register=1009, bit=3)), CsvSignal('/Battery/Devices/AlarmFlags/TbM2', read_bool(base_register=1009, bit=5)), CsvSignal('/Battery/Devices/AlarmFlags/VBm2', read_bool(base_register=1009, bit=7)), CsvSignal('/Battery/Devices/AlarmFlags/VBM2', read_bool(base_register=1009, bit=9)), CsvSignal('/Battery/Devices/AlarmFlags/IDM2', read_bool(base_register=1009, bit=11)), CsvSignal('/Battery/Devices/AlarmFlags/ISOB', read_bool(base_register=1009, bit=12)), CsvSignal('/Battery/Devices/AlarmFlags/MSWE', read_bool(base_register=1009, bit=13)), CsvSignal('/Battery/Devices/AlarmFlags/FUSE', read_bool(base_register=1009, bit=14)), CsvSignal('/Battery/Devices/AlarmFlags/HTRE', read_bool(base_register=1009, bit=15)), CsvSignal('/Battery/Devices/AlarmFlags/TCPE', read_bool(base_register=1009, bit=16)), CsvSignal('/Battery/Devices/AlarmFlags/STRE', read_bool(base_register=1009, bit=17)), CsvSignal('/Battery/Devices/AlarmFlags/CME', read_bool(base_register=1009, bit=18)), CsvSignal('/Battery/Devices/AlarmFlags/HWFL', read_bool(base_register=1009, bit=19)), CsvSignal('/Battery/Devices/AlarmFlags/HWEM', read_bool(base_register=1009, bit=20)), CsvSignal('/Battery/Devices/AlarmFlags/ThM', read_bool(base_register=1009, bit=21)), CsvSignal('/Battery/Devices/AlarmFlags/vsm2', read_bool(base_register=1009, bit=23)), CsvSignal('/Battery/Devices/AlarmFlags/vsM2', read_bool(base_register=1009, bit=25)), CsvSignal('/Battery/Devices/AlarmFlags/iCM2', read_bool(base_register=1009, bit=27)), CsvSignal('/Battery/Devices/AlarmFlags/iDM2', read_bool(base_register=1009, bit=29)), CsvSignal('/Battery/Devices/AlarmFlags/MID2', read_bool(base_register=1009, bit=31)), CsvSignal('/Battery/Devices/AlarmFlags/HTFS', read_bool(base_register=1009, bit=42)), CsvSignal('/Battery/Devices/AlarmFlags/DATA', read_bool(base_register=1009, bit=43)), CsvSignal('/Battery/Devices/AlarmFlags/LMPA', read_bool(base_register=1009, bit=45)), CsvSignal('/Battery/Devices/AlarmFlags/HEBT', read_bool(base_register=1009, bit=46)), CsvSignal('/Battery/Devices/AlarmFlags/CURM', read_bool(base_register=1009, bit=48)), CsvSignal('/Battery/Devices/AlarmFlags/2 or more string are disabled',read_limb_string(1059)), ]