285 lines
14 KiB
Python
285 lines
14 KiB
Python
|
# coding=utf-8
|
||
|
|
||
|
import config as cfg
|
||
|
from convert import mean, read_float, read_led_state, read_bool, read_bool_inverted, count_bits, comma_separated
|
||
|
from data import BatterySignal, Battery, LedColor, ServiceSignal, BatteryStatus, LedState
|
||
|
from python_libs.ie_dbus.dbus_service import DBusService
|
||
|
|
||
|
# noinspection PyUnreachableCode
|
||
|
if False:
|
||
|
from typing import List, Iterable
|
||
|
|
||
|
|
||
|
def init_service_signals(batteries):
|
||
|
# type: (List[Battery]) -> Iterable[ServiceSignal]
|
||
|
|
||
|
n_batteries = len(batteries)
|
||
|
product_name = cfg.PRODUCT_NAME + ' x' + str(n_batteries)
|
||
|
|
||
|
return [
|
||
|
ServiceSignal('/NbOfBatteries', n_batteries), # TODO: nb of operational 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():
|
||
|
# type: () -> Iterable[BatterySignal]
|
||
|
|
||
|
read_voltage = read_float(register=999, scale_factor=0.01, offset=0)
|
||
|
read_current = read_float(register=1000, scale_factor=0.01, offset=-10000)
|
||
|
read_bus_voltage = read_float(register=1001, scale_factor=0.01, offset=0)
|
||
|
|
||
|
read_led_amber = read_led_state(register=1004, led=LedColor.amber)
|
||
|
read_led_green = read_led_state(register=1004, led=LedColor.green)
|
||
|
read_led_blue = read_led_state(register=1004, led=LedColor.blue)
|
||
|
read_led_red = read_led_state(register=1004, led=LedColor.red)
|
||
|
|
||
|
read_center_heater_PWM = read_float(register=1018, scale_factor=0.1, offset=0)
|
||
|
read_lateral_heater_PWM = read_float(register=1019, scale_factor=0.1, offset=0)
|
||
|
|
||
|
read_center_temperature_celcius = read_float(register=1015, scale_factor=0.1, offset=-400)
|
||
|
read_lateral1_temperature_celcius = read_float(register=1016, scale_factor=0.1, offset=-400)
|
||
|
read_lateral2_temperature_celcius = read_float(register=1017, scale_factor=0.1, offset=-400)
|
||
|
|
||
|
def read_total_power(status):
|
||
|
# type: (BatteryStatus) -> int
|
||
|
return int(read_current(status) * read_voltage(status))
|
||
|
|
||
|
def calculate_lateral_heater_power(status):
|
||
|
# type: (BatteryStatus) -> int
|
||
|
return round(read_lateral_heater_PWM(status) / 100 * (read_bus_voltage(status) ** 2) / cfg.R_HEATER_LATERAL, 2)
|
||
|
|
||
|
def calculate_center_heater_power(status):
|
||
|
# type: (BatteryStatus) -> int
|
||
|
return round(read_center_heater_PWM(status) / 100 * (read_bus_voltage(status) ** 2) / cfg.R_HEATER_CENTER, 2)
|
||
|
|
||
|
def calculate_heating_power(status):
|
||
|
# type: (BatteryStatus) -> int
|
||
|
return round(calculate_lateral_heater_power(status) + calculate_center_heater_power(status), 2)
|
||
|
|
||
|
def calc_power_limit_imposed_by_voltage_limit(v, i, v_limit, r_int):
|
||
|
# type: (float, float, float, float) -> float
|
||
|
|
||
|
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):
|
||
|
# type: (float, float, float, float) -> float
|
||
|
|
||
|
di = i_limit - i
|
||
|
dv = di * r_int
|
||
|
p_limit = i_limit * (v + dv)
|
||
|
|
||
|
return p_limit
|
||
|
|
||
|
def calc_max_charge_power(bs):
|
||
|
# type: (BatteryStatus) -> int
|
||
|
|
||
|
b = bs.battery
|
||
|
v = read_voltage(bs)
|
||
|
i = read_current(bs)
|
||
|
|
||
|
p_limits = [
|
||
|
calc_power_limit_imposed_by_voltage_limit(v, i, b.v_max, b.r_int_min),
|
||
|
calc_power_limit_imposed_by_voltage_limit(v, i, b.v_max, b.r_int_max),
|
||
|
calc_power_limit_imposed_by_current_limit(v, i, b.i_max, b.r_int_min),
|
||
|
calc_power_limit_imposed_by_current_limit(v, i, b.i_max, b.r_int_max),
|
||
|
]
|
||
|
|
||
|
p_limit = min(p_limits) # p_limit is normally positive here (signed)
|
||
|
p_limit = max(p_limit, 0) # charge power must not become negative
|
||
|
|
||
|
return int(p_limit)
|
||
|
|
||
|
def calc_max_discharge_power(bs):
|
||
|
# type: (BatteryStatus) -> float
|
||
|
|
||
|
b = bs.battery
|
||
|
v = read_voltage(bs)
|
||
|
i = read_current(bs)
|
||
|
|
||
|
p_limits = [
|
||
|
calc_power_limit_imposed_by_voltage_limit(v, i, b.v_min, b.r_int_min),
|
||
|
calc_power_limit_imposed_by_voltage_limit(v, i, b.v_min, b.r_int_max),
|
||
|
calc_power_limit_imposed_by_current_limit(v, i, -b.i_max, b.r_int_min),
|
||
|
calc_power_limit_imposed_by_current_limit(v, i, -b.i_max, b.r_int_max),
|
||
|
]
|
||
|
|
||
|
p_limit = max(p_limits) # p_limit is normally negative here (signed)
|
||
|
p_limit = min(p_limit, 0) # discharge power must not become positive
|
||
|
|
||
|
return int(-p_limit) # make unsigned!
|
||
|
|
||
|
def calc_max_discharge_current(status):
|
||
|
# type: (BatteryStatus) -> float
|
||
|
|
||
|
soc = read_soc(status)
|
||
|
|
||
|
if soc < 21:
|
||
|
return 0
|
||
|
elif soc > 25:
|
||
|
return -status.battery.i_max
|
||
|
elif soc < 25:
|
||
|
return ((soc-21)/25)*(-status.battery.i_max)
|
||
|
|
||
|
def calc_max_charge_current(status):
|
||
|
soc = read_soc(status)
|
||
|
imax = status.battery.i_max
|
||
|
|
||
|
if soc>21 or soc<20:
|
||
|
return imax
|
||
|
else:
|
||
|
return (soc-20)*imax
|
||
|
|
||
|
|
||
|
|
||
|
def read_battery_cold(status):
|
||
|
return \
|
||
|
read_led_green(status) >= LedState.blinking_slow and \
|
||
|
read_led_blue(status) >= LedState.blinking_slow
|
||
|
|
||
|
def read_soc(status):
|
||
|
soc = read_float(register=1053, scale_factor=0.1, offset=0)(status)
|
||
|
|
||
|
# if the SOC is 100 but EOC is not yet reached, report 99.9 instead of 100
|
||
|
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 read_eoc_reached(status):
|
||
|
return \
|
||
|
read_led_green(status) == LedState.on and \
|
||
|
read_led_amber(status) == LedState.off and \
|
||
|
read_led_blue(status) == LedState.off
|
||
|
|
||
|
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_total_power, unit='W'),
|
||
|
|
||
|
BatterySignal('/Dc/0/Temperature', mean, read_float(register=1003, scale_factor=0.1, offset=-400), unit='C'),
|
||
|
|
||
|
BatterySignal('/Heating/Center/Power', sum, get_value=calculate_center_heater_power, unit='W'),
|
||
|
BatterySignal('/Heating/Center/PWM', sum, get_value=read_center_heater_PWM, unit='%'),
|
||
|
|
||
|
BatterySignal('/Heating/Lateral/Power', sum, get_value=calculate_lateral_heater_power, unit='W'),
|
||
|
BatterySignal('/Heating/Lateral/PWM', sum, get_value=read_lateral_heater_PWM, unit='%'),
|
||
|
|
||
|
BatterySignal('/Heating/Power', sum, get_value=calculate_heating_power, unit='W'),
|
||
|
|
||
|
BatterySignal('/Temperature/Center', mean, get_value=read_center_temperature_celcius, unit='C'),
|
||
|
BatterySignal('/Temperature/Lateral1', mean, get_value=read_lateral1_temperature_celcius, unit='C'),
|
||
|
BatterySignal('/Temperature/Lateral2', mean, get_value=read_lateral2_temperature_celcius, unit='C'),
|
||
|
|
||
|
BatterySignal('/BussVoltage', mean, read_float(register=1001, scale_factor=0.01, offset=0), unit='V'),
|
||
|
BatterySignal('/Soc', mean, read_soc, unit='%'),
|
||
|
|
||
|
BatterySignal('/NumberOfWarningFlags', sum, count_bits(base_register=1005, nb_of_registers=3, nb_of_bits=47)),
|
||
|
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=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/Ah_W', any, read_bool(base_register=1005, bit=35)),
|
||
|
BatterySignal('/WarningFlags/MPMM', any, read_bool(base_register=1005, bit=38)),
|
||
|
BatterySignal('/WarningFlags/TCMM', any, read_bool(base_register=1005, bit=39)),
|
||
|
BatterySignal('/WarningFlags/TCdi', any, read_bool(base_register=1005, bit=40)),
|
||
|
BatterySignal('/WarningFlags/WMTO', any, read_bool(base_register=1005, bit=41)),
|
||
|
BatterySignal('/WarningFlags/bit44', any, read_bool(base_register=1005, bit=44)),
|
||
|
BatterySignal('/WarningFlags/CELL1', any, read_bool(base_register=1005, bit=46)),
|
||
|
|
||
|
BatterySignal('/NumberOfAlarmFlags', sum, count_bits(base_register=1009, nb_of_registers=3, nb_of_bits=47)),
|
||
|
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/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/vsm1', any, read_bool(base_register=1009, bit=22)),
|
||
|
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/CCBF', any, read_bool(base_register=1009, bit=33)),
|
||
|
BatterySignal('/AlarmFlags/AhFL', any, read_bool(base_register=1009, bit=34)),
|
||
|
BatterySignal('/AlarmFlags/TbCM', any, read_bool(base_register=1009, bit=36)),
|
||
|
BatterySignal('/AlarmFlags/BRNF', any, read_bool(base_register=1009, bit=37)),
|
||
|
BatterySignal('/AlarmFlags/HTFS', any, read_bool(base_register=1009, bit=42)),
|
||
|
BatterySignal('/AlarmFlags/DATA', any, read_bool(base_register=1009, bit=43)),
|
||
|
BatterySignal('/AlarmFlags/CELL2', any, read_bool(base_register=1009, bit=45)),
|
||
|
|
||
|
BatterySignal('/LedStatus/Red', max, read_led_red),
|
||
|
BatterySignal('/LedStatus/Blue', max, read_led_blue),
|
||
|
BatterySignal('/LedStatus/Green', max, read_led_green),
|
||
|
BatterySignal('/LedStatus/Amber', max, read_led_amber),
|
||
|
|
||
|
BatterySignal('/IoStatus/ConnectedToDcBus', any, read_bool_inverted(base_register=1013, bit=0)),
|
||
|
|
||
|
BatterySignal('/IoStatus/AlarmOutActive', any, read_bool(base_register=1013, bit=1)),
|
||
|
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_bool(base_register=1013, bit=4)),
|
||
|
BatterySignal('/IoStatus/RemoteState', any, read_bool(base_register=1013, bit=5)),
|
||
|
BatterySignal('/IoStatus/HeaterOn', any, read_bool(base_register=1013, bit=6)),
|
||
|
BatterySignal('/IoStatus/EocReached', min, read_eoc_reached),
|
||
|
BatterySignal('/IoStatus/BatteryCold', any, read_battery_cold),
|
||
|
|
||
|
BatterySignal('/Strings/1/Voltage', None, read_float(register=1020, scale_factor=0.01, offset=0), unit='V'),
|
||
|
BatterySignal('/Strings/1/Current', None, read_float(register=1021, scale_factor=0.01, offset=-10000), unit='A'),
|
||
|
BatterySignal('/Strings/1/PWM', None, read_float(register=1025, scale_factor=0.1, offset=0), unit='%'),
|
||
|
BatterySignal('/Strings/2/Voltage', None, read_float(register=1026, scale_factor=0.01, offset=0), unit='V'),
|
||
|
BatterySignal('/Strings/2/Current', None, read_float(register=1027, scale_factor=0.01, offset=-10000), unit='A'),
|
||
|
BatterySignal('/Strings/2/PWM', None, read_float(register=1031, scale_factor=0.1, offset=0), unit='%'),
|
||
|
BatterySignal('/Strings/3/Voltage', None, read_float(register=1032, scale_factor=0.01, offset=0), unit='V'),
|
||
|
BatterySignal('/Strings/3/Current', None, read_float(register=1033, scale_factor=0.01, offset=-10000), unit='A'),
|
||
|
BatterySignal('/Strings/3/PWM', None, read_float(register=1037, scale_factor=0.1, offset=0), unit='%'),
|
||
|
BatterySignal('/Strings/4/Voltage', None, read_float(register=1038, scale_factor=0.01, offset=0), unit='V'),
|
||
|
BatterySignal('/Strings/4/Current', None, read_float(register=1039, scale_factor=0.01, offset=-10000), unit='A'),
|
||
|
BatterySignal('/Strings/4/PWM', None, read_float(register=1043, scale_factor=0.1, offset=0), unit='%'),
|
||
|
BatterySignal('/Strings/5/Voltage', None, read_float(register=1044, scale_factor=0.01, offset=0), unit='V'),
|
||
|
BatterySignal('/Strings/5/Current', None, read_float(register=1045, scale_factor=0.1, offset=-10000), unit='A'),
|
||
|
BatterySignal('/Strings/5/PWM', None, read_float(register=1049, scale_factor=0.1, offset=0), unit='%'),
|
||
|
|
||
|
# see protocol doc page 7
|
||
|
BatterySignal('/Info/MaxDischargeCurrent', sum, get_value= lambda bs: calc_max_discharge_current() , unit='A'), # legacy -bs.battery.i_max
|
||
|
BatterySignal('/Info/MaxChargeCurrent', sum, get_value= lambda bs: calc_max_charge_current() , unit='A'), #legacy bs.battery.i_max
|
||
|
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/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)
|
||
|
|
||
|
]
|