#include "gui/common/NamedField.hpp" #include "texts/TextKeysAndLanguages.hpp" #include "touchgfx/Unicode.hpp" #include "params.h" #include "vehicle_state.h" #include #include #define VEH_FIELD(FIELD) []() { return (void *)&vehicle_state.FIELD; } #define VEH_BIT_FIELD(FIELD) \ []() { \ static int x; \ x = vehicle_state.FIELD; \ return (void *)&x; \ } void *get_tsstate_text() { const char *text; switch (vehicle_state.ts_state) { case TS_INACTIVE: text = "INACT"; break; case TS_ACTIVE: text = "ACT"; break; case TS_PRECHARGE: text = "PRECH"; break; case TS_DISCHARGE: text = "DISCH"; break; case TS_ERROR: text = "ERROR"; break; case TS_CHARGING_CHECK: text = "CH_CHK"; break; case TS_CHARGING: text = "CH"; break; default: text = "UNKNOWN"; } return (void *)text; } void *get_asstate_text() { const char *text; switch (vehicle_state.as_state) { case AS_OFF: text = "OFF"; break; case AS_MANUAL: text = "MAN"; break; case AS_READY: text = "RDY"; break; case AS_DRIVING: text = "DRIVE"; break; case AS_FINISHED: text = "FIN"; break; case AS_EMERGENCY: text = "EMERG"; break; default: text = "UNKNOWN"; } return (void *)text; } void *get_mission_text() { const char *text; switch (vehicle_state.active_mission) { case MISSION_NONE: text = "NONE"; break; case MISSION_ACCEL: text = "ACCEL"; break; case MISSION_SKIDPAD: text = "SKIDPAD"; break; case MISSION_AUTOX: text = "AUTOX"; break; case MISSION_TRACKDRIVE: text = "TRACK"; break; case MISSION_EBS: text = "EBS"; break; case MISSION_INSPECTION: text = "INSPECT"; break; case MISSION_MANUAL: text = "MAN"; break; default: text = "UNKNOWN"; } return (void *)text; } void *get_r2dprog_text() { const char *text; switch (vehicle_state.r2d_progress) { case R2D_NONE: text = "NONE"; break; case R2D_TSMS: text = "TSMS"; break; case R2D_TSACTIVE: text = "TSACT"; break; case R2D_RESETTING_NODES: text = "RST NODE"; break; case R2D_RESETTING_COMMS: text = "RST COM"; break; case R2D_WAITING_INIT: text = "WAIT INIT"; break; case R2D_INIT_STAGE1: text = "INIT S1"; break; case R2D_INIT_STAGE2: text = "INIT S2"; break; case R2D_INIT_SUCCESS: text = "INIT SUCC"; break; default: text = "UNKNOWN"; } return (void *)text; } void *get_inichk_text() { return (void *)inichkstate_str(vehicle_state.ini_chk_state); } void *get_sdc_text() { const char *text; if (vehicle_state.errors.sdc_bfl) { text = "BFL"; } else if (vehicle_state.errors.sdc_brl) { text = "BRL"; } else if (vehicle_state.errors.sdc_acc) { text = "ACC"; } else if (vehicle_state.errors.sdc_hvb) { text = "HVB"; } else { text = "CLOSED"; } return (void *)text; } void *get_err_text() { const char *text; if (vehicle_state.errors.err_sdc) { text = "SDC"; } else if (vehicle_state.errors.err_ams) { text = "AMS"; } else if (vehicle_state.errors.err_pdu) { text = "PDU"; } else if (vehicle_state.errors.err_ini_chk) { text = "IniChk"; } else if (vehicle_state.errors.err_con_mon) { text = "ConMon"; } else if (vehicle_state.errors.err_scs) { text = "SCS"; } else if (vehicle_state.errors.err_sbspd) { text = "sBSPD"; } else if (vehicle_state.errors.err_appsp) { text = "APPSp"; } else if (vehicle_state.errors.err_as) { text = "AS"; } else if (vehicle_state.errors.err_ros) { text = "ROS"; } else if (vehicle_state.errors.err_res) { text = "RES"; } else if (vehicle_state.errors.err_invl) { text = "INVL"; } else if (vehicle_state.errors.err_invr) { text = "INVR"; } else { text = "NONE"; } return (void *)text; } void *get_zero() { static float zero = 0.0f; return &zero; } NamedFieldDescription dataFieldDescs[] = { [DF_TSState] = {NamedFieldKind::Text, "TSSTATE", 1, 0, get_tsstate_text}, [DF_ASState] = {NamedFieldKind::Text, "ASSTATE", 1, 0, get_asstate_text}, [DF_ActiveMission] = {NamedFieldKind::Text, "MISSION", 1, 0, get_mission_text}, [DF_R2DProgress] = {NamedFieldKind::Text, "R2DPROG", 1, 0, get_r2dprog_text}, [DF_INVLReady] = {NamedFieldKind::Bool, "INVLRDY", 0, 0, VEH_BIT_FIELD(errors.invl_ready)}, [DF_INVRReady] = {NamedFieldKind::Bool, "INVRRDY", 0, 0, VEH_BIT_FIELD(errors.invr_ready)}, [DF_SDC] = {NamedFieldKind::Text, "SDC", 0, 0, get_sdc_text}, [DF_ERR] = {NamedFieldKind::Text, "ERROR", 0, 0, get_err_text}, [DF_IniChkState] = {NamedFieldKind::Text, "ICSTATE", 1, 0, get_inichk_text}, [DF_LapCount] = {NamedFieldKind::Int, "LAPS", 3, 0, VEH_FIELD(lap_count)}, [DF_TireTempFL] = {NamedFieldKind::Float, "TTFL", 2, 1, VEH_FIELD(temps.tire_fl)}, [DF_TireTempFR] = {NamedFieldKind::Float, "TTFR", 2, 1, VEH_FIELD(temps.tire_fr)}, [DF_TireTempRL] = {NamedFieldKind::Float, "TTRL", 2, 1, VEH_FIELD(temps.tire_rl)}, [DF_TireTempRR] = {NamedFieldKind::Float, "TTRR", 2, 1, VEH_FIELD(temps.tire_rr)}, [DF_MinCellVolt] = {NamedFieldKind::Float, "VMIN", 1, 2, VEH_FIELD(min_cell_volt)}, [DF_MaxCellTemp] = {NamedFieldKind::Float, "TBAT", 2, 1, VEH_FIELD(max_cell_temp)}, [DF_TSSoC] = {NamedFieldKind::Int, "TSSOC", 3, 0, VEH_FIELD(soc_ts)}, [DF_LVSoC] = {NamedFieldKind::Int, "LVSOC", 3, 0, VEH_FIELD(soc_lv)}, [DF_TSCurrent] = {NamedFieldKind::Float, "ITS", 3, 0, VEH_FIELD(ts_current)}, [DF_TSVoltageBat] = {NamedFieldKind::Float, "TSVBAT", 3, 1, VEH_FIELD(ts_voltage_bat)}, [DF_TSVoltageVeh] = {NamedFieldKind::Float, "TSVVEH", 3, 1, VEH_FIELD(ts_voltage_veh)}, [DF_Speed] = {NamedFieldKind::Float, "SPEED", 3, 0, VEH_FIELD(speed)}, [DF_BBal] = {NamedFieldKind::Float, "BBAL", 3, 1, get_zero}, [DF_BPF] = {NamedFieldKind::Float, "BPF", 3, 1, VEH_FIELD(brake_press_f)}, [DF_BPR] = {NamedFieldKind::Float, "BPR", 3, 1, VEH_FIELD(brake_press_r)}, [DF_DistanceTotal] = {NamedFieldKind::Float, "DIST", 3, 1, VEH_FIELD(distance_total)}, [DF_TempMotL] = {NamedFieldKind::Float, "TMOTL", 2, 1, VEH_FIELD(temps.mot_l)}, [DF_TempMotR] = {NamedFieldKind::Float, "TMOTR", 2, 1, VEH_FIELD(temps.mot_r)}, [DF_TempInvL] = {NamedFieldKind::Float, "TINVL", 2, 1, VEH_FIELD(temps.inv_l)}, [DF_TempInvR] = {NamedFieldKind::Float, "TINVR", 2, 1, VEH_FIELD(temps.inv_r)}, [DF_TempBrakeFL] = {NamedFieldKind::Float, "TBFL", 3, 0, VEH_FIELD(temps.brake_fl)}, [DF_TempBrakeFR] = {NamedFieldKind::Float, "TBFR", 3, 0, VEH_FIELD(temps.brake_fr)}, [DF_TempBrakeRL] = {NamedFieldKind::Float, "TBRL", 3, 0, VEH_FIELD(temps.brake_rl)}, [DF_TempBrakeRR] = {NamedFieldKind::Float, "TBRR", 3, 0, VEH_FIELD(temps.brake_rr)}, [DF_LapBest] = {NamedFieldKind::Float, "LAPBEST", 3, 1, VEH_FIELD(lap_best)}, [DF_LapLast] = {NamedFieldKind::Float, "LAPLAST", 3, 1, VEH_FIELD(lap_last)}, [DF_LVBatVoltage] = {NamedFieldKind::Float, "LVVBAT", 2, 2, VEH_FIELD(lv_bat_voltage)}, }; static_assert(sizeof(dataFieldDescs) / sizeof(dataFieldDescs[0]) == DataFieldType_COUNT, "Incorrect number of data field descriptions"); #define PARAM_FIELD(FIELD) []() { return (void *)¶ms.FIELD; } NamedFieldDescription paramFieldDescs[] = { [PF_BBAL] = {NamedFieldKind::Float, "BBAL", 2, 1, PARAM_FIELD(bbal)}, [PF_SLIPREF] = {NamedFieldKind::Float, "SLIPREF", 2, 2, PARAM_FIELD(slipref)}, [PF_MUMAX] = {NamedFieldKind::Float, "MUMAX", 2, 1, PARAM_FIELD(mumax)}, [PF_ASRP] = {NamedFieldKind::Int, "ASR-P", 2, 0, PARAM_FIELD(asrp)}, [PF_ASRON] = {NamedFieldKind::Int, "ASR-ON", 2, 0, PARAM_FIELD(asron)}, [PF_ASRI] = {NamedFieldKind::Int, "ASR-I", 2, 0, PARAM_FIELD(asri)}, [PF_PLIM] = {NamedFieldKind::Int, "PLIM", 2, 0, PARAM_FIELD(plim)}, }; static_assert(sizeof(paramFieldDescs) / sizeof(paramFieldDescs[0]) == ParamType_COUNT, "Incorrect number of param field descriptions"); DataFieldType dataFieldByAlphaIndex[DataFieldType_COUNT]; size_t dataFieldAlphaIndexByField[DataFieldType_COUNT]; ParamType paramByAlphaIndex[ParamType_COUNT]; size_t paramAlphaIndexByParam[ParamType_COUNT]; template struct NFAlphabeticComp { NFAlphabeticComp(const NamedFieldDescription *fieldDescs) : fieldDescs{fieldDescs} {} const NamedFieldDescription *fieldDescs; bool operator()(const T &a, const T &b) const { return strcmp(fieldDescs[a].title, fieldDescs[b].title) < 0; } }; template void namedFieldSort(const NamedFieldDescription *fieldDescs, T *fieldByAlpha, size_t *alphaIndexByField, size_t numFields) { for (size_t i = 0; i < numFields; i++) { fieldByAlpha[i] = static_cast(i); } std::sort(fieldByAlpha, fieldByAlpha + numFields, NFAlphabeticComp(fieldDescs)); for (size_t i = 0; i < numFields; i++) { alphaIndexByField[fieldByAlpha[i]] = i; } } void namedFieldSort() { namedFieldSort(dataFieldDescs, dataFieldByAlphaIndex, dataFieldAlphaIndexByField, DataFieldType_COUNT); namedFieldSort(paramFieldDescs, paramByAlphaIndex, paramAlphaIndexByParam, ParamType_COUNT); } template NamedField::NamedField(const NamedFieldDescription *fieldDescs) : fieldDescs{fieldDescs} {} template void NamedField::setType(T type) { this->type = type; desc = &fieldDescs[type]; touchgfx::Unicode::strncpy(titleBuffer, desc->title, sizeof(titleBuffer) / sizeof(*titleBuffer)); titleBufferUpdated(); typeUpdated(); updateValue(); } template const T &NamedField::getType() { return type; } template void NamedField::updateValue() { void *val = desc->getValue(); switch (desc->kind) { case NamedFieldKind::Float: setFloatValue(*static_cast(val)); break; case NamedFieldKind::Bool: setBoolValue(*static_cast(val)); break; case NamedFieldKind::Text: setStrValue(static_cast(val)); break; case NamedFieldKind::Int: setIntValue(*static_cast(val)); break; } } template void NamedField::setFloatValue(float floatValue) { fieldValue.f = floatValue; updateValueBuffer(); } template void NamedField::setBoolValue(int boolValue) { fieldValue.b = boolValue; updateValueBuffer(); } template void NamedField::setIntValue(int intValue) { fieldValue.i = intValue; updateValueBuffer(); } template void NamedField::setStrValue(const char *strValue) { touchgfx::Unicode::strncpy(valueBuffer, strValue, sizeof(valueBuffer) / sizeof(*valueBuffer)); updateValueBuffer(); } template void NamedField::updateValueBuffer() { switch (desc->kind) { case NamedFieldKind::Float: { size_t width = desc->int_digits; if (desc->decimal_digits != 0) { width += desc->decimal_digits + 1; // 1 digit for the decimal point } float params[3] = {(float)width, (float)desc->decimal_digits, fieldValue.f}; touchgfx::Unicode::snprintfFloats( valueBuffer, sizeof(valueBuffer) / sizeof(*valueBuffer), "%*.*f", params); break; } case NamedFieldKind::Bool: { const char *str = fieldValue.b ? "YES" : "NO"; touchgfx::Unicode::strncpy(valueBuffer, str, sizeof(valueBuffer) / sizeof(*valueBuffer)); break; } case NamedFieldKind::Text: // This is handled by the child class in setValue() break; case NamedFieldKind::Int: touchgfx::Unicode::snprintf(valueBuffer, sizeof(valueBuffer) / sizeof(*valueBuffer), "%*d", desc->int_digits, fieldValue.i); break; } valueBufferUpdated(); } template class NamedField; template class NamedField;