#include <sys/time.h>
#include <sys/types.h>
#include <math.h>
+#include <device/display.h>
+#include <device/callback.h>
#include "proc-common.h"
#include "notifier.h"
#define HEART_BATTERY_DATA_FILE HEART_FILE_PATH"/.battery_data.dat"
#define VCONFKEY_HEART_BATTERY_DEVICE_MODE "db/setting/psmode"
+/*
+ * Remaining Useful Life prediction logic
+ */
+#define RUL_DATA_FILE HEART_FILE_PATH"/.rul_data.dat"
+#define RUL_CHARGING_CYCLES_FILE HEART_FILE_PATH"/.charging_cycles.dat"
+#define RUL_MIN_CAPACITY_DIFF 50
+#define RUL_DISPLAY_ON_CUTOFF_RATIO 0.1f
+#define RUL_DATA_SIZE 32
+
enum {
TA = 0, /* prediction based on total data average */
PCB = 1, /* prediction with physiological behaviors */
DEFAULT_VALUE_MAX = 3,
};
+
+/* Structure to calculate percentage time LCD on during charging */
+struct rul_lcd_data {
+ display_state_e state;
+ long on_time;
+ long on_start_time;
+ long on_stop_time;
+};
+
+/* Structure to store RUL related charging info */
+struct rul_info {
+ long charging_start_time;
+ long charging_stop_time;
+ float raw_capacity; /* capacity (mAh) accumulation during charging */
+ int soc_start; /* SoC(%) value when charging starts */
+ int soc_stop; /* SoC(%) value when charging stops */
+ int charging_cycles; /* Number of charging cycles */
+ int curr_soc; /* Current SoC %, compare with prev_soc to determine if charging */
+ int prev_soc; /* Previous value of SoC % */
+ struct rul_lcd_data lcd_data; /* LCD related data */
+};
+
struct battery_used {
time_t used_time_sec; /* seconds on battery */
time_t last_update_time;
*/
static char battery_header[BATTERY_LINE_MAX] = "BATTERY";
+/*
+ * Remaining Useful Life (RUL) prediction logic
+ */
+static int logic_rul;
+struct rul_info rul;
+
+int heart_battery_get_capacity(void)
+{
+ return batt_stat.curr_capacity;
+}
+
+enum charger_status_type heart_battery_get_charger_status(void)
+{
+ return batt_stat.curr_charger_status;
+}
+
static inline void heart_battery_set_usage_reset_stime(int history, time_t start_time)
{
batt_stat.batt_reset_usage[history].start_time = start_time;
return RESOURCED_ERROR_NONE;
}
+static void rul_display_state_changed_cb(device_callback_e type, void *value, void *user_data)
+{
+ int display_state = (int) ((intptr_t) value), charger_status;
+ /* 0 : on, 1 : dim, 2: off */
+
+ /*
+ * If charger_status==CHARGING and raw capacity calculation ongoing,
+ * then, handle display_on time for RUL
+ */
+
+ charger_status = heart_battery_get_charger_status();
+ if ((charger_status == CHARGING) && (rul.charging_start_time != 0)) {
+ if (type != DEVICE_CALLBACK_DISPLAY_STATE)
+ return;
+
+ if (display_state == DISPLAY_STATE_NORMAL) {
+ _I("DISPLAY: NORMAL/DIM");
+ rul.lcd_data.state = DISPLAY_STATE_NORMAL;
+ if (rul.lcd_data.on_start_time == 0) {
+ rul.lcd_data.on_start_time = logging_get_time(CLOCK_BOOTTIME);
+ }
+ } else if (display_state == DISPLAY_STATE_SCREEN_OFF) {
+ _I("DISPLAY: OFF");
+ rul.lcd_data.state = DISPLAY_STATE_SCREEN_OFF;
+ if (rul.lcd_data.on_start_time > 0) {
+ rul.lcd_data.on_stop_time = logging_get_time(CLOCK_BOOTTIME);
+ rul.lcd_data.on_time += rul.lcd_data.on_stop_time - rul.lcd_data.on_start_time;
+ rul.lcd_data.on_start_time = 0;
+ rul.lcd_data.on_stop_time = 0;
+ }
+ }
+ }
+}
+
+static void rul_write_data_to_file(void)
+{
+ int len = 0;
+ char buff[RUL_DATA_SIZE] = {0};
+ _cleanup_fclose_ FILE *fp = NULL, *fp1 = NULL;
+ float fullcapnom = 0, fullcapnom_mAh = 0;
+
+ /* Update count of Charging cycles*/
+ rul.charging_cycles += 1;
+
+ /* Calculate normalized capacity, i.e., extrapolate for 0% to 100% */
+ fullcapnom = rul.raw_capacity / ((rul.soc_stop/100.0f) - (rul.soc_start/100.0f));
+ fullcapnom_mAh = fullcapnom / 3.6f;
+ _I("rul : raw_capacity = %.4f As, fullcapnom = %.4f As = %.4f mAh", rul.raw_capacity, fullcapnom, fullcapnom_mAh);
+
+ /* Update RUL data file */
+ fp = fopen(RUL_DATA_FILE, "a");
+ if (fp == NULL) {
+ _E("Could not open file - RUL_DATA_FILE");
+ return;
+ }
+
+ len = snprintf(buff, RUL_DATA_SIZE, "%d %.4f\n", rul.charging_cycles, fullcapnom_mAh);
+
+ if (len < RUL_DATA_SIZE)
+ fputs(buff, fp);
+
+ /* Write Charging cycles count to file */
+ fp1 = fopen(RUL_CHARGING_CYCLES_FILE, "w");
+ if (fp1 == NULL) {
+ _E("Could not open file for write - RUL_CHARGING_CYCLES_FILE");
+ return;
+ }
+ fprintf(fp1, "%d", rul.charging_cycles);
+
+}
+
+static void rul_charging_cycle_end_work(void)
+{
+ int capacity;
+ long total_charging_time;
+ bool lcd_on_time_check;
+ float lcd_on_ratio = 1.0f;
+
+ capacity = heart_battery_get_capacity();
+
+ rul.soc_stop = capacity;
+ rul.charging_stop_time = logging_get_time(CLOCK_BOOTTIME);
+
+ /* If LCD was on before disconnecting charger, update display_on time */
+ if (rul.lcd_data.on_start_time != 0) {
+ rul.lcd_data.on_stop_time = logging_get_time(CLOCK_BOOTTIME);
+ rul.lcd_data.on_time += rul.lcd_data.on_stop_time - rul.lcd_data.on_start_time;
+ }
+
+ /* Max LCD ON Time ratio criteria met ? */
+ total_charging_time = rul.charging_stop_time - rul.charging_start_time;
+ if (total_charging_time != 0)
+ lcd_on_ratio = (rul.lcd_data.on_time / (total_charging_time * 1.0f));
+ lcd_on_time_check = lcd_on_ratio <= RUL_DISPLAY_ON_CUTOFF_RATIO;
+
+ /* write data to file if condititons met*/
+ if ((rul.charging_start_time != 0) && (lcd_on_time_check == true) &&
+ (rul.soc_stop - rul.soc_start >= RUL_MIN_CAPACITY_DIFF))
+ rul_write_data_to_file();
+
+ /* init for next charging cycle */
+ rul.soc_start = 0;
+ rul.soc_stop = 0;
+ rul.charging_start_time = 0;
+ rul.charging_stop_time = 0;
+ rul.lcd_data.on_start_time = 0;
+ rul.lcd_data.on_stop_time = 0;
+ rul.lcd_data.on_time = 0;
+}
+
+static void rul_calculate_raw_capacity(void)
+{
+ /*
+ * This function updates Raw Capacity every 1% SoC change.
+ *
+ * Upon first time 1% SoC change, initialize rul_info variables.
+ *
+ * Subsequently, Update Raw Capacity using time interval and
+ * current_now sys node values.
+ *
+ * If capacity = 100%, dump collected data
+ */
+
+ long rul_curr_time, rul_time_interval;
+ static long prev_charging_time;
+ int fg_curr_inst, capacity, ret;
+ float fg_curr_inst_f;
+ static float prev_fg_curr_inst_f = 0.0f;
+
+ capacity = heart_battery_get_capacity();
+
+ if (rul.charging_start_time == 0) {
+ rul.charging_start_time = logging_get_time(CLOCK_BOOTTIME);
+ rul.soc_start = capacity;
+ rul.raw_capacity = 0.0f;
+ prev_charging_time = rul.charging_start_time;
+ prev_fg_curr_inst_f = 0.0f;
+ rul.lcd_data.on_start_time = 0;
+ rul.lcd_data.on_stop_time = 0;
+ rul.lcd_data.on_time = 0;
+
+ /* Get display status at time of initiating RUL calculations */
+ ret = device_display_get_state(&rul.lcd_data.state);
+ if (ret != DEVICE_ERROR_NONE) {
+ _E("Failed to get device display state with err = %d", ret);
+ return;
+ }
+ if ((rul.lcd_data.state == DISPLAY_STATE_NORMAL) || (rul.lcd_data.state == DISPLAY_STATE_SCREEN_DIM))
+ rul.lcd_data.on_start_time = logging_get_time(CLOCK_BOOTTIME);
+ } else {
+ rul_curr_time = logging_get_time(CLOCK_BOOTTIME);
+ rul_time_interval = rul_curr_time - prev_charging_time;
+ prev_charging_time = rul_curr_time;
+
+ ret = fread_int("/sys/class/power_supply/battery/current_now", &fg_curr_inst);
+ if (ret != RESOURCED_ERROR_NONE) {
+ _E("This device doesn't support FG CURRENT_NOW information. Disable LOGIC_RUL");
+ return;
+ }
+
+ fg_curr_inst_f = fg_curr_inst / 1000.0f;
+ rul.raw_capacity += ((fg_curr_inst_f + prev_fg_curr_inst_f) / 2.0f)*rul_time_interval;
+ prev_fg_curr_inst_f = fg_curr_inst_f;
+ }
+
+ if (capacity == 100) {
+ rul_charging_cycle_end_work();
+ }
+}
+
/* ============================ DBUS -> DEVICED on demand ==================== */
static int heart_battery_direct_get_capacity(void)
first_level_change = FALSE;
}
}
+ if (logic_rul) {
+ /* Update prev_soc, curr_soc */
+ rul.prev_soc = rul.curr_soc;
+ rul.curr_soc = capacity;
+
+ /*
+ * If charger_status==CHARGING and SOC_INCREASING, start/continue raw capacity calculation.
+ * Else, if raw capacity calculation was ongoing, then dump collected data.
+ */
+ if ((batt_stat.curr_charger_status == CHARGING) && (rul.curr_soc >= rul.prev_soc))
+ rul_calculate_raw_capacity();
+ else if (rul.charging_start_time != 0)
+ rul_charging_cycle_end_work();
+ }
}
static void heart_battery_charger_status(GVariant *params)
heart_battery_cal_charging_rem_time();
}
}
+ if (logic_rul) {
+ /*
+ * If charger disconnected and raw capacity calculation was ongoing,
+ * then dump collected data.
+ */
+ if ((charger_status == DISCHARGING) && (rul.charging_start_time != 0))
+ rul_charging_cycle_end_work();
+ }
}
/* ========================= DBUS -> DEVICED handler END ==================== */
if (!strncmp(result->name, "LOGIC_V2", sizeof("LOGIC_V2") + 1)) {
logic_v2 = atoi(result->value);
_I("logic_v2 : %d", logic_v2);
+ } else if (!strncmp(result->name, "LOGIC_RUL", sizeof("LOGIC_RUL") + 1)) {
+ logic_rul = atoi(result->value);
+ _I("logic_rul : %d", logic_rul);
} else if (!strncmp(result->name, "DISCHARGE_FAST", sizeof("DISCHARGE_FAST") + 1)) {
discharge_fast = atoi(result->value);
_I("discharge_fast: %d", discharge_fast);
batt_capacity, battery_header);
}
+static void rul_data_init(void)
+{
+ _cleanup_fclose_ FILE *fp = NULL;
+ int ret;
+
+ rul.charging_start_time = 0;
+ rul.charging_stop_time = 0;
+ rul.raw_capacity = 0;
+ rul.soc_start = 0;
+ rul.soc_stop = 0;
+ rul.curr_soc = heart_battery_direct_get_capacity();
+ rul.prev_soc = rul.curr_soc;
+
+ /* Init LCD related data */
+ rul.lcd_data.on_start_time = 0;
+ rul.lcd_data.on_stop_time = 0;
+ rul.lcd_data.on_time = 0;
+ ret = device_display_get_state(&rul.lcd_data.state);
+ if (ret != DEVICE_ERROR_NONE) {
+ _E("Failed to get device display state with err = %d", ret);
+ return;
+ }
+
+ /* Register callback for LCD state change */
+ device_add_callback(DEVICE_CALLBACK_DISPLAY_STATE, rul_display_state_changed_cb, NULL);
+
+ /* Read Charging cycles count from file */
+ fp = fopen(RUL_CHARGING_CYCLES_FILE, "r");
+ if (fp == NULL) {
+ _E("Can't open RUL_CHARGING_CYCLES_FILE");
+ rul.charging_cycles = 0;
+ return;
+ }
+
+ if (fscanf(fp, "%d", &rul.charging_cycles) != 1) {
+ _E("Unable to assign charging cycles from file");
+ rul.charging_cycles = 0;
+ return;
+ }
+}
+
static int heart_battery_init(void *data)
{
int ret;
heart_battery_status_init();
+ if (logic_rul)
+ rul_data_init();
+
register_notifier(RESOURCED_NOTIFIER_LOW_BATTERY, low_battery_handler);
register_notifier(RESOURCED_NOTIFIER_DATA_RESET, heart_battery_reset);