1559 lines
42 KiB
C
1559 lines
42 KiB
C
/**
|
|
* Copyright (C) 2020 Evgeny Zinoviev
|
|
* This file is part of isv <https://github.com/gch1p/isv>.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <math.h>
|
|
|
|
#include "p18.h"
|
|
#include "util.h"
|
|
|
|
const char *p18_query_cmds[] = {
|
|
"PI", /* Protocol ID */
|
|
"T", /* Current time */
|
|
"ET", /* Total generated energy */
|
|
"EY", /* Year generated energy */
|
|
"EM", /* Month generated energy */
|
|
"ED", /* Day generated energy */
|
|
"ID", /* Series number */
|
|
"VFW", /* CPU version */
|
|
"PIRI", /* Rated information */
|
|
"GS", /* General status */
|
|
"MOD", /* Working mode */
|
|
"FWS", /* Fault and warning status */
|
|
"FLAG", /* Enable/Disable flags statuses */
|
|
"DI", /* Default values of changeable parameters */
|
|
"MCHGCR", /* Max charging current selectable (allowed) values */
|
|
"MUCHGCR", /* Max AC charging current selectable (allowed) values */
|
|
"PRI", /* Parallel system rated information */
|
|
"PGS", /* Parallel system general status */
|
|
"ACCT", /* AC charge time bucket */
|
|
"ACLT", /* AC supply load time bucket */
|
|
};
|
|
|
|
const char *p18_set_cmds[] = {
|
|
"LON", /* Enable/disable machine supply power to the loads */
|
|
"P", /* Enable/disable flags */
|
|
"PF", /* Reset changeable params */
|
|
"MCHGC", /* Battery maximum charge current */
|
|
"MUCHGC", /* Battery maximum AC charge current */
|
|
/* The protocol documentation defines two commands, "F50" and "F60",
|
|
but it's identical as if there were just one "F" command with an argument. */
|
|
"F", /* Set AC output frequency to be 50Hz or 60Hz */
|
|
"MCHGV", /* Battery maximum charge voltage */
|
|
"V", /* AC output rated voltage */
|
|
"POP", /* Output source priority */
|
|
"BUCD", /* Battery re-charged and re-discharged voltage when utility is available */
|
|
"PCP", /* Charging source priority */
|
|
"PSP", /* Solar power priority */
|
|
"PGR", /* AC input voltage range */
|
|
"PBT", /* Battery type */
|
|
"POPM", /* Output model */
|
|
"PSDV", /* Battery cut-off voltage */
|
|
"ID", /* Set solar configuration */
|
|
"CLE", /* Clear all data of generated energy */
|
|
"DAT", /* Set date and time */
|
|
"ACCT", /* AC charge time bucket */
|
|
"ACLT", /* AC supply load time bucket */
|
|
};
|
|
|
|
const p18_fault_code_list_item_t fault_codes[] = {
|
|
{1, "Fan is locked"},
|
|
{2, "Over temperature"},
|
|
{3, "Battery voltage is too high"},
|
|
{4, "Battery voltage is too low"},
|
|
{5, "Output short circuited or Over temperature"},
|
|
{6, "Output voltage is too high"},
|
|
{7, "Over load time out"},
|
|
{8, "Bus voltage is too high"},
|
|
{9, "Bus soft start failed"},
|
|
{11, "Main relay failed"},
|
|
{51, "Over current inverter"},
|
|
{52, "Bus soft start failed"},
|
|
{53, "Inverter soft start failed"},
|
|
{54, "Self-test failed"},
|
|
{55, "Over DC voltage on output of inverter"},
|
|
{56, "Battery connection is open"},
|
|
{57, "Current sensor failed"},
|
|
{58, "Output voltage is too low"},
|
|
{60, "Inverter negative power"},
|
|
{71, "Parallel version different"},
|
|
{72, "Output circuit failed"},
|
|
{80, "CAN communication failed"},
|
|
{81, "Parallel host line lost"},
|
|
{82, "Parallel synchronized signal lost"},
|
|
{83, "Parallel battery voltage detect different"},
|
|
{84, "Parallel Line voltage or frequency detect different"},
|
|
{85, "Parallel Line input current unbalanced"},
|
|
{86, "Parallel output setting different"},
|
|
};
|
|
|
|
const p18_flag_printable_list_item_t p18_flags_printable_list[9] = {
|
|
{"BUZZ", "A", "Silence buzzer or open buzzer"},
|
|
{"OLBP", "B", "Overload bypass function"},
|
|
{"LCDE", "C", "LCD display escape to default page after 1min timeout"},
|
|
{"OLRS", "D", "Overload restart"},
|
|
{"OTRS", "E", "Overload temperature restart"},
|
|
{"BLON", "F", "Backlight on"},
|
|
{"ALRM", "G", "Alarm on primary source interrupt"},
|
|
{"FTCR", "H", "Fault code record"},
|
|
{"MTYP", "I", "Machine type (1=Grid-Tie, 0=Off-Grid-Tie)"},
|
|
};
|
|
|
|
const int p18_ac_output_rated_voltages[5] = {
|
|
202,
|
|
208,
|
|
220,
|
|
230,
|
|
240,
|
|
};
|
|
|
|
const char *p18_battery_util_recharging_voltages_12v_unit[8] = {
|
|
"11",
|
|
"11.3",
|
|
"11.5",
|
|
"11.8",
|
|
"12",
|
|
"12.3",
|
|
"12.5",
|
|
"12.8",
|
|
};
|
|
|
|
const char *p18_battery_util_recharging_voltages_24v_unit[8] = {
|
|
"22",
|
|
"22.5",
|
|
"23",
|
|
"23.5",
|
|
"24",
|
|
"24.5",
|
|
"25",
|
|
"25.5",
|
|
};
|
|
const char *p18_battery_util_recharging_voltages_48v_unit[8] = {
|
|
"44",
|
|
"45",
|
|
"46",
|
|
"47",
|
|
"48",
|
|
"49",
|
|
"50",
|
|
"51",
|
|
};
|
|
|
|
const char *p18_battery_util_redischarging_voltages_12v_unit[12] = {
|
|
"0",
|
|
"12",
|
|
"12.3",
|
|
"12.5",
|
|
"12.8",
|
|
"13",
|
|
"13.3",
|
|
"13.5",
|
|
"13.8",
|
|
"14",
|
|
"14.3",
|
|
"14.5",
|
|
};
|
|
const char *p18_battery_util_redischarging_voltages_24v_unit[12] = {
|
|
"0",
|
|
"24",
|
|
"24.5",
|
|
"25",
|
|
"25.5",
|
|
"26",
|
|
"26.5",
|
|
"27",
|
|
"27.5",
|
|
"28",
|
|
"28.5",
|
|
"29"
|
|
};
|
|
const char *p18_battery_util_redischarging_voltages_48v_unit[12] = {
|
|
"0",
|
|
"48",
|
|
"49",
|
|
"50",
|
|
"51",
|
|
"52",
|
|
"53",
|
|
"54",
|
|
"55",
|
|
"56",
|
|
"57",
|
|
"58",
|
|
};
|
|
|
|
const char *p18_battery_types[] = {
|
|
"AGM",
|
|
"Flooded",
|
|
"User",
|
|
};
|
|
|
|
const char *p18_voltage_ranges[] = {
|
|
"Appliance",
|
|
"UPS"
|
|
};
|
|
|
|
const char *p18_output_source_priorities[] = {
|
|
"Solar-Utility-Battery",
|
|
"Solar-Battery-Utility"
|
|
};
|
|
|
|
const char *p18_charger_source_priorities[] = {
|
|
"Solar-First",
|
|
"Solar-and-Utility",
|
|
"Solar-Only"
|
|
};
|
|
|
|
const char *p18_machine_types[] = {
|
|
"Off-Grid-Tie",
|
|
"Grid-Tie"
|
|
};
|
|
|
|
const char *p18_topologies[] = {
|
|
"Transformerless",
|
|
"Transformer"
|
|
};
|
|
|
|
const char *p18_output_model_settings[] = {
|
|
"Single module",
|
|
"Parallel output",
|
|
"Phase 1 of three phase output",
|
|
"Phase 2 of three phase output",
|
|
"Phase 3 of three phase",
|
|
};
|
|
|
|
const char *p18_solar_power_priorities[] = {
|
|
"Battery-Load-Utility",
|
|
"Load-Battery-Utility"
|
|
};
|
|
|
|
const char *p18_mppt_charger_statuses[] = {
|
|
"Abnormal",
|
|
"Not charging",
|
|
"Charging"
|
|
};
|
|
|
|
const char *p18_battery_power_directions[] = {
|
|
"Do nothing",
|
|
"Charge",
|
|
"Discharge"
|
|
};
|
|
|
|
const char *p18_dc_ac_power_directions[] = {
|
|
"Do nothing",
|
|
"AC/DC",
|
|
"DC/AC"
|
|
};
|
|
|
|
const char *p18_line_power_directions[] = {
|
|
"Do nothing",
|
|
"Input",
|
|
"Output",
|
|
};
|
|
|
|
const char *p18_working_modes[] = {
|
|
"Power on mode",
|
|
"Standby mode",
|
|
"Bypass mode",
|
|
"Battery mode",
|
|
"Fault mode",
|
|
"Hybrid mode",
|
|
};
|
|
|
|
const char *p18_parallel_connection_statuses[] = {
|
|
"Non-existent",
|
|
"Existent"
|
|
};
|
|
|
|
const char *p18_unknown = "Unknown";
|
|
|
|
/* ------------------------------------------ */
|
|
|
|
static char *p18_append_arguments(int command, char *buf, const char **a)
|
|
{
|
|
switch (command) {
|
|
case P18_QUERY_YEAR_GENERATED:
|
|
buf += sprintf(buf, "%s", a[0]);
|
|
break;
|
|
|
|
case P18_QUERY_MONTH_GENERATED:
|
|
buf += sprintf(buf, "%s%02d", a[0], atoi(a[1]));
|
|
break;
|
|
|
|
case P18_QUERY_DAY_GENERATED:
|
|
buf += sprintf(buf, "%s%02d%02d",
|
|
a[0], atoi(a[1]), atoi(a[2]));
|
|
break;
|
|
|
|
case P18_QUERY_PARALLEL_RATED_INFORMATION:
|
|
case P18_QUERY_PARALLEL_GENERAL_STATUS:
|
|
buf += sprintf(buf, "%d", atoi(a[0]));
|
|
break;
|
|
|
|
case P18_SET_LOADS:
|
|
buf += sprintf(buf, "%s", a[0]);
|
|
break;
|
|
|
|
case P18_SET_FLAG:
|
|
buf += sprintf(buf, "%c%s",
|
|
*a[1] == '1' ? 'E' : 'D',
|
|
a[0]);
|
|
break;
|
|
|
|
case P18_SET_BAT_MAX_CHARGE_CURRENT:
|
|
case P18_SET_BAT_MAX_AC_CHARGE_CURRENT:
|
|
buf += sprintf(buf, "%c,%03d", *a[0], atoi(a[1]));
|
|
break;
|
|
|
|
case P18_SET_AC_OUTPUT_FREQ:
|
|
buf += sprintf(buf, "%02d", atoi(a[0]));
|
|
break;
|
|
|
|
case P18_SET_BAT_MAX_CHARGE_VOLTAGE: {
|
|
double cv = atof(a[0]);
|
|
double fv = atof(a[1]);
|
|
buf += sprintf(buf, "%03d,%03d", (int)round(cv*10), (int)round(fv*10));
|
|
break;
|
|
}
|
|
|
|
case P18_SET_AC_OUTPUT_RATED_VOLTAGE: {
|
|
int v = atoi(a[0]);
|
|
buf += sprintf(buf, "%04d", v*10);
|
|
break;
|
|
}
|
|
|
|
case P18_SET_OUTPUT_SOURCE_PRIORITY:
|
|
case P18_SET_SOLAR_POWER_PRIORITY:
|
|
case P18_SET_AC_INPUT_VOLTAGE_RANGE:
|
|
case P18_SET_BAT_TYPE:
|
|
buf += sprintf(buf, "%c", *a[0]);
|
|
break;
|
|
|
|
case P18_SET_BAT_CHARGING_THRESHOLDS_WHEN_UTILITY_AVAIL: {
|
|
double cv = atof(a[0]);
|
|
double dv = atof(a[1]);
|
|
buf += sprintf(buf, "%03d,%03d", (int)round(cv*10), (int)round(dv*10));
|
|
break;
|
|
}
|
|
|
|
case P18_SET_CHARGING_SOURCE_PRIORITY:
|
|
case P18_SET_OUTPUT_MODEL:
|
|
buf += sprintf(buf, "%c,%c", *a[0], *a[1]);
|
|
break;
|
|
|
|
case P18_SET_BAT_CUTOFF_VOLTAGE: {
|
|
double v = atof(a[0]);
|
|
buf += sprintf(buf, "%03d", (int)round(v*10));
|
|
break;
|
|
}
|
|
|
|
case P18_SET_SOLAR_CONFIG: {
|
|
int len = (int)strlen(a[0]);
|
|
buf += sprintf(buf, "%02d", len);
|
|
buf += sprintf(buf, "%s", a[0]);
|
|
if (len < 20) {
|
|
for (int i = 0; i < 20-len; i++)
|
|
buf += sprintf(buf, "%c", '0');
|
|
}
|
|
break;
|
|
}
|
|
|
|
case P18_SET_DATE_TIME: {
|
|
int Y = atoi(a[0]) - 2000;
|
|
int M = atoi(a[1]);
|
|
int D = atoi(a[2]);
|
|
int h = atoi(a[3]);
|
|
int m = atoi(a[4]);
|
|
int s = atoi(a[5]);
|
|
buf += sprintf(buf, "%02d%02d%02d%02d%02d%02d", Y, M, D, h, m, s);
|
|
break;
|
|
}
|
|
|
|
case P18_SET_AC_CHARGE_TIME_BUCKET:
|
|
case P18_SET_AC_SUPPLY_LOAD_TIME_BUCKET:
|
|
buf += sprintf(buf, "%02d%02d,%02d%02d",
|
|
atoi(a[0]), atoi(a[1]), atoi(a[2]), atoi(a[3]));
|
|
break;
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
bool p18_build_command(int command, const char **args, size_t args_size, char *buf)
|
|
{
|
|
UNUSED(args_size);
|
|
|
|
bool set = command >= P18_SET_CMDS_ENUM_OFFSET;
|
|
int offset;
|
|
const char **list;
|
|
size_t list_size;
|
|
if (!set) {
|
|
list = p18_query_cmds;
|
|
list_size = ARRAY_SIZE(p18_query_cmds);
|
|
offset = P18_QUERY_CMDS_ENUM_OFFSET;
|
|
} else {
|
|
list = p18_set_cmds;
|
|
list_size = ARRAY_SIZE(p18_set_cmds);
|
|
offset = P18_SET_CMDS_ENUM_OFFSET;
|
|
}
|
|
|
|
int cmd_index = command - offset;
|
|
if (cmd_index < 0 || cmd_index >= (int)list_size) {
|
|
LOG("%s: invalid cmd_index %d\n", __func__, cmd_index);
|
|
return false;
|
|
}
|
|
|
|
(*buf++) = '^';
|
|
(*buf++) = set ? 'S' : 'P';
|
|
|
|
char *len_p = buf;
|
|
buf += sprintf(buf, "000");
|
|
buf += sprintf(buf, "%s", list[cmd_index]);
|
|
|
|
buf = p18_append_arguments(command, buf, args);
|
|
|
|
char len_buf[4];
|
|
sprintf(len_buf, "%03zu", (size_t)(buf-len_p));
|
|
memcpy(len_p, len_buf, 3);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool p18_validate_query_response(const char *buf, size_t size, size_t *data_size)
|
|
{
|
|
if (buf[0] != '^' || buf[1] != 'D')
|
|
return false;
|
|
|
|
char lenbuf[4];
|
|
memcpy(lenbuf, &buf[2], 3);
|
|
lenbuf[3] = '\0';
|
|
|
|
unsigned int len = atoi(lenbuf);
|
|
if (size < len+4)
|
|
return false;
|
|
|
|
if (data_size)
|
|
*data_size = len-3;
|
|
return true;
|
|
}
|
|
|
|
bool p18_set_result(const char *buf, size_t size)
|
|
{
|
|
if (size < 2) {
|
|
ERROR("%s: response is too small\n", __func__);
|
|
return false;
|
|
}
|
|
if (buf[0] != '^') {
|
|
ERROR("%s: invalid character %c, ^ expected\n", __func__, *buf);
|
|
return false;
|
|
}
|
|
return buf[1] == '1';
|
|
}
|
|
|
|
static void p18_parse_list(
|
|
const char *data,
|
|
void *message_ptr,
|
|
int expected_items_count, /* -1 means no fixed limit */
|
|
p18_parse_cb_t callback)
|
|
{
|
|
int data_size = (int)strlen(data);
|
|
static const int buf_size = 64;
|
|
char buf[buf_size];
|
|
char *c = (char *)data;
|
|
|
|
for (int i = 0, char_index = 0, item_index = 0;
|
|
i < data_size;
|
|
i++) {
|
|
|
|
if (*c != ',' && i < data_size-1) {
|
|
if (char_index < buf_size-1)
|
|
buf[char_index++] = *c;
|
|
c++;
|
|
continue;
|
|
}
|
|
|
|
/* last character */
|
|
if (*c != ',' && char_index < buf_size-1)
|
|
buf[char_index++] = *c;
|
|
|
|
if (expected_items_count != -1 && item_index >= expected_items_count)
|
|
ERROR("warning: item %d is not expected\n", item_index);
|
|
else {
|
|
buf[char_index] = '\0';
|
|
callback((const char *)buf, char_index, item_index, message_ptr);
|
|
}
|
|
|
|
char_index = 0;
|
|
c++;
|
|
item_index++;
|
|
}
|
|
}
|
|
|
|
static void p18_expect_listitem_length(const char *f,
|
|
int index,
|
|
size_t expected_len,
|
|
size_t actual_len) {
|
|
if (actual_len != expected_len) {
|
|
LOG("%s: length of item %d is %zu != %zu\n",
|
|
f, index, actual_len, expected_len);
|
|
}
|
|
}
|
|
|
|
/* ------------------------------------------ */
|
|
/* Command-specific methods */
|
|
|
|
P18_UNPACK_FN(protocol_id)
|
|
{
|
|
char s[4];
|
|
strncpy(s, data, 2);
|
|
s[2] = '\0';
|
|
|
|
p18_protocol_id_msg_t m;
|
|
m.id = atoi(s);
|
|
|
|
return m;
|
|
}
|
|
|
|
P18_UNPACK_FN(current_time)
|
|
{
|
|
char s[4];
|
|
int n;
|
|
p18_current_time_msg_t m;
|
|
|
|
substr_copy(s, data, 4);
|
|
m.year = atoi(s);
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
substr_copy(s, data + 4 + (i * 2), 2);
|
|
n = atoi(s);
|
|
switch (i) {
|
|
case 0: m.month = n; break;
|
|
case 1: m.day = n; break;
|
|
case 2: m.hour = n; break;
|
|
case 3: m.minute = n; break;
|
|
case 4: m.second = n; break;
|
|
default:
|
|
ERROR("%s: warning: unexpected code path, i=%d\n",
|
|
__func__, i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
static long parse_generated(const char *buf)
|
|
{
|
|
char s[16];
|
|
strncpy(s, buf, 8);
|
|
s[8] = '\0';
|
|
return atol(s);
|
|
}
|
|
|
|
P18_UNPACK_FN(total_generated)
|
|
{
|
|
p18_total_generated_msg_t m = {
|
|
.kwh = parse_generated(data)
|
|
};
|
|
return m;
|
|
}
|
|
|
|
P18_UNPACK_FN(year_generated)
|
|
{
|
|
p18_year_generated_msg_t m = {
|
|
.kwh = parse_generated(data)
|
|
};
|
|
return m;
|
|
}
|
|
|
|
P18_UNPACK_FN(month_generated)
|
|
{
|
|
p18_month_generated_msg_t m = {
|
|
.kwh = parse_generated(data)
|
|
};
|
|
return m;
|
|
}
|
|
|
|
P18_UNPACK_FN(day_generated)
|
|
{
|
|
p18_day_generated_msg_t m = {
|
|
.kwh = parse_generated(data)
|
|
};
|
|
return m;
|
|
}
|
|
|
|
P18_UNPACK_FN(series_number)
|
|
{
|
|
p18_series_number_msg_t m;
|
|
|
|
/* read length, reuse the already available id datafer */
|
|
substr_copy(m.id, data, 2);
|
|
m.length = atoi(m.id);
|
|
|
|
/* read id of given length */
|
|
substr_copy(m.id, data + 2, m.length);
|
|
|
|
return m;
|
|
}
|
|
|
|
P18_PARSE_CB_FN(cpu_version)
|
|
{
|
|
//LOG("%s: value=%s, value_len=%d\n", __func__, value, value_len);
|
|
|
|
p18_cpu_version_msg_t *m = (p18_cpu_version_msg_t *)message_ptr;
|
|
P18_EXPECT_LISTITEM_LENGTH(5);
|
|
|
|
switch (index) {
|
|
case 0:
|
|
substr_copy(m->main_cpu_version, value, 5);
|
|
break;
|
|
|
|
case 1:
|
|
substr_copy(m->slave1_cpu_version, value, 5);
|
|
break;
|
|
|
|
case 2:
|
|
substr_copy(m->slave2_cpu_version, value, 5);
|
|
break;
|
|
}
|
|
}
|
|
P18_UNPACK_FN(cpu_version)
|
|
{
|
|
P18_PARSE_LIST_AND_RETURN(cpu_version, 3);
|
|
}
|
|
|
|
P18_PARSE_CB_FN(rated_information)
|
|
{
|
|
p18_rated_information_msg_t *m = (p18_rated_information_msg_t *)message_ptr;
|
|
int num = atoi(value);
|
|
switch (index) {
|
|
case 0: /* AAAA */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->ac_input_rating_voltage = num;
|
|
break;
|
|
case 1: /* BBB */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->ac_input_rating_current = num;
|
|
break;
|
|
case 2: /* CCCC */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->ac_output_rating_voltage = num;
|
|
break;
|
|
case 3: /* DDD */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->ac_output_rating_freq = num;
|
|
break;
|
|
case 4: /* EEE */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->ac_output_rating_current = num;
|
|
break;
|
|
case 5: /* FFFF */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->ac_output_rating_apparent_power = num;
|
|
break;
|
|
case 6: /* GGGG */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->ac_output_rating_active_power = num;
|
|
break;
|
|
case 7: /* HHH */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->battery_rating_voltage = num;
|
|
break;
|
|
case 8: /* III */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->battery_recharge_voltage = num;
|
|
break;
|
|
case 9: /* JJJ */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->battery_redischarge_voltage = num;
|
|
break;
|
|
case 10: /* KKK */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->battery_under_voltage = num;
|
|
break;
|
|
case 11: /* LLL */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->battery_bulk_voltage = num;
|
|
break;
|
|
case 12: /* MMM */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->battery_float_voltage = num;
|
|
break;
|
|
case 13: /* N */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->battery_type = num;
|
|
break;
|
|
case 14: /* OO */
|
|
P18_EXPECT_LISTITEM_LENGTH(2);
|
|
m->max_ac_charging_current = num;
|
|
break;
|
|
case 15: /* PPP */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->max_charging_current = num;
|
|
break;
|
|
case 16: /* Q */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->input_voltage_range = num;
|
|
break;
|
|
case 17: /* R */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->output_source_priority = num;
|
|
break;
|
|
case 18: /* S */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->charger_source_priority = num;
|
|
break;
|
|
case 19: /* T */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->parallel_max_num = num;
|
|
break;
|
|
case 20: /* U */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->machine_type = num;
|
|
break;
|
|
case 21: /* V */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->topology = num;
|
|
break;
|
|
case 22: /* W */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->output_model_setting = num;
|
|
break;
|
|
case 23: /* Z */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->solar_power_priority = num;
|
|
break;
|
|
case 24: /* a */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
substr_copy(m->mppt, value, 1);
|
|
break;
|
|
}
|
|
}
|
|
P18_UNPACK_FN(rated_information)
|
|
{
|
|
P18_PARSE_LIST_AND_RETURN(rated_information, 25)
|
|
}
|
|
|
|
P18_PARSE_CB_FN(general_status)
|
|
{
|
|
p18_general_status_msg_t *m = (p18_general_status_msg_t *)message_ptr;
|
|
int num = atoi(value);
|
|
switch (index) {
|
|
case 0: /* AAAA */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->grid_voltage = num;
|
|
break;
|
|
case 1: /* BBB */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->grid_freq = num;
|
|
break;
|
|
case 2: /* CCCC */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->ac_output_voltage = num;
|
|
break;
|
|
case 3: /* DDD */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->ac_output_freq = num;
|
|
break;
|
|
case 4: /* EEEE */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->ac_output_apparent_power = num;
|
|
break;
|
|
case 5: /* FFFF */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->ac_output_active_power = num;
|
|
break;
|
|
case 6: /* GGG */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->output_load_percent = num;
|
|
break;
|
|
case 7: /* HHH */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->battery_voltage = num;
|
|
break;
|
|
case 8: /* III */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->battery_voltage_scc = num;
|
|
break;
|
|
case 9: /* JJJ */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->battery_voltage_scc2 = num;
|
|
break;
|
|
case 10: /* KKK */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->battery_discharge_current = num;
|
|
break;
|
|
case 11: /* LLL */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->battery_charging_current = num;
|
|
break;
|
|
case 12: /* MMM */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->battery_capacity = num;
|
|
break;
|
|
case 13: /* NNN */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->inverter_heat_sink_temp = num;
|
|
break;
|
|
case 14: /* OOO */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->mppt1_charger_temp = num;
|
|
break;
|
|
case 15: /* PPP */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->mppt2_charger_temp = num;
|
|
break;
|
|
case 16: /* QQQQ */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->pv1_input_power = num;
|
|
break;
|
|
case 17: /* RRRR */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->pv2_input_power = num;
|
|
break;
|
|
case 18: /* SSSS */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->pv1_input_voltage = num;
|
|
break;
|
|
case 19: /* TTTT */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->pv2_input_voltage = num;
|
|
break;
|
|
case 20: /* U */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->settings_values_changed = num == 1;
|
|
break;
|
|
case 21: /* V */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->mppt1_charger_status = num;
|
|
break;
|
|
case 22: /* W */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->mppt2_charger_status = num;
|
|
break;
|
|
case 23: /* X */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->load_connected = num == 1;
|
|
break;
|
|
case 24: /* Y */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->battery_power_direction = num;
|
|
break;
|
|
case 25: /* Z */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->dc_ac_power_direction = num;
|
|
break;
|
|
case 26: /* a */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->line_power_direction = num;
|
|
break;
|
|
case 27: /* b */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->local_parallel_id = num;
|
|
break;
|
|
}
|
|
}
|
|
P18_UNPACK_FN(general_status)
|
|
{
|
|
P18_PARSE_LIST_AND_RETURN(general_status, 28)
|
|
}
|
|
|
|
P18_UNPACK_FN(working_mode)
|
|
{
|
|
char s[4];
|
|
strncpy(s, data, 2);
|
|
s[2] = '\0';
|
|
|
|
p18_working_mode_msg_t m;
|
|
m.mode = atoi(s);
|
|
|
|
return m;
|
|
}
|
|
|
|
P18_PARSE_CB_FN(faults_warnings)
|
|
{
|
|
p18_faults_warnings_msg_t *m = (p18_faults_warnings_msg_t *)message_ptr;
|
|
int num = atoi(value);
|
|
P18_EXPECT_LISTITEM_LENGTH(index == 0 ? 2 : 1);
|
|
switch (index) {
|
|
case 0:
|
|
m->fault_code = num;
|
|
break;
|
|
case 1:
|
|
m->line_fail = num > 0;
|
|
break;
|
|
case 2:
|
|
m->output_circuit_short = num > 0;
|
|
break;
|
|
case 3:
|
|
m->inverter_over_temperature = num > 0;
|
|
break;
|
|
case 4:
|
|
m->fan_lock = num > 0;
|
|
break;
|
|
case 5:
|
|
m->battery_voltage_high = num > 0;
|
|
break;
|
|
case 6:
|
|
m->battery_low = num > 0;
|
|
break;
|
|
case 7:
|
|
m->battery_under = num > 0;
|
|
break;
|
|
case 8:
|
|
m->over_load = num > 0;
|
|
break;
|
|
case 9:
|
|
m->eeprom_fail = num > 0;
|
|
break;
|
|
case 10:
|
|
m->power_limit = num > 0;
|
|
break;
|
|
case 11:
|
|
m->pv1_voltage_high = num > 0;
|
|
break;
|
|
case 12:
|
|
m->pv2_voltage_high = num > 0;
|
|
break;
|
|
case 13:
|
|
m->mppt1_overload_warning = num > 0;
|
|
break;
|
|
case 14:
|
|
m->mppt2_overload_warning = num > 0;
|
|
break;
|
|
case 15:
|
|
m->battery_too_low_to_charge_for_scc1 = num > 0;
|
|
break;
|
|
case 16:
|
|
m->battery_too_low_to_charge_for_scc2 = num > 0;
|
|
break;
|
|
}
|
|
}
|
|
P18_UNPACK_FN(faults_warnings)
|
|
{
|
|
P18_PARSE_LIST_AND_RETURN(faults_warnings, 17)
|
|
}
|
|
|
|
P18_PARSE_CB_FN(flags_statuses)
|
|
{
|
|
p18_flags_statuses_msg_t *m = (p18_flags_statuses_msg_t *)message_ptr;
|
|
int num = atoi(value);
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
switch (index) {
|
|
case 0:
|
|
m->buzzer = num > 0;
|
|
break;
|
|
case 1:
|
|
m->overload_bypass = num > 0;
|
|
break;
|
|
case 2:
|
|
m->lcd_escape_to_default_page_after_1min_timeout = num > 0;
|
|
break;
|
|
case 3:
|
|
m->overload_restart = num > 0;
|
|
break;
|
|
case 4:
|
|
m->over_temp_restart = num > 0;
|
|
break;
|
|
case 5:
|
|
m->backlight_on = num > 0;
|
|
break;
|
|
case 6:
|
|
m->alarm_on_primary_source_interrupt = num > 0;
|
|
break;
|
|
case 7:
|
|
m->fault_code_record = num > 0;
|
|
break;
|
|
}
|
|
}
|
|
P18_UNPACK_FN(flags_statuses)
|
|
{
|
|
P18_PARSE_LIST_AND_RETURN(flags_statuses, 9)
|
|
}
|
|
|
|
P18_PARSE_CB_FN(defaults)
|
|
{
|
|
p18_defaults_msg_t *m = (p18_defaults_msg_t *)message_ptr;
|
|
int num = atoi(value);
|
|
switch (index) {
|
|
case 0: /* AAAA */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->ac_output_voltage = num;
|
|
break;
|
|
case 1: /* BBB */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->ac_output_freq = num;
|
|
break;
|
|
case 2: /* C */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->ac_input_voltage_range = num;
|
|
break;
|
|
case 3: /* DDD */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->battery_under_voltage = num;
|
|
break;
|
|
case 4: /* EEE */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->charging_float_voltage = num;
|
|
break;
|
|
case 5: /* FFF */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->charging_bulk_voltage = num;
|
|
break;
|
|
case 6: /* GGG */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->battery_recharge_voltage = num;
|
|
break;
|
|
case 7: /* HHH */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->battery_redischarge_voltage = num;
|
|
break;
|
|
case 8: /* III */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->max_charging_current = num;
|
|
break;
|
|
case 9: /* JJ */
|
|
P18_EXPECT_LISTITEM_LENGTH(2);
|
|
m->max_ac_charging_current = num;
|
|
break;
|
|
case 10: /* K */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->battery_type = num;
|
|
break;
|
|
case 11: /* L */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->output_source_priority = num;
|
|
break;
|
|
case 12: /* M */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->charger_source_priority = num;
|
|
break;
|
|
case 13: /* N */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->solar_power_priority = num;
|
|
break;
|
|
case 14: /* O */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->machine_type = num;
|
|
break;
|
|
case 15: /* P */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->output_model_setting = num;
|
|
break;
|
|
case 16: /* S */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->flag_buzzer = num > 0;
|
|
break;
|
|
case 17: /* T */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->flag_overload_restart = num > 0;
|
|
break;
|
|
case 18: /* U */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->flag_over_temp_restart = num > 0;
|
|
break;
|
|
case 19: /* V */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->flag_backlight_on = num > 0;
|
|
break;
|
|
case 20: /* W */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->flag_alarm_on_primary_source_interrupt = num > 0;
|
|
break;
|
|
case 21: /* X */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->flag_fault_code_record = num > 0;
|
|
break;
|
|
case 22: /* Y */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->flag_overload_bypass = num > 0;
|
|
break;
|
|
case 23: /* Z */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->flag_lcd_escape_to_default_page_after_1min_timeout = num > 0;
|
|
break;
|
|
}
|
|
}
|
|
P18_UNPACK_FN(defaults)
|
|
{
|
|
P18_PARSE_LIST_AND_RETURN(defaults, 24)
|
|
}
|
|
|
|
P18_PARSE_CB_FN(max_charging_current_selectable_values)
|
|
{
|
|
UNUSED(value_len);
|
|
p18_max_charging_current_selectable_values_msg_t *m =
|
|
(p18_max_charging_current_selectable_values_msg_t *)message_ptr;
|
|
if (m->len >= ARRAY_SIZE(m->amps))
|
|
ERROR("warning: %dth item, ignoring as we accepting up to %zu values\n",
|
|
index, ARRAY_SIZE(m->amps));
|
|
else
|
|
m->amps[m->len++] = atoi(value);
|
|
}
|
|
P18_UNPACK_FN(max_charging_current_selectable_values)
|
|
{
|
|
P18_PARSE_LIST_AND_RETURN(max_charging_current_selectable_values, -1)
|
|
}
|
|
|
|
P18_PARSE_CB_FN(max_ac_charging_current_selectable_values)
|
|
{
|
|
UNUSED(value_len);
|
|
p18_max_ac_charging_current_selectable_values_msg_t *m =
|
|
(p18_max_ac_charging_current_selectable_values_msg_t *)message_ptr;
|
|
if (m->len >= ARRAY_SIZE(m->amps))
|
|
ERROR("warning: %dth item, ignoring as we accepting up to %zu values\n",
|
|
index, ARRAY_SIZE(m->amps));
|
|
else
|
|
m->amps[m->len++] = atoi(value);
|
|
}
|
|
P18_UNPACK_FN(max_ac_charging_current_selectable_values)
|
|
{
|
|
P18_PARSE_LIST_AND_RETURN(max_ac_charging_current_selectable_values, -1)
|
|
}
|
|
|
|
P18_PARSE_CB_FN(parallel_rated_information)
|
|
{
|
|
p18_parallel_rated_information_msg_t *m = (p18_parallel_rated_information_msg_t *)message_ptr;
|
|
switch (index) {
|
|
case 0: /* A */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->parallel_id_connection_status = atoi(value);
|
|
break;
|
|
case 1: /* BB */
|
|
P18_EXPECT_LISTITEM_LENGTH(2);
|
|
m->serial_number_valid_length = atoi(value);
|
|
break;
|
|
case 2: /* CCCCCCCCCCCCCCCCCCCC */
|
|
P18_EXPECT_LISTITEM_LENGTH(20);
|
|
substr_copy(m->serial_number, value, MIN(m->serial_number_valid_length, (int)ARRAY_SIZE(m->serial_number)-1));
|
|
break;
|
|
case 3: /* D */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->charger_source_priority = atoi(value);
|
|
break;
|
|
case 4: /* EEE */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->max_charging_current = atoi(value);
|
|
break;
|
|
case 5: /* FF */
|
|
P18_EXPECT_LISTITEM_LENGTH(2);
|
|
m->max_ac_charging_current = atoi(value);
|
|
break;
|
|
case 6: /* G */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->output_model_setting = atoi(value);
|
|
break;
|
|
}
|
|
}
|
|
P18_UNPACK_FN(parallel_rated_information)
|
|
{
|
|
P18_PARSE_LIST_AND_RETURN(parallel_rated_information, 7)
|
|
}
|
|
|
|
P18_PARSE_CB_FN(parallel_general_status)
|
|
{
|
|
p18_parallel_general_status_msg_t *m = (p18_parallel_general_status_msg_t *)message_ptr;
|
|
int num = atoi(value);
|
|
switch (index) {
|
|
case 0: /* A */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->parallel_id_connection_status = num;
|
|
break;
|
|
case 1: /* B */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->work_mode = num;
|
|
break;
|
|
case 2: /* CC */
|
|
P18_EXPECT_LISTITEM_LENGTH(2);
|
|
m->fault_code = num;
|
|
break;
|
|
case 3: /* DDDD */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->grid_voltage = num;
|
|
break;
|
|
case 4: /* EEE */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->grid_freq = num;
|
|
break;
|
|
case 5: /* FFFF */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->ac_output_voltage = num;
|
|
break;
|
|
case 6: /* GGG */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->ac_output_freq = num;
|
|
break;
|
|
case 7: /* HHHH */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->ac_output_apparent_power = num;
|
|
break;
|
|
case 8: /* IIII */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->ac_output_active_power = num;
|
|
break;
|
|
case 9: /* JJJJJ */
|
|
P18_EXPECT_LISTITEM_LENGTH(5);
|
|
m->total_ac_output_apparent_power = num;
|
|
break;
|
|
case 10: /* KKKKK */
|
|
P18_EXPECT_LISTITEM_LENGTH(5);
|
|
m->total_ac_output_active_power = num;
|
|
break;
|
|
case 11: /* LLL */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->output_load_percent = num;
|
|
break;
|
|
case 12: /* MMM */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->total_output_load_percent = num;
|
|
break;
|
|
case 13: /* NNN */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->battery_voltage = num;
|
|
break;
|
|
case 14: /* OOO */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->battery_discharge_current = num;
|
|
break;
|
|
case 15: /* PPP */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->battery_charging_current = num;
|
|
break;
|
|
case 16: /* QQQ */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->total_battery_charging_current = num;
|
|
break;
|
|
case 17: /* MMM. It's not my mistake, it's as per the doc. */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->battery_capacity = num;
|
|
break;
|
|
case 18: /* RRRR */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->pv1_input_power = num;
|
|
break;
|
|
case 19: /* SSSS */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->pv2_input_power = num;
|
|
break;
|
|
case 20: /* TTTT */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->pv1_input_voltage = num == 1;
|
|
break;
|
|
case 21: /* UUUU */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
m->pv2_input_voltage = num;
|
|
break;
|
|
case 22: /* V */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->mppt1_charger_status = num;
|
|
break;
|
|
case 23: /* W */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->mppt2_charger_status = num;
|
|
break;
|
|
case 24: /* X */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->load_connected = num == 1;
|
|
break;
|
|
case 25: /* Y */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->battery_power_direction = num;
|
|
break;
|
|
case 26: /* Z */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->dc_ac_power_direction = num;
|
|
break;
|
|
case 27: /* a */
|
|
P18_EXPECT_LISTITEM_LENGTH(1);
|
|
m->line_power_direction = num;
|
|
break;
|
|
case 28: /* bbb. It's marked in red in the doc, idk what it means. */
|
|
/* My guess is that this elem is not always present. */
|
|
P18_EXPECT_LISTITEM_LENGTH(3);
|
|
m->max_temp = num;
|
|
break;
|
|
}
|
|
}
|
|
P18_UNPACK_FN(parallel_general_status)
|
|
{
|
|
P18_PARSE_LIST_AND_RETURN(parallel_general_status, 29)
|
|
}
|
|
|
|
P18_PARSE_CB_FN(ac_charge_time_bucket)
|
|
{
|
|
p18_ac_charge_time_bucket_msg_t *m = (p18_ac_charge_time_bucket_msg_t *)message_ptr;
|
|
char buf[4];
|
|
switch (index) {
|
|
case 0: /* AAAA */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
|
|
substr_copy(buf, value, 2);
|
|
m->start_h = atoi(buf);
|
|
|
|
substr_copy(buf, value+2, 2);
|
|
m->start_m = atoi(buf);
|
|
|
|
break;
|
|
|
|
case 1: /* BBBB */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
|
|
substr_copy(buf, value, 2);
|
|
m->end_h = atoi(buf);
|
|
|
|
substr_copy(buf, value+2, 2);
|
|
m->end_m = atoi(buf);
|
|
|
|
break;
|
|
}
|
|
}
|
|
P18_UNPACK_FN(ac_charge_time_bucket)
|
|
{
|
|
P18_PARSE_LIST_AND_RETURN(ac_charge_time_bucket, 2)
|
|
}
|
|
|
|
P18_PARSE_CB_FN(ac_supply_load_time_bucket)
|
|
{
|
|
p18_ac_supply_load_time_bucket_msg_t *m = (p18_ac_supply_load_time_bucket_msg_t *)message_ptr;
|
|
char buf[4];
|
|
switch (index) {
|
|
case 0: /* AAAA */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
|
|
substr_copy(buf, value, 2);
|
|
m->start_h = atoi(buf);
|
|
|
|
substr_copy(buf, value+2, 2);
|
|
m->start_m = atoi(buf);
|
|
|
|
break;
|
|
|
|
case 1: /* BBBB */
|
|
P18_EXPECT_LISTITEM_LENGTH(4);
|
|
|
|
substr_copy(buf, value, 2);
|
|
m->end_h = atoi(buf);
|
|
|
|
substr_copy(buf, value+2, 2);
|
|
m->end_m = atoi(buf);
|
|
|
|
break;
|
|
}
|
|
}
|
|
P18_UNPACK_FN(ac_supply_load_time_bucket)
|
|
{
|
|
P18_PARSE_LIST_AND_RETURN(ac_supply_load_time_bucket, 2)
|
|
}
|
|
|
|
/* ------------------------------------------ */
|
|
/* Label getters */
|
|
|
|
const char *p18_battery_type_label(p18_battery_type_t type)
|
|
{
|
|
switch (type) {
|
|
case P18_BT_AGM:
|
|
case P18_BT_USER:
|
|
case P18_BT_FLOODED:
|
|
return p18_battery_types[type];
|
|
|
|
default:
|
|
return p18_unknown;
|
|
}
|
|
}
|
|
|
|
const char *p18_input_voltage_range_label(p18_input_voltage_range_t range)
|
|
{
|
|
switch (range) {
|
|
case P18_IVR_APPLIANCE:
|
|
case P18_IVR_UPS:
|
|
return p18_voltage_ranges[range];
|
|
|
|
default:
|
|
return p18_unknown;
|
|
}
|
|
}
|
|
|
|
const char *p18_output_source_priority_label(p18_output_source_priority_t priority)
|
|
{
|
|
switch (priority) {
|
|
case P18_OSP_SOLAR_BATTERY_UTILITY:
|
|
case P18_OSP_SOLAR_UTILITY_BATTERY:
|
|
return p18_output_source_priorities[priority];
|
|
|
|
default:
|
|
return p18_unknown;
|
|
}
|
|
}
|
|
|
|
const char *p18_charge_source_priority_label(p18_charger_source_priority_t priority)
|
|
{
|
|
switch (priority) {
|
|
case P18_CSP_SOLAR_FIRST:
|
|
case P18_CSP_SOLAR_AND_UTILITY:
|
|
case P18_CSP_SOLAR_ONLY:
|
|
return p18_charger_source_priorities[priority];
|
|
|
|
default:
|
|
return p18_unknown;
|
|
}
|
|
}
|
|
|
|
const char *p18_machine_type_label(p18_machine_type_t type)
|
|
{
|
|
switch (type) {
|
|
case P18_MT_GRID_TIE:
|
|
case P18_MT_OFF_GRID_TIE:
|
|
return p18_machine_types[type];
|
|
|
|
default:
|
|
return p18_unknown;
|
|
}
|
|
}
|
|
|
|
const char *p18_topology_label(p18_topology_t topology)
|
|
{
|
|
switch (topology) {
|
|
case P18_TRANSFORMER:
|
|
case P18_TRANSFORMERLESS:
|
|
return p18_topologies[topology];
|
|
|
|
default:
|
|
return p18_unknown;
|
|
}
|
|
}
|
|
|
|
const char *p18_output_model_setting_label(p18_output_model_setting_t setting)
|
|
{
|
|
switch (setting) {
|
|
case P18_OMS_PARALLEL_OUTPUT:
|
|
case P18_OMS_PHASE1_OF_3PHASE_OUTPUT:
|
|
case P18_OMS_PHASE2_OF_3PHASE_OUTPUT:
|
|
case P18_OMS_PHASE3_OF_3PHASE_OUTPUT:
|
|
case P18_OMS_SINGLE_MODULE:
|
|
return p18_output_model_settings[setting];
|
|
|
|
default:
|
|
return p18_unknown;
|
|
}
|
|
}
|
|
|
|
const char *p18_solar_power_priority_label(p18_solar_power_priority_t priority)
|
|
{
|
|
switch (priority) {
|
|
case P18_SPP_BATTERY_LOAD_UTILITY:
|
|
case P18_SPP_LOAD_BATTERY_UTILITY:
|
|
return p18_solar_power_priorities[priority];
|
|
|
|
default:
|
|
return p18_unknown;
|
|
}
|
|
}
|
|
|
|
const char *p18_mppt_charger_status_label(p18_mppt_charger_status_t status)
|
|
{
|
|
switch (status) {
|
|
case P18_MPPT_CS_ABNORMAL:
|
|
case P18_MPPT_CS_CHARGED:
|
|
case P18_MPPT_CS_NOT_CHARGED:
|
|
return p18_mppt_charger_statuses[status];
|
|
|
|
default:
|
|
return p18_unknown;
|
|
}
|
|
}
|
|
|
|
const char *p18_battery_power_direction_label(p18_battery_power_direction_t direction)
|
|
{
|
|
switch (direction) {
|
|
case P18_BPD_CHARGE:
|
|
case P18_BPD_DISCHARGE:
|
|
case P18_BPD_DONOTHING:
|
|
return p18_battery_power_directions[direction];
|
|
|
|
default:
|
|
return p18_unknown;
|
|
}
|
|
}
|
|
|
|
const char *p18_dc_ac_power_direction_label(p18_dc_ac_power_direction_t direction)
|
|
{
|
|
switch (direction) {
|
|
case P18_DAPD_AC_DC:
|
|
case P18_DAPD_DC_AC:
|
|
case P18_DAPD_DONOTHING:
|
|
return p18_dc_ac_power_directions[direction];
|
|
|
|
default:
|
|
return p18_unknown;
|
|
}
|
|
}
|
|
|
|
const char *p18_line_power_direction_label(p18_line_power_direction_t direction)
|
|
{
|
|
switch (direction) {
|
|
case P18_LPD_DONOTHING:
|
|
case P18_LPD_INPUT:
|
|
case P18_LPD_OUTPUT:
|
|
return p18_line_power_directions[direction];
|
|
|
|
default:
|
|
return p18_unknown;
|
|
}
|
|
}
|
|
|
|
const char *p18_working_mode_label(p18_working_mode_t mode)
|
|
{
|
|
switch (mode) {
|
|
case P18_WM_BATTERY_MODE:
|
|
case P18_WM_BYPASS_MODE:
|
|
case P18_WM_FAULT_MODE:
|
|
case P18_WM_HYBRID_MODE:
|
|
case P18_WM_POWER_ON_MODE:
|
|
case P18_WM_STANDBY_MODE:
|
|
return p18_working_modes[mode];
|
|
|
|
default:
|
|
return p18_unknown;
|
|
}
|
|
}
|
|
|
|
const char *p18_fault_code_label(unsigned int code)
|
|
{
|
|
int len = (int)ARRAY_SIZE(fault_codes);
|
|
for (int i = 0; i < len; i++) {
|
|
const p18_fault_code_list_item_t *fc = &fault_codes[i];
|
|
if (fc->id == code)
|
|
return fc->message;
|
|
}
|
|
return p18_unknown;
|
|
}
|
|
|
|
const char *p18_parallel_connection_status_label(p18_parallel_id_connection_status_t status)
|
|
{
|
|
switch (status) {
|
|
case P18_PCS_NON_EXISTENT:
|
|
case P18_PCS_EXISTENT:
|
|
return p18_parallel_connection_statuses[status];
|
|
|
|
default:
|
|
return p18_unknown;
|
|
}
|
|
} |