Implementazione supporto multi-inverter paralleli e fix comunicazione MQTT
Build Docker Image for Raspberry Pi / build-and-push (push) Failing after 1m15s
Build Docker Image for Raspberry Pi / build-and-push (push) Failing after 1m15s
- Aggiunto supporto lettura inverter paralleli tramite comandi QPGS0-QPGS9 - Implementato discovery automatico inverter con filtro duplicati e serial invalidi - Risolti bug critici comunicazione seriale: * Fix buffer ExecuteCmd da 7 a 200 bytes * Supporto terminatori CR e LF * Modalità blocking con delay 500ms * Lettura byte-by-byte per terminatore affidabile - Implementato script MQTT per pubblicazione dati multi-inverter: * mqtt-push-parallel.sh con topic separati per ogni inverter * Fix autenticazione MQTT con username/password * Aggiunto flag retain (-r) per persistenza dati - Creato test-loop-parallel.sh per simulazione completa container - Aggiornata documentazione con compatibilità MKS IV e guida test loop - Aggiornati profili debug VS Code per bash e parallel discovery - Configurazione MQTT completa con server reale (192.168.1.37:1883) Sistema testato e funzionante con 2 inverter Voltronic Axpert MKS IV
This commit is contained in:
@@ -76,7 +76,7 @@ bool cInverter::query(const char *cmd, int replysize) {
|
||||
int fd;
|
||||
int i=0, n;
|
||||
|
||||
fd = open(this->device.data(), O_RDWR | O_NONBLOCK);
|
||||
fd = open(this->device.data(), O_RDWR | O_NOCTTY);
|
||||
if (fd == -1) {
|
||||
lprintf("INVERTER: Unable to open device file (errno=%d %s)", errno, strerror(errno));
|
||||
sleep(5);
|
||||
@@ -134,12 +134,15 @@ bool cInverter::query(const char *cmd, int replysize) {
|
||||
// Flush output to ensure command is sent
|
||||
tcdrain(fd);
|
||||
|
||||
// Critical delay after write (like Python implementation)
|
||||
usleep(500000); // 500ms delay
|
||||
|
||||
// Clear buffer again before reading
|
||||
memset(buf, 0, sizeof(buf));
|
||||
time(&started);
|
||||
|
||||
do {
|
||||
n = read(fd, (void*)buf+i, replysize-i);
|
||||
n = read(fd, (void*)buf+i, 1); // Read one byte at a time for reliable terminator detection
|
||||
if (n < 0) {
|
||||
if (time(NULL) - started > 2) {
|
||||
lprintf("INVERTER: %s read timeout", cmd);
|
||||
@@ -152,9 +155,9 @@ bool cInverter::query(const char *cmd, int replysize) {
|
||||
|
||||
if (n > 0) {
|
||||
i += n;
|
||||
// Check if we've received the terminator
|
||||
if (i > 0 && buf[i-1] == 0x0d) {
|
||||
lprintf("INVERTER: %s received terminator at byte %d", cmd, i);
|
||||
// Check if we've received the terminator (CR or LF)
|
||||
if (i > 0 && (buf[i-1] == 0x0d || buf[i-1] == 0x0a)) {
|
||||
lprintf("INVERTER: %s received terminator (0x%02X) at byte %d", cmd, buf[i-1], i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -181,8 +184,8 @@ bool cInverter::query(const char *cmd, int replysize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (buf[i-1]!=0x0d) {
|
||||
lprintf("INVERTER: %s: incorrect stop byte (got 0x%02X at pos %d, expected CR). Buffer: %s", cmd, buf[i-1], i-1, buf);
|
||||
if (buf[i-1]!=0x0d && buf[i-1]!=0x0a) {
|
||||
lprintf("INVERTER: %s: incorrect stop byte (got 0x%02X at pos %d, expected CR or LF). Buffer: %s", cmd, buf[i-1], i-1, buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -277,8 +280,8 @@ void cInverter::poll() {
|
||||
}
|
||||
|
||||
void cInverter::ExecuteCmd(const string cmd) {
|
||||
// Sending any command raw
|
||||
if (query(cmd.data(), 7)) {
|
||||
// Sending any command raw - use larger buffer to accept full responses
|
||||
if (query(cmd.data(), 200)) {
|
||||
m.lock();
|
||||
strcpy(status2, (const char*)buf+1);
|
||||
m.unlock();
|
||||
@@ -470,3 +473,82 @@ void cInverter::AutoDiscoverBufferSizes() {
|
||||
printf("DISCOVERY_SUCCESS=%s\n", (qmod_size > 0 && qpigs_size > 0 && qpiri_size > 0 && qpiws_size > 0) ? "true" : "false");
|
||||
}
|
||||
|
||||
// Discover number of parallel inverters
|
||||
int cInverter::DiscoverParallelInverters() {
|
||||
fprintf(stderr, "\n=== PARALLEL INVERTER DISCOVERY ===\n");
|
||||
fprintf(stderr, "Checking for parallel inverter configuration...\n\n");
|
||||
|
||||
int count = 0;
|
||||
char cmd[16];
|
||||
std::string found_serials[10]; // Track unique serials
|
||||
|
||||
// Test QPGS0 through QPGS9
|
||||
for (int i = 0; i < 10; i++) {
|
||||
snprintf(cmd, sizeof(cmd), "QPGS%d", i);
|
||||
|
||||
if (query(cmd, 200)) {
|
||||
// Check if response is valid (not NAK)
|
||||
if (buf[0] == '(' && buf[1] != 'N') {
|
||||
// Extract serial number (starts at position 3)
|
||||
char serial[20] = {0};
|
||||
int j = 0;
|
||||
for (int k = 3; k < 17 && buf[k] != ' '; k++) {
|
||||
serial[j++] = buf[k];
|
||||
}
|
||||
|
||||
// Check if serial is valid (not all zeros and not empty)
|
||||
bool valid_serial = false;
|
||||
for (int k = 0; k < j; k++) {
|
||||
if (serial[k] != '0') {
|
||||
valid_serial = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if serial is duplicate
|
||||
bool duplicate = false;
|
||||
std::string serial_str(serial);
|
||||
for (int k = 0; k < count; k++) {
|
||||
if (found_serials[k] == serial_str) {
|
||||
duplicate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid_serial && j > 0 && !duplicate) {
|
||||
found_serials[count] = serial_str;
|
||||
count++;
|
||||
fprintf(stderr, "✓ Inverter #%d via %s (Serial: %s)\n", count, cmd, serial);
|
||||
printf("INVERTER_%d_SERIAL=%s\n", count, serial);
|
||||
printf("INVERTER_%d_QPGS=%d\n", count, i);
|
||||
} else if (duplicate) {
|
||||
fprintf(stderr, "⊗ Skipping %s (Duplicate serial: %s)\n", cmd, serial);
|
||||
} else {
|
||||
fprintf(stderr, "⊗ Skipping %s (Invalid serial: %s)\n", cmd, serial);
|
||||
}
|
||||
}
|
||||
}
|
||||
usleep(100000); // 100ms between queries
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n=== DISCOVERY RESULT ===\n");
|
||||
fprintf(stderr, "Total unique parallel inverters: %d\n", count);
|
||||
printf("PARALLEL_COUNT=%d\n", count);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// Get parallel status for specific inverter
|
||||
string cInverter::GetParallelStatus(int inverter_num) {
|
||||
char cmd[16];
|
||||
snprintf(cmd, sizeof(cmd), "QPGS%d", inverter_num);
|
||||
|
||||
if (query(cmd, 200)) {
|
||||
if (buf[0] == '(' && buf[1] != 'N') {
|
||||
// Return data without leading '('
|
||||
return string((char*)buf + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user