mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-03 17:59:27 +02:00
Merge pull request #1291 from kyberias/bmw-ix-ext
Eliminate BMW IX extended datalayer
This commit is contained in:
commit
ae218e98ee
5 changed files with 208 additions and 196 deletions
|
@ -101,36 +101,6 @@ void BmwIXBattery::update_values() { //This function maps all the values fetche
|
|||
|
||||
datalayer.battery.info.number_of_cells = detected_number_of_cells;
|
||||
|
||||
datalayer_extended.bmwix.min_cell_voltage_data_age = (millis() - min_cell_voltage_lastchanged);
|
||||
|
||||
datalayer_extended.bmwix.max_cell_voltage_data_age = (millis() - max_cell_voltage_lastchanged);
|
||||
|
||||
datalayer_extended.bmwix.T30_Voltage = terminal30_12v_voltage;
|
||||
|
||||
datalayer_extended.bmwix.hvil_status = hvil_status;
|
||||
|
||||
datalayer_extended.bmwix.bms_uptime = sme_uptime;
|
||||
|
||||
datalayer_extended.bmwix.pyro_status_pss1 = pyro_status_pss1;
|
||||
|
||||
datalayer_extended.bmwix.pyro_status_pss4 = pyro_status_pss4;
|
||||
|
||||
datalayer_extended.bmwix.pyro_status_pss6 = pyro_status_pss6;
|
||||
|
||||
datalayer_extended.bmwix.iso_safety_positive = iso_safety_positive;
|
||||
|
||||
datalayer_extended.bmwix.iso_safety_negative = iso_safety_negative;
|
||||
|
||||
datalayer_extended.bmwix.iso_safety_parallel = iso_safety_parallel;
|
||||
|
||||
datalayer_extended.bmwix.allowable_charge_amps = allowable_charge_amps;
|
||||
|
||||
datalayer_extended.bmwix.allowable_discharge_amps = allowable_discharge_amps;
|
||||
|
||||
datalayer_extended.bmwix.balancing_status = balancing_status;
|
||||
|
||||
datalayer_extended.bmwix.battery_voltage_after_contactor = battery_voltage_after_contactor;
|
||||
|
||||
if (battery_info_available) {
|
||||
// If we have data from battery - override the defaults to suit
|
||||
datalayer.battery.info.max_design_voltage_dV = max_design_voltage;
|
||||
|
@ -493,30 +463,26 @@ void BmwIXBattery::HandleIncomingUserRequest(void) {
|
|||
// Debug user request to open or close the contactors
|
||||
#ifdef DEBUG_LOG
|
||||
logging.print("User request: contactor close: ");
|
||||
logging.print(datalayer_extended.bmwix.UserRequestContactorClose);
|
||||
logging.print(userRequestContactorClose);
|
||||
logging.print(" User request: contactor open: ");
|
||||
logging.println(datalayer_extended.bmwix.UserRequestContactorOpen);
|
||||
logging.println(userRequestContactorOpen);
|
||||
#endif // DEBUG_LOG
|
||||
if ((datalayer_extended.bmwix.UserRequestContactorClose == false) &&
|
||||
(datalayer_extended.bmwix.UserRequestContactorOpen == false)) {
|
||||
if ((userRequestContactorClose == false) && (userRequestContactorOpen == false)) {
|
||||
// do nothing
|
||||
} else if ((datalayer_extended.bmwix.UserRequestContactorClose == true) &&
|
||||
(datalayer_extended.bmwix.UserRequestContactorOpen == false)) {
|
||||
} else if ((userRequestContactorClose == true) && (userRequestContactorOpen == false)) {
|
||||
BmwIxCloseContactors();
|
||||
// set user request to false
|
||||
datalayer_extended.bmwix.UserRequestContactorClose = false;
|
||||
} else if ((datalayer_extended.bmwix.UserRequestContactorClose == false) &&
|
||||
(datalayer_extended.bmwix.UserRequestContactorOpen == true)) {
|
||||
userRequestContactorClose = false;
|
||||
} else if ((userRequestContactorClose == false) && (userRequestContactorOpen == true)) {
|
||||
BmwIxOpenContactors();
|
||||
// set user request to false
|
||||
datalayer_extended.bmwix.UserRequestContactorOpen = false;
|
||||
} else if ((datalayer_extended.bmwix.UserRequestContactorClose == true) &&
|
||||
(datalayer_extended.bmwix.UserRequestContactorOpen == true)) {
|
||||
userRequestContactorOpen = false;
|
||||
} else if ((userRequestContactorClose == true) && (userRequestContactorOpen == true)) {
|
||||
// these flasgs should not be true at the same time, therefore open contactors, as that is the safest state
|
||||
BmwIxOpenContactors();
|
||||
// set user request to false
|
||||
datalayer_extended.bmwix.UserRequestContactorClose = false;
|
||||
datalayer_extended.bmwix.UserRequestContactorOpen = false;
|
||||
userRequestContactorClose = false;
|
||||
userRequestContactorOpen = false;
|
||||
// print error, as both these flags shall not be true at the same time
|
||||
#ifdef DEBUG_LOG
|
||||
logging.println(
|
||||
|
@ -695,3 +661,50 @@ void BmwIXBattery::HandleBmwIxOpenContactorsRequest(uint16_t counter_10ms) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Getter implementations for HTML renderer
|
||||
int BmwIXBattery::get_battery_voltage_after_contactor() const {
|
||||
return battery_voltage_after_contactor;
|
||||
}
|
||||
unsigned long BmwIXBattery::get_min_cell_voltage_data_age() const {
|
||||
return millis() - min_cell_voltage_lastchanged;
|
||||
}
|
||||
unsigned long BmwIXBattery::get_max_cell_voltage_data_age() const {
|
||||
return millis() - max_cell_voltage_lastchanged;
|
||||
}
|
||||
int BmwIXBattery::get_T30_Voltage() const {
|
||||
return terminal30_12v_voltage;
|
||||
}
|
||||
int BmwIXBattery::get_balancing_status() const {
|
||||
return balancing_status;
|
||||
}
|
||||
int BmwIXBattery::get_hvil_status() const {
|
||||
return hvil_status;
|
||||
}
|
||||
unsigned long BmwIXBattery::get_bms_uptime() const {
|
||||
return sme_uptime;
|
||||
}
|
||||
int BmwIXBattery::get_allowable_charge_amps() const {
|
||||
return allowable_charge_amps;
|
||||
}
|
||||
int BmwIXBattery::get_allowable_discharge_amps() const {
|
||||
return allowable_discharge_amps;
|
||||
}
|
||||
int BmwIXBattery::get_iso_safety_positive() const {
|
||||
return iso_safety_positive;
|
||||
}
|
||||
int BmwIXBattery::get_iso_safety_negative() const {
|
||||
return iso_safety_negative;
|
||||
}
|
||||
int BmwIXBattery::get_iso_safety_parallel() const {
|
||||
return iso_safety_parallel;
|
||||
}
|
||||
int BmwIXBattery::get_pyro_status_pss1() const {
|
||||
return pyro_status_pss1;
|
||||
}
|
||||
int BmwIXBattery::get_pyro_status_pss4() const {
|
||||
return pyro_status_pss4;
|
||||
}
|
||||
int BmwIXBattery::get_pyro_status_pss6() const {
|
||||
return pyro_status_pss6;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
class BmwIXBattery : public CanBattery {
|
||||
public:
|
||||
BmwIXBattery() : renderer(*this) {}
|
||||
|
||||
virtual void setup(void);
|
||||
virtual void handle_incoming_can_frame(CAN_frame rx_frame);
|
||||
virtual void update_values();
|
||||
|
@ -19,12 +21,32 @@ class BmwIXBattery : public CanBattery {
|
|||
|
||||
bool supports_contactor_close() { return true; }
|
||||
|
||||
void request_open_contactors() { datalayer_extended.bmwix.UserRequestContactorOpen = true; }
|
||||
void request_close_contactors() { datalayer_extended.bmwix.UserRequestContactorClose = true; }
|
||||
void request_open_contactors() { userRequestContactorOpen = true; }
|
||||
void request_close_contactors() { userRequestContactorClose = true; }
|
||||
|
||||
static constexpr const char* Name = "BMW iX and i4-7 platform";
|
||||
|
||||
// Getter methods for HTML renderer
|
||||
int get_battery_voltage_after_contactor() const;
|
||||
unsigned long get_min_cell_voltage_data_age() const;
|
||||
unsigned long get_max_cell_voltage_data_age() const;
|
||||
int get_T30_Voltage() const;
|
||||
int get_balancing_status() const;
|
||||
int get_hvil_status() const;
|
||||
unsigned long get_bms_uptime() const;
|
||||
int get_allowable_charge_amps() const;
|
||||
int get_allowable_discharge_amps() const;
|
||||
int get_iso_safety_positive() const;
|
||||
int get_iso_safety_negative() const;
|
||||
int get_iso_safety_parallel() const;
|
||||
int get_pyro_status_pss1() const;
|
||||
int get_pyro_status_pss4() const;
|
||||
int get_pyro_status_pss6() const;
|
||||
|
||||
private:
|
||||
bool userRequestContactorClose = false;
|
||||
bool userRequestContactorOpen = false;
|
||||
|
||||
BmwIXHtmlRenderer renderer;
|
||||
static const int MAX_PACK_VOLTAGE_DV = 4650; //4650 = 465.0V
|
||||
static const int MIN_PACK_VOLTAGE_DV = 3000;
|
||||
|
|
120
Software/src/battery/BMW-IX-HTML.cpp
Normal file
120
Software/src/battery/BMW-IX-HTML.cpp
Normal file
|
@ -0,0 +1,120 @@
|
|||
#include "BMW-IX-HTML.h"
|
||||
#include "../include.h"
|
||||
#include "BMW-IX-BATTERY.h"
|
||||
|
||||
String BmwIXHtmlRenderer::get_status_html() {
|
||||
String content;
|
||||
|
||||
content += "<h4>Battery Voltage after Contactor: " + String(batt.get_battery_voltage_after_contactor()) + " dV</h4>";
|
||||
content += "<h4>Max Design Voltage: " + String(datalayer.battery.info.max_design_voltage_dV) + " dV</h4>";
|
||||
content += "<h4>Min Design Voltage: " + String(datalayer.battery.info.min_design_voltage_dV) + " dV</h4>";
|
||||
content += "<h4>Max Cell Design Voltage: " + String(datalayer.battery.info.max_cell_voltage_mV) + " mV</h4>";
|
||||
content += "<h4>Min Cell Design Voltage: " + String(datalayer.battery.info.min_cell_voltage_mV) + " mV</h4>";
|
||||
content += "<h4>Min Cell Voltage Data Age: " + String(batt.get_min_cell_voltage_data_age()) + " ms</h4>";
|
||||
content += "<h4>Max Cell Voltage Data Age: " + String(batt.get_max_cell_voltage_data_age()) + " ms</h4>";
|
||||
content += "<h4>Allowed Discharge Power: " + String(datalayer.battery.status.max_discharge_power_W) + " W</h4>";
|
||||
content += "<h4>Allowed Charge Power: " + String(datalayer.battery.status.max_charge_power_W) + " W</h4>";
|
||||
content += "<h4>T30 Terminal Voltage: " + String(batt.get_T30_Voltage()) + " mV</h4>";
|
||||
content += "<h4>Detected Cell Count: " + String(datalayer.battery.info.number_of_cells) + "</h4>";
|
||||
content += "<h4>Balancing: ";
|
||||
switch (batt.get_balancing_status()) {
|
||||
case 0:
|
||||
content += "0 No balancing mode active</h4>";
|
||||
break;
|
||||
case 1:
|
||||
content += "1 Voltage-Controlled Balancing Mode</h4>";
|
||||
break;
|
||||
case 2:
|
||||
content += "2 Time-Controlled Balancing Mode with Demand Calculation at End of Charging</h4>";
|
||||
break;
|
||||
case 3:
|
||||
content += "3 Time-Controlled Balancing Mode with Demand Calculation at Resting Voltage</h4>";
|
||||
break;
|
||||
case 4:
|
||||
content += "4 No balancing mode active, qualifier invalid</h4>";
|
||||
break;
|
||||
default:
|
||||
content += "Unknown</h4>";
|
||||
}
|
||||
content += "<h4>HVIL Status: ";
|
||||
switch (batt.get_hvil_status()) {
|
||||
case 0:
|
||||
content += "Error (Loop Open)</h4>";
|
||||
break;
|
||||
case 1:
|
||||
content += "OK (Loop Closed)</h4>";
|
||||
break;
|
||||
default:
|
||||
content += "Unknown</h4>";
|
||||
}
|
||||
content += "<h4>BMS Uptime: " + String(batt.get_bms_uptime()) + " seconds</h4>";
|
||||
content += "<h4>BMS Allowed Charge Amps: " + String(batt.get_allowable_charge_amps()) + " A</h4>";
|
||||
content += "<h4>BMS Allowed Disharge Amps: " + String(batt.get_allowable_discharge_amps()) + " A</h4>";
|
||||
content += "<br>";
|
||||
content += "<h3>HV Isolation (2147483647kOhm = maximum/invalid)</h3>";
|
||||
content += "<h4>Isolation Positive: " + String(batt.get_iso_safety_positive()) + " kOhm</h4>";
|
||||
content += "<h4>Isolation Negative: " + String(batt.get_iso_safety_negative()) + " kOhm</h4>";
|
||||
content += "<h4>Isolation Parallel: " + String(batt.get_iso_safety_parallel()) + " kOhm</h4>";
|
||||
content += "<h4>Pyro Status PSS1: ";
|
||||
switch (batt.get_pyro_status_pss1()) {
|
||||
case 0:
|
||||
content += "0 Value Invalid</h4>";
|
||||
break;
|
||||
case 1:
|
||||
content += "1 Successfully Blown</h4>";
|
||||
break;
|
||||
case 2:
|
||||
content += "2 Disconnected</h4>";
|
||||
break;
|
||||
case 3:
|
||||
content += "3 Not Activated - Pyro Intact</h4>";
|
||||
break;
|
||||
case 4:
|
||||
content += "4 Unknown</h4>";
|
||||
break;
|
||||
default:
|
||||
content += "Unknown</h4>";
|
||||
}
|
||||
content += "<h4>Pyro Status PSS4: ";
|
||||
switch (batt.get_pyro_status_pss4()) {
|
||||
case 0:
|
||||
content += "0 Value Invalid</h4>";
|
||||
break;
|
||||
case 1:
|
||||
content += "1 Successfully Blown</h4>";
|
||||
break;
|
||||
case 2:
|
||||
content += "2 Disconnected</h4>";
|
||||
break;
|
||||
case 3:
|
||||
content += "3 Not Activated - Pyro Intact</h4>";
|
||||
break;
|
||||
case 4:
|
||||
content += "4 Unknown</h4>";
|
||||
break;
|
||||
default:
|
||||
content += "Unknown</h4>";
|
||||
}
|
||||
content += "<h4>Pyro Status PSS6: ";
|
||||
switch (batt.get_pyro_status_pss6()) {
|
||||
case 0:
|
||||
content += "0 Value Invalid</h4>";
|
||||
break;
|
||||
case 1:
|
||||
content += "1 Successfully Blown</h4>";
|
||||
break;
|
||||
case 2:
|
||||
content += "2 Disconnected</h4>";
|
||||
break;
|
||||
case 3:
|
||||
content += "3 Not Activated - Pyro Intact</h4>";
|
||||
break;
|
||||
case 4:
|
||||
content += "4 Unknown</h4>";
|
||||
break;
|
||||
default:
|
||||
content += "Unknown</h4>";
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
|
@ -2,132 +2,18 @@
|
|||
#define _BMW_IX_HTML_H
|
||||
|
||||
#include "../datalayer/datalayer.h"
|
||||
#include "../datalayer/datalayer_extended.h"
|
||||
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
|
||||
|
||||
class BmwIXBattery;
|
||||
|
||||
class BmwIXHtmlRenderer : public BatteryHtmlRenderer {
|
||||
private:
|
||||
BmwIXBattery& batt;
|
||||
|
||||
public:
|
||||
String get_status_html() {
|
||||
String content;
|
||||
BmwIXHtmlRenderer(BmwIXBattery& b) : batt(b) {}
|
||||
|
||||
content +=
|
||||
"<h4>Battery Voltage after Contactor: " + String(datalayer_extended.bmwix.battery_voltage_after_contactor) +
|
||||
" dV</h4>";
|
||||
content += "<h4>Max Design Voltage: " + String(datalayer.battery.info.max_design_voltage_dV) + " dV</h4>";
|
||||
content += "<h4>Min Design Voltage: " + String(datalayer.battery.info.min_design_voltage_dV) + " dV</h4>";
|
||||
content += "<h4>Max Cell Design Voltage: " + String(datalayer.battery.info.max_cell_voltage_mV) + " mV</h4>";
|
||||
content += "<h4>Min Cell Design Voltage: " + String(datalayer.battery.info.min_cell_voltage_mV) + " mV</h4>";
|
||||
content +=
|
||||
"<h4>Min Cell Voltage Data Age: " + String(datalayer_extended.bmwix.min_cell_voltage_data_age) + " ms</h4>";
|
||||
content +=
|
||||
"<h4>Max Cell Voltage Data Age: " + String(datalayer_extended.bmwix.max_cell_voltage_data_age) + " ms</h4>";
|
||||
content += "<h4>Allowed Discharge Power: " + String(datalayer.battery.status.max_discharge_power_W) + " W</h4>";
|
||||
content += "<h4>Allowed Charge Power: " + String(datalayer.battery.status.max_charge_power_W) + " W</h4>";
|
||||
content += "<h4>T30 Terminal Voltage: " + String(datalayer_extended.bmwix.T30_Voltage) + " mV</h4>";
|
||||
content += "<h4>Detected Cell Count: " + String(datalayer.battery.info.number_of_cells) + "</h4>";
|
||||
content += "<h4>Balancing: ";
|
||||
switch (datalayer_extended.bmwix.balancing_status) {
|
||||
case 0:
|
||||
content += "0 No balancing mode active</h4>";
|
||||
break;
|
||||
case 1:
|
||||
content += "1 Voltage-Controlled Balancing Mode</h4>";
|
||||
break;
|
||||
case 2:
|
||||
content += "2 Time-Controlled Balancing Mode with Demand Calculation at End of Charging</h4>";
|
||||
break;
|
||||
case 3:
|
||||
content += "3 Time-Controlled Balancing Mode with Demand Calculation at Resting Voltage</h4>";
|
||||
break;
|
||||
case 4:
|
||||
content += "4 No balancing mode active, qualifier invalid</h4>";
|
||||
break;
|
||||
default:
|
||||
content += "Unknown</h4>";
|
||||
}
|
||||
content += "<h4>HVIL Status: ";
|
||||
switch (datalayer_extended.bmwix.hvil_status) {
|
||||
case 0:
|
||||
content += "Error (Loop Open)</h4>";
|
||||
break;
|
||||
case 1:
|
||||
content += "OK (Loop Closed)</h4>";
|
||||
break;
|
||||
default:
|
||||
content += "Unknown</h4>";
|
||||
}
|
||||
content += "<h4>BMS Uptime: " + String(datalayer_extended.bmwix.bms_uptime) + " seconds</h4>";
|
||||
content += "<h4>BMS Allowed Charge Amps: " + String(datalayer_extended.bmwix.allowable_charge_amps) + " A</h4>";
|
||||
content +=
|
||||
"<h4>BMS Allowed Disharge Amps: " + String(datalayer_extended.bmwix.allowable_discharge_amps) + " A</h4>";
|
||||
content += "<br>";
|
||||
content += "<h3>HV Isolation (2147483647kOhm = maximum/invalid)</h3>";
|
||||
content += "<h4>Isolation Positive: " + String(datalayer_extended.bmwix.iso_safety_positive) + " kOhm</h4>";
|
||||
content += "<h4>Isolation Negative: " + String(datalayer_extended.bmwix.iso_safety_negative) + " kOhm</h4>";
|
||||
content += "<h4>Isolation Parallel: " + String(datalayer_extended.bmwix.iso_safety_parallel) + " kOhm</h4>";
|
||||
content += "<h4>Pyro Status PSS1: ";
|
||||
switch (datalayer_extended.bmwix.pyro_status_pss1) {
|
||||
case 0:
|
||||
content += "0 Value Invalid</h4>";
|
||||
break;
|
||||
case 1:
|
||||
content += "1 Successfully Blown</h4>";
|
||||
break;
|
||||
case 2:
|
||||
content += "2 Disconnected</h4>";
|
||||
break;
|
||||
case 3:
|
||||
content += "3 Not Activated - Pyro Intact</h4>";
|
||||
break;
|
||||
case 4:
|
||||
content += "4 Unknown</h4>";
|
||||
break;
|
||||
default:
|
||||
content += "Unknown</h4>";
|
||||
}
|
||||
content += "<h4>Pyro Status PSS4: ";
|
||||
switch (datalayer_extended.bmwix.pyro_status_pss4) {
|
||||
case 0:
|
||||
content += "0 Value Invalid</h4>";
|
||||
break;
|
||||
case 1:
|
||||
content += "1 Successfully Blown</h4>";
|
||||
break;
|
||||
case 2:
|
||||
content += "2 Disconnected</h4>";
|
||||
break;
|
||||
case 3:
|
||||
content += "3 Not Activated - Pyro Intact</h4>";
|
||||
break;
|
||||
case 4:
|
||||
content += "4 Unknown</h4>";
|
||||
break;
|
||||
default:
|
||||
content += "Unknown</h4>";
|
||||
}
|
||||
content += "<h4>Pyro Status PSS6: ";
|
||||
switch (datalayer_extended.bmwix.pyro_status_pss6) {
|
||||
case 0:
|
||||
content += "0 Value Invalid</h4>";
|
||||
break;
|
||||
case 1:
|
||||
content += "1 Successfully Blown</h4>";
|
||||
break;
|
||||
case 2:
|
||||
content += "2 Disconnected</h4>";
|
||||
break;
|
||||
case 3:
|
||||
content += "3 Not Activated - Pyro Intact</h4>";
|
||||
break;
|
||||
case 4:
|
||||
content += "4 Unknown</h4>";
|
||||
break;
|
||||
default:
|
||||
content += "Unknown</h4>";
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
String get_status_html();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -40,34 +40,6 @@ typedef struct {
|
|||
int16_t battery_current_7E4 = 0;
|
||||
} DATALAYER_INFO_BOLTAMPERA;
|
||||
|
||||
typedef struct {
|
||||
/** User requesting contactor open or close via WebUI*/
|
||||
bool UserRequestContactorClose = false;
|
||||
bool UserRequestContactorOpen = false;
|
||||
/** uint16_t */
|
||||
/** Terminal 30 - 12V SME Supply Voltage */
|
||||
uint16_t T30_Voltage = 0;
|
||||
/** Status HVIL, 1 HVIL OK, 0 HVIL disconnected*/
|
||||
uint8_t hvil_status = 0;
|
||||
/** Min/Max Cell SOH*/
|
||||
uint16_t min_soh_state = 0;
|
||||
uint16_t max_soh_state = 0;
|
||||
uint32_t bms_uptime = 0;
|
||||
uint8_t pyro_status_pss1 = 0;
|
||||
uint8_t pyro_status_pss4 = 0;
|
||||
uint8_t pyro_status_pss6 = 0;
|
||||
int32_t iso_safety_positive = 0;
|
||||
int32_t iso_safety_negative = 0;
|
||||
int32_t iso_safety_parallel = 0;
|
||||
int32_t allowable_charge_amps = 0;
|
||||
int32_t allowable_discharge_amps = 0;
|
||||
int16_t balancing_status = 0;
|
||||
int16_t battery_voltage_after_contactor = 0;
|
||||
unsigned long min_cell_voltage_data_age = 0;
|
||||
unsigned long max_cell_voltage_data_age = 0;
|
||||
|
||||
} DATALAYER_INFO_BMWIX;
|
||||
|
||||
typedef struct {
|
||||
/** uint8_t */
|
||||
/** Status isolation external, 0 not evaluated, 1 OK, 2 error active, 3 Invalid signal*/
|
||||
|
@ -851,7 +823,6 @@ typedef struct {
|
|||
class DataLayerExtended {
|
||||
public:
|
||||
DATALAYER_INFO_BOLTAMPERA boltampera;
|
||||
DATALAYER_INFO_BMWIX bmwix;
|
||||
DATALAYER_INFO_BMWPHEV bmwphev;
|
||||
DATALAYER_INFO_BYDATTO3 bydAtto3;
|
||||
DATALAYER_INFO_CELLPOWER cellpower;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue