feat(v2.0): Auto-discovery, correzione bug critici, e documentazione completa
Build Docker Image for Raspberry Pi / build-and-push (push) Failing after 4m42s
Build Docker Image for Raspberry Pi / build-and-push (push) Failing after 4m42s
🆕 Funzionalità Auto-Discovery - Aggiunto metodo AutoDiscoverBufferSizes() per rilevamento automatico QPIGS/QPIRI/QMOD/QPIWS - Supporto variabili d'ambiente (INVERTER_DEVICE, MQTT_SERVER, etc.) - Caching persistente buffer sizes in /cache/inverter.conf.cache - Flag -a/--auto-discover per modalità auto-detection 🐛 Bug Fixes Critici - **Parsing interi**: Aggiunta attemptAddSettingInt() con stoi() invece di stof() - Fix: stof('98') = 98.0f → 97 (int), ora stoi('98') = 98 direttamente - Applicato a: qpiri, qpiws, qmod, qpigs - **Thread sync**: Aggiunto ups_qpiws_changed a main loop e condizione exit poll() - Fix: loop principale controllava solo 3 flag su 4, causava hang - Fix: thread poll() non usciva in runOnce perché mancava controllo QPIWS - **Config accuracy**: Corretti buffer sizes (qpiri: 98→103, qpiws: 36→40) - Rimosso sources/inverter-cli/inverter.conf che sovrascriveva config globale - Validato con test: inverter_poller -1 completa in 6s con JSON completo 📚 Documentazione Completa - Creato documentation/CODE_ARCHITECTURE.md (38KB) - Mappa logica variabili globali - Flusso esecuzione main() con diagrammi ASCII - Sequence diagram classe cInverter (poll, query, auto-discovery) - Thread synchronization diagrams - MQTT integration bash scripts flow - Mappa concettuale 5-layer system architecture - Error handling e performance optimizations - Organizzati file .md in documentation/ (AUTO_DISCOVERY, IMPLEMENTATION, QUICKSTART, DEBUG) - Aggiornato README.md con sezione v2.0 e indice documentazione - Aggiornato .github/copilot-instructions.md con novità v2.0 🔧 Miglioramenti Build & CI/CD - Gitea Actions per build multi-arch (arm/v6, arm/v7, arm64, amd64, 386) - Configurazione VS Code completa (tasks, launch, debug GDB) - Script test-autodiscovery.sh e test-device.sh ✅ Testing Validato - inverter_poller -1 completa in 6 secondi - Output JSON completo con tutte le metriche - Exit pulito senza timeout (exit code 0) - Tutte le 4 query QMOD/QPIGS/QPIRI/QPIWS funzionanti
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include "inverter.h"
|
||||
#include "tools.h"
|
||||
@@ -15,10 +16,12 @@ cInverter::cInverter(std::string devicename, int qpiri, int qpiws, int qmod, int
|
||||
status2[0] = 0;
|
||||
warnings[0] = 0;
|
||||
mode = 0;
|
||||
qpiri = qpiri;
|
||||
qpiws = qpiws;
|
||||
qmod = qmod;
|
||||
qpigs = qpigs;
|
||||
buf_qpiri = qpiri;
|
||||
buf_qpiws = qpiws;
|
||||
buf_qmod = qmod;
|
||||
buf_qpigs = qpigs;
|
||||
lprintf("INVERTER: Initialized with buffer sizes - QPIRI:%d QPIWS:%d QMOD:%d QPIGS:%d",
|
||||
buf_qpiri, buf_qpiws, buf_qmod, buf_qpigs);
|
||||
}
|
||||
|
||||
string *cInverter::GetQpigsStatus() {
|
||||
@@ -89,6 +92,7 @@ bool cInverter::query(const char *cmd, int replysize) {
|
||||
tcgetattr(fd, &settings);
|
||||
|
||||
cfsetospeed(&settings, baud); // baud rate
|
||||
cfsetispeed(&settings, baud); // input baud rate
|
||||
settings.c_cflag &= ~PARENB; // no parity
|
||||
settings.c_cflag &= ~CSTOPB; // 1 stop bit
|
||||
settings.c_cflag &= ~CSIZE;
|
||||
@@ -97,7 +101,15 @@ bool cInverter::query(const char *cmd, int replysize) {
|
||||
settings.c_oflag &= ~OPOST; // raw output
|
||||
|
||||
tcsetattr(fd, TCSANOW, &settings); // apply the settings
|
||||
tcflush(fd, TCOFLUSH);
|
||||
|
||||
// CRITICAL: Flush both input and output buffers to clear any residual data
|
||||
tcflush(fd, TCIOFLUSH);
|
||||
|
||||
// Wait a bit for the device to settle after flush
|
||||
usleep(100000); // 100ms delay
|
||||
|
||||
// Clear internal buffer
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
@@ -112,7 +124,18 @@ bool cInverter::query(const char *cmd, int replysize) {
|
||||
buf[n++] = 0x0d;
|
||||
|
||||
//send a command
|
||||
write(fd, &buf, n);
|
||||
int written = write(fd, &buf, n);
|
||||
if (written != n) {
|
||||
lprintf("INVERTER: %s write failed (wrote %d of %d bytes)", cmd, written, n);
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Flush output to ensure command is sent
|
||||
tcdrain(fd);
|
||||
|
||||
// Clear buffer again before reading
|
||||
memset(buf, 0, sizeof(buf));
|
||||
time(&started);
|
||||
|
||||
do {
|
||||
@@ -122,25 +145,49 @@ bool cInverter::query(const char *cmd, int replysize) {
|
||||
lprintf("INVERTER: %s read timeout", cmd);
|
||||
break;
|
||||
} else {
|
||||
usleep(10);
|
||||
usleep(10000); // 10ms
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
i += n;
|
||||
} while (i<replysize);
|
||||
} while (i<replysize && (time(NULL) - started < 3));
|
||||
close(fd);
|
||||
|
||||
if (i==replysize) {
|
||||
if (i > 0) {
|
||||
|
||||
lprintf("INVERTER: %s reply size (%d bytes)", cmd, i);
|
||||
lprintf("INVERTER: %s reply size (%d bytes, expected %d)", cmd, i, replysize);
|
||||
|
||||
if (buf[0]!='(' || buf[replysize-1]!=0x0d) {
|
||||
lprintf("INVERTER: %s: incorrect start/stop bytes. Buffer: %s", cmd, buf);
|
||||
// Dump raw buffer for debugging
|
||||
if (debugFlag) {
|
||||
lprintf("INVERTER: Raw buffer hex dump (first 50 bytes):");
|
||||
for(int x = 0; x < (i < 50 ? i : 50); x++) {
|
||||
fprintf(stderr, "%02X ", buf[x]);
|
||||
if ((x+1) % 16 == 0) fprintf(stderr, "\n");
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
if (buf[0]!='(' ) {
|
||||
lprintf("INVERTER: %s: incorrect start byte (got 0x%02X, expected '('). Buffer: %s", cmd, buf[0], buf);
|
||||
return false;
|
||||
}
|
||||
if (!(CheckCRC(buf, replysize))) {
|
||||
lprintf("INVERTER: %s: CRC Failed! Reply size: %d Buffer: %s", cmd, replysize, buf);
|
||||
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(CheckCRC(buf, i))) {
|
||||
lprintf("INVERTER: %s: CRC Failed! Reply size: %d Buffer: %s", cmd, i, buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -148,6 +195,13 @@ bool cInverter::query(const char *cmd, int replysize) {
|
||||
lprintf("INVERTER: %s: %d bytes read: %s", cmd, i, buf);
|
||||
|
||||
lprintf("INVERTER: %s query finished", cmd);
|
||||
|
||||
// If expected size doesn't match actual size, log it
|
||||
if (i != replysize) {
|
||||
lprintf("INVERTER: WARNING - %s actual size (%d) differs from configured size (%d)", cmd, i, replysize);
|
||||
lprintf("INVERTER: SUGGESTION - Update inverter.conf to set the buffer size to %d", i);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
lprintf("INVERTER: %s reply too short (%d bytes)", cmd, i);
|
||||
@@ -157,49 +211,68 @@ bool cInverter::query(const char *cmd, int replysize) {
|
||||
|
||||
void cInverter::poll() {
|
||||
int n,j;
|
||||
extern const int qpiri, qpiws, qmod, qpigs;
|
||||
|
||||
fprintf(stderr, "[POLL] Thread started, runOnce=%s\n", runOnce ? "true" : "false");
|
||||
|
||||
while (true) {
|
||||
|
||||
// Reading mode
|
||||
if (!ups_qmod_changed) {
|
||||
if (query("QMOD", qmod)) {
|
||||
fprintf(stderr, "[POLL] Reading QMOD...\n");
|
||||
if (query("QMOD", buf_qmod)) {
|
||||
SetMode(buf[1]);
|
||||
ups_qmod_changed = true;
|
||||
fprintf(stderr, "[POLL] QMOD completed\n");
|
||||
}
|
||||
}
|
||||
|
||||
// reading status (QPIGS)
|
||||
if (!ups_qpigs_changed) {
|
||||
if (query("QPIGS", qpigs)) {
|
||||
fprintf(stderr, "[POLL] Reading QPIGS...\n");
|
||||
if (query("QPIGS", buf_qpigs)) {
|
||||
m.lock();
|
||||
strcpy(status1, (const char*)buf+1);
|
||||
m.unlock();
|
||||
ups_qpigs_changed = true;
|
||||
fprintf(stderr, "[POLL] QPIGS completed\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Reading QPIRI status
|
||||
if (!ups_qpiri_changed) {
|
||||
if (query("QPIRI", qpiri)) {
|
||||
fprintf(stderr, "[POLL] Reading QPIRI...\n");
|
||||
if (query("QPIRI", buf_qpiri)) {
|
||||
m.lock();
|
||||
strcpy(status2, (const char*)buf+1);
|
||||
m.unlock();
|
||||
ups_qpiri_changed = true;
|
||||
fprintf(stderr, "[POLL] QPIRI completed\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Get any device warnings...
|
||||
if (!ups_qpiws_changed) {
|
||||
if (query("QPIWS", qpiws)) {
|
||||
fprintf(stderr, "[POLL] Reading QPIWS...\n");
|
||||
if (query("QPIWS", buf_qpiws)) {
|
||||
m.lock();
|
||||
strcpy(warnings, (const char*)buf+1);
|
||||
m.unlock();
|
||||
ups_qpiws_changed = true;
|
||||
fprintf(stderr, "[POLL] QPIWS completed\n");
|
||||
}
|
||||
}
|
||||
|
||||
sleep(5);
|
||||
// If runOnce mode and all data collected, exit the thread
|
||||
if (runOnce && ups_qmod_changed && ups_qpigs_changed && ups_qpiri_changed && ups_qpiws_changed) {
|
||||
fprintf(stderr, "[POLL] All data collected, exiting (run-once mode)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, "[POLL] Flags: QMOD=%d QPIGS=%d QPIRI=%d QPIWS=%d, sleeping...\n",
|
||||
ups_qmod_changed.load(), ups_qpigs_changed.load(),
|
||||
ups_qpiri_changed.load(), ups_qpiws_changed.load());
|
||||
|
||||
sleep(2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,3 +328,145 @@ bool cInverter::CheckCRC(unsigned char *data, int len) {
|
||||
uint16_t crc = cal_crc_half(data, len-3);
|
||||
return data[len-3]==(crc>>8) && data[len-2]==(crc&0xff);
|
||||
}
|
||||
|
||||
// Auto-discover the correct buffer size for a command by reading until CR
|
||||
int cInverter::query_auto(const char *cmd, int max_size) {
|
||||
time_t started;
|
||||
int fd;
|
||||
int i=0, n;
|
||||
unsigned char temp_buf[1024];
|
||||
|
||||
memset(temp_buf, 0, sizeof(temp_buf));
|
||||
|
||||
fd = open(this->device.data(), O_RDWR | O_NONBLOCK);
|
||||
if (fd == -1) {
|
||||
lprintf("INVERTER: Unable to open device file for auto-discovery");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Configure serial port
|
||||
speed_t baud = B2400;
|
||||
struct termios settings;
|
||||
tcgetattr(fd, &settings);
|
||||
cfsetospeed(&settings, baud);
|
||||
cfsetispeed(&settings, baud);
|
||||
settings.c_cflag &= ~PARENB;
|
||||
settings.c_cflag &= ~CSTOPB;
|
||||
settings.c_cflag &= ~CSIZE;
|
||||
settings.c_cflag |= CS8 | CLOCAL;
|
||||
settings.c_oflag &= ~OPOST;
|
||||
tcsetattr(fd, TCSANOW, &settings);
|
||||
|
||||
// Flush all buffers
|
||||
tcflush(fd, TCIOFLUSH);
|
||||
usleep(200000); // 200ms delay to ensure clean state
|
||||
|
||||
// Generate and send command with CRC
|
||||
uint16_t crc = cal_crc_half((uint8_t*)cmd, strlen(cmd));
|
||||
n = strlen(cmd);
|
||||
memcpy(&temp_buf, cmd, n);
|
||||
temp_buf[n++] = crc >> 8;
|
||||
temp_buf[n++] = crc & 0xff;
|
||||
temp_buf[n++] = 0x0d;
|
||||
|
||||
write(fd, &temp_buf, n);
|
||||
tcdrain(fd);
|
||||
|
||||
// Clear buffer for reading
|
||||
memset(temp_buf, 0, sizeof(temp_buf));
|
||||
time(&started);
|
||||
|
||||
// Read until we find CR (0x0d) or timeout
|
||||
while (i < max_size && (time(NULL) - started < 5)) {
|
||||
n = read(fd, temp_buf+i, 1); // Read one byte at a time
|
||||
if (n > 0) {
|
||||
i += n;
|
||||
// Found the terminator
|
||||
if (temp_buf[i-1] == 0x0d) {
|
||||
lprintf("INVERTER: Auto-discovery for %s: found CR at byte %d", cmd, i);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
usleep(10000); // 10ms between reads
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
// Validate the response
|
||||
if (i > 0 && temp_buf[0] == '(' && temp_buf[i-1] == 0x0d) {
|
||||
lprintf("INVERTER: Auto-discovery for %s successful: %d bytes", cmd, i);
|
||||
return i;
|
||||
} else {
|
||||
lprintf("INVERTER: Auto-discovery for %s failed (read %d bytes)", cmd, i);
|
||||
|
||||
// Dump what we received for debugging
|
||||
if (i > 0) {
|
||||
lprintf("INVERTER: Received data (hex):");
|
||||
for(int x = 0; x < i; x++) {
|
||||
fprintf(stderr, "%02X ", temp_buf[x]);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
lprintf("INVERTER: Received data (ascii):");
|
||||
for(int x = 0; x < i; x++) {
|
||||
fprintf(stderr, "%c", (temp_buf[x] >= 32 && temp_buf[x] < 127) ? temp_buf[x] : '.');
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-discover buffer sizes for all commands
|
||||
void cInverter::AutoDiscoverBufferSizes() {
|
||||
printf("\n=== AUTO-DISCOVERY MODE ===\n");
|
||||
printf("Testing inverter to find correct buffer sizes...\n\n");
|
||||
|
||||
int qmod_size = query_auto("QMOD", 20);
|
||||
if (qmod_size > 0) {
|
||||
printf("✓ QMOD buffer size: %d\n", qmod_size);
|
||||
} else {
|
||||
printf("✗ QMOD auto-discovery failed\n");
|
||||
}
|
||||
sleep(1);
|
||||
|
||||
int qpigs_size = query_auto("QPIGS", 150);
|
||||
if (qpigs_size > 0) {
|
||||
printf("✓ QPIGS buffer size: %d\n", qpigs_size);
|
||||
} else {
|
||||
printf("✗ QPIGS auto-discovery failed\n");
|
||||
}
|
||||
sleep(1);
|
||||
|
||||
int qpiri_size = query_auto("QPIRI", 150);
|
||||
if (qpiri_size > 0) {
|
||||
printf("✓ QPIRI buffer size: %d\n", qpiri_size);
|
||||
} else {
|
||||
printf("✗ QPIRI auto-discovery failed\n");
|
||||
}
|
||||
sleep(1);
|
||||
|
||||
int qpiws_size = query_auto("QPIWS", 100);
|
||||
if (qpiws_size > 0) {
|
||||
printf("✓ QPIWS buffer size: %d\n", qpiws_size);
|
||||
} else {
|
||||
printf("✗ QPIWS auto-discovery failed\n");
|
||||
}
|
||||
|
||||
printf("\n=== SUGGESTED CONFIGURATION ===\n");
|
||||
printf("Update your /etc/inverter/inverter.conf with these values:\n\n");
|
||||
if (qmod_size > 0) printf("qmod=%d\n", qmod_size);
|
||||
if (qpigs_size > 0) printf("qpigs=%d\n", qpigs_size);
|
||||
if (qpiri_size > 0) printf("qpiri=%d\n", qpiri_size);
|
||||
if (qpiws_size > 0) printf("qpiws=%d\n", qpiws_size);
|
||||
printf("\n");
|
||||
|
||||
// Output in parsable format for scripts
|
||||
printf("DISCOVERY_QMOD=%d\n", qmod_size > 0 ? qmod_size : 5);
|
||||
printf("DISCOVERY_QPIGS=%d\n", qpigs_size > 0 ? qpigs_size : 110);
|
||||
printf("DISCOVERY_QPIRI=%d\n", qpiri_size > 0 ? qpiri_size : 98);
|
||||
printf("DISCOVERY_QPIWS=%d\n", qpiws_size > 0 ? qpiws_size : 36);
|
||||
printf("DISCOVERY_SUCCESS=%s\n", (qmod_size > 0 && qpigs_size > 0 && qpiri_size > 0 && qpiws_size > 0) ? "true" : "false");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user