Switched from CAN connection to UART for correctly fetch the datas.
This commit is contained in:
@@ -1,6 +1,4 @@
|
||||
/*
|
||||
Modello di un singolo controller MPPT Poweren Boost.
|
||||
|
||||
Ogni istanza mantiene lo stato corrente del dispositivo (tensioni, correnti,
|
||||
temperature, flag operativi, warnings) aggiornato dai frame CAN broadcast
|
||||
(MSG1 status, MSG2 power) e dalle risposte alle richieste di lettura registro.
|
||||
@@ -15,7 +13,25 @@ const {
|
||||
registerAddresses,
|
||||
} = require('./constants');
|
||||
|
||||
// Arrotondamenti convenzionali per esporre dati leggibili nei getter
|
||||
/*
|
||||
Mappa indirizzo registro -> campo di stato e fattore di conversione.
|
||||
Usata da updateRegister per convertire il valore raw a 16 bit nel valore fisico.
|
||||
I registri di flag (status1, warning) sono gestiti a parte perche' vanno decodificati a bit.
|
||||
signed=true indica un intero a 16 bit con segno (complemento a 2), usato per le temperature.
|
||||
*/
|
||||
const numericRegisterMap = {
|
||||
[registerAddresses.voltageInput]: { field: 'voltageInput', factor: conversionsFactors.voltageInput, signed: false },
|
||||
[registerAddresses.currentInput]: { field: 'currentInput', factor: conversionsFactors.currentInput, signed: false },
|
||||
[registerAddresses.voltageOutput]: { field: 'voltageOutput', factor: conversionsFactors.voltageOutput, signed: false },
|
||||
[registerAddresses.currentOutput]: { field: 'currentOutput', factor: conversionsFactors.currentOutput, signed: false },
|
||||
[registerAddresses.powerInput]: { field: 'powerInput', factor: conversionsFactors.powerInput, signed: false },
|
||||
[registerAddresses.powerOutput]: { field: 'powerOutput', factor: conversionsFactors.powerOutput, signed: false },
|
||||
[registerAddresses.chargeCapacity]: { field: 'chargeCapacity', factor: conversionsFactors.chargeCapacity, signed: false },
|
||||
[registerAddresses.temperature1]: { field: 'temperature1', factor: conversionsFactors.temperaturePhase1, signed: true },
|
||||
[registerAddresses.temperature2]: { field: 'temperature2', factor: conversionsFactors.temperaturePhase2, signed: true },
|
||||
};
|
||||
|
||||
// Arrotondamenti
|
||||
const roundedTo1 = (v) => Number(v.toFixed(1));
|
||||
const roundedTo2 = (v) => Number(v.toFixed(2));
|
||||
const roundedTo3 = (v) => Number(v.toFixed(3));
|
||||
@@ -32,27 +48,14 @@ const decodeFlags = (value, map) => {
|
||||
return active;
|
||||
};
|
||||
|
||||
// Parsing del payload broadcast: 8 byte -> 4 uint16 big-endian
|
||||
function parsePayload(data) {
|
||||
if (!data || data.length < 8) return null;
|
||||
return [
|
||||
(data[0] << 8) | data[1],
|
||||
(data[2] << 8) | data[3],
|
||||
(data[4] << 8) | data[5],
|
||||
(data[6] << 8) | data[7],
|
||||
];
|
||||
}
|
||||
|
||||
class MPPT {
|
||||
constructor({
|
||||
name,
|
||||
rxID,
|
||||
txIdBase,
|
||||
address,
|
||||
log = () => {},
|
||||
} = {}) {
|
||||
this.name = name; // identificativo logico (es. 'port', 'starboard')
|
||||
this.rxID = rxID; // indirizzo CAN del dispositivo (registro Addr)
|
||||
this.txIdBase = txIdBase; // ID base di trasmissione (broadcast su +1 e +2)
|
||||
this.address = address; // indirizzo UART/DST del dispositivo (registro Addr)
|
||||
this.log = log;
|
||||
|
||||
this.state = {
|
||||
@@ -95,51 +98,34 @@ class MPPT {
|
||||
return 'bulk';
|
||||
}
|
||||
|
||||
// Aggiorna lo stato da un frame broadcast (MSG1=status, MSG2=power)
|
||||
updateFromBroadcast(type, data) {
|
||||
const regs = parsePayload(data);
|
||||
if (!regs) return;
|
||||
|
||||
if (type === 'status') {
|
||||
const [st1, , warn, chgCap] = regs;
|
||||
this.state.status1Raw = st1;
|
||||
this.state.warningRaw = warn;
|
||||
this.state.chargeCapacity = chgCap / conversionsFactors.chargeCapacity;
|
||||
this.state.flags = decodeFlags(st1, status1Flags);
|
||||
this.state.warnings = decodeFlags(warn, errorsFlags);
|
||||
/*
|
||||
Aggiorna lo stato a partire dalla lettura UART di un singolo registro.
|
||||
regAddr = indirizzo del registro letto
|
||||
rawValue = valore raw a 16 bit ricevuto nella risposta (big-endian, gia' decodificato)
|
||||
*/
|
||||
updateRegister(regAddr, rawValue) {
|
||||
if (regAddr === registerAddresses.status1) {
|
||||
// Registro di stato St1: decodifica i bit in flag operativi
|
||||
this.state.status1Raw = rawValue;
|
||||
this.state.flags = decodeFlags(rawValue, status1Flags);
|
||||
} else if (regAddr === registerAddresses.warning) {
|
||||
// Registro Warning: decodifica i bit in codici di errore/warning
|
||||
this.state.warningRaw = rawValue;
|
||||
this.state.warnings = decodeFlags(rawValue, errorsFlags);
|
||||
if (this.state.warnings.length) {
|
||||
this.log(`[${this.name}] WARN: ${this.state.warnings.join(',')}`);
|
||||
}
|
||||
} else if (type === 'power') {
|
||||
const [vi, ii, vo, io] = regs;
|
||||
this.state.voltageInput = vi / conversionsFactors.voltageInput;
|
||||
this.state.currentInput = ii / conversionsFactors.currentInput;
|
||||
this.state.voltageOutput = vo / conversionsFactors.voltageOutput;
|
||||
this.state.currentOutput = io / conversionsFactors.currentOutput;
|
||||
} else {
|
||||
// Registri numerici (tensioni, correnti, temperature, capacita')
|
||||
const target = numericRegisterMap[regAddr];
|
||||
if (!target) return;
|
||||
let value = rawValue;
|
||||
if (target.signed && value >= 0x8000) value -= 0x10000; // intero con segno
|
||||
this.state[target.field] = value / target.factor;
|
||||
}
|
||||
this.state.lastUpdate = Date.now();
|
||||
}
|
||||
|
||||
// Aggiorna lo stato a partire da una risposta a richiesta di registro
|
||||
// Payload reply: [reg_addr, value_high, value_low]
|
||||
updateFromRegisterReply(data) {
|
||||
if (!data || data.length < 3) return;
|
||||
const regAddr = data[0];
|
||||
const rawValue = (data[1] << 8) | data[2];
|
||||
|
||||
const mapping = {
|
||||
[registerAddresses.powerInput]: { field: 'powerInput', factor: conversionsFactors.powerInput },
|
||||
[registerAddresses.powerOutput]: { field: 'powerOutput', factor: conversionsFactors.powerOutput },
|
||||
[registerAddresses.temperature1]: { field: 'temperature1', factor: conversionsFactors.temperaturePhase1 },
|
||||
[registerAddresses.temperature2]: { field: 'temperature2', factor: conversionsFactors.temperaturePhase2 },
|
||||
};
|
||||
|
||||
const target = mapping[regAddr];
|
||||
if (!target) return;
|
||||
this.state[target.field] = rawValue / target.factor;
|
||||
this.state.lastUpdate = Date.now();
|
||||
}
|
||||
|
||||
// Costruisce gli update SignalK (path + value) a partire dallo stato corrente
|
||||
// Tutti i valori sono in unita' SI (V, A, W, K, ratio, Ah)
|
||||
buildSignalKUpdates() {
|
||||
@@ -200,6 +186,5 @@ class MPPT {
|
||||
|
||||
module.exports = {
|
||||
MPPT,
|
||||
parsePayload,
|
||||
decodeFlags,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user