From d953a1a52539ce836bcb3776d7ade876f9f22041 Mon Sep 17 00:00:00 2001 From: Torsten Oltmanns Date: Sat, 13 Jan 2024 14:03:20 +0800 Subject: [PATCH] made the BMS processing more resilient by closing and reopening the port in case of too many no-bytes readings --- .../daly/common/AbstractDalyBmsProcessor.java | 60 ++++--------- .../daly/common/NoDataAvailableException.java | 11 --- .../bms/daly/rs485/DalyBmsRS485Processor.java | 62 ++++++++----- .../bms/jk/can/JKBmsCANProcessor.java | 63 ++++++-------- .../bms/pylon/can/PylonBmsCANProcessor.java | 87 +++++++++---------- .../bms/seplos/can/SeplosBmsCANProcessor.java | 87 +++++++++---------- .../bmstoinverter/BmsToInverter.java | 6 +- .../core/AbstractBMSProcessor.java | 72 +++++++++++++++ .../bmstoinverter/core/{Bms.java => BMS.java} | 4 +- .../core/EnergyStorageProducer.java | 2 +- .../core/NoDataAvailableException.java | 9 ++ .../airepublic/bmstoinverter/core/Port.java | 30 +++++++ .../core/TooManyInvalidFramesException.java | 9 ++ .../protocol/can/JavaCANPort.java | 23 +---- .../protocol/rs485/JSerialCommPort.java | 21 ----- 15 files changed, 291 insertions(+), 255 deletions(-) delete mode 100644 bms-daly-common/src/main/java/com/airepublic/bmstoinverter/bms/daly/common/NoDataAvailableException.java create mode 100644 core-api/src/main/java/com/airepublic/bmstoinverter/core/AbstractBMSProcessor.java rename core-api/src/main/java/com/airepublic/bmstoinverter/core/{Bms.java => BMS.java} (85%) create mode 100644 core-api/src/main/java/com/airepublic/bmstoinverter/core/NoDataAvailableException.java create mode 100644 core-api/src/main/java/com/airepublic/bmstoinverter/core/TooManyInvalidFramesException.java diff --git a/bms-daly-common/src/main/java/com/airepublic/bmstoinverter/bms/daly/common/AbstractDalyBmsProcessor.java b/bms-daly-common/src/main/java/com/airepublic/bmstoinverter/bms/daly/common/AbstractDalyBmsProcessor.java index ab5dcba9..3fb8db28 100644 --- a/bms-daly-common/src/main/java/com/airepublic/bmstoinverter/bms/daly/common/AbstractDalyBmsProcessor.java +++ b/bms-daly-common/src/main/java/com/airepublic/bmstoinverter/bms/daly/common/AbstractDalyBmsProcessor.java @@ -13,8 +13,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.airepublic.bmstoinverter.core.Bms; +import com.airepublic.bmstoinverter.core.AbstractBMSProcessor; +import com.airepublic.bmstoinverter.core.BMS; +import com.airepublic.bmstoinverter.core.NoDataAvailableException; import com.airepublic.bmstoinverter.core.Port; +import com.airepublic.bmstoinverter.core.TooManyInvalidFramesException; import com.airepublic.bmstoinverter.core.bms.data.BatteryPack; import com.airepublic.bmstoinverter.core.bms.data.EnergyStorage; import com.airepublic.bmstoinverter.core.protocol.can.CAN; @@ -23,10 +26,10 @@ import jakarta.inject.Inject; /** - * An abstraction for the Daly {@link Bms} since the {@link RS485} and {@link CAN} communication is + * An abstraction for the Daly {@link BMS} since the {@link RS485} and {@link CAN} communication is * very similar. */ -public abstract class AbstractDalyBmsProcessor implements Bms { +public abstract class AbstractDalyBmsProcessor extends AbstractBMSProcessor implements BMS { private final static Logger LOG = LoggerFactory.getLogger(AbstractDalyBmsProcessor.class); @Inject private EnergyStorage energyStorage; @@ -65,36 +68,18 @@ public void initialize() { @Override - public void process(final Runnable callback) { - try { - LOG.info("---------------------------------> Thread " + Thread.currentThread().getId()); - clearBuffers(); - - for (int bmsNo = 0; bmsNo < energyStorage.getBatteryPackCount(); bmsNo++) { - sendMessage(bmsNo, DalyCommand.READ_VOUT_IOUT_SOC, requestData); // 0x90 - sendMessage(bmsNo, DalyCommand.READ_MIN_MAX_CELL_VOLTAGE, requestData); // 0x91 - sendMessage(bmsNo, DalyCommand.READ_MIN_MAX_TEMPERATURE, requestData); // 0x92 - sendMessage(bmsNo, DalyCommand.READ_DISCHARGE_CHARGE_MOS_STATUS, requestData); // 0x93 - sendMessage(bmsNo, DalyCommand.READ_STATUS_INFO, requestData); // 0x94 - sendMessage(bmsNo, DalyCommand.READ_CELL_VOLTAGES, requestData); // 0x95 - sendMessage(bmsNo, DalyCommand.READ_CELL_TEMPERATURE, requestData); // 0x96 - sendMessage(bmsNo, DalyCommand.READ_CELL_BALANCE_STATE, requestData); // 0x97 - sendMessage(bmsNo, DalyCommand.READ_FAILURE_CODES, requestData); // 0x98 - } - - // autoCalibrateSOC(); - } catch (final NoDataAvailableException e) { - return; - } catch (final Throwable e) { - LOG.error("Error requesting data!", e); - return; - } + public void collectData(final int bmsNo) throws IOException, TooManyInvalidFramesException, NoDataAvailableException { + + sendMessage(bmsNo, DalyCommand.READ_VOUT_IOUT_SOC, requestData); // 0x90 + sendMessage(bmsNo, DalyCommand.READ_MIN_MAX_CELL_VOLTAGE, requestData); // 0x91 + sendMessage(bmsNo, DalyCommand.READ_MIN_MAX_TEMPERATURE, requestData); // 0x92 + sendMessage(bmsNo, DalyCommand.READ_DISCHARGE_CHARGE_MOS_STATUS, requestData); // 0x93 + sendMessage(bmsNo, DalyCommand.READ_STATUS_INFO, requestData); // 0x94 + sendMessage(bmsNo, DalyCommand.READ_CELL_VOLTAGES, requestData); // 0x95 + sendMessage(bmsNo, DalyCommand.READ_CELL_TEMPERATURE, requestData); // 0x96 + sendMessage(bmsNo, DalyCommand.READ_CELL_BALANCE_STATE, requestData); // 0x97 + sendMessage(bmsNo, DalyCommand.READ_FAILURE_CODES, requestData); // 0x98 - try { - callback.run(); - } catch (final Throwable e) { - LOG.error("BMS process callback threw an exception!", e); - } } @@ -153,7 +138,7 @@ protected void autoCalibrateSOC() { * @return * @throws IOException */ - protected abstract List sendMessage(final int bmsNo, final DalyCommand cmd, final byte[] data) throws IOException; + protected abstract List sendMessage(final int bmsNo, final DalyCommand cmd, final byte[] data) throws IOException, NoDataAvailableException, TooManyInvalidFramesException; /** @@ -195,13 +180,4 @@ protected int getResponseFrameCount(final DalyCommand cmd) { */ protected abstract DalyMessage convertReceiveFrameToDalyMessage(final ByteBuffer buffer); - - /** - * Clears any buffers or queues on all associated ports to restart communication. - */ - protected void clearBuffers() { - for (final BatteryPack pack : energyStorage.getBatteryPacks()) { - pack.port.clearBuffers(); - } - } } diff --git a/bms-daly-common/src/main/java/com/airepublic/bmstoinverter/bms/daly/common/NoDataAvailableException.java b/bms-daly-common/src/main/java/com/airepublic/bmstoinverter/bms/daly/common/NoDataAvailableException.java deleted file mode 100644 index 6859206b..00000000 --- a/bms-daly-common/src/main/java/com/airepublic/bmstoinverter/bms/daly/common/NoDataAvailableException.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.airepublic.bmstoinverter.bms.daly.common; - -import com.airepublic.bmstoinverter.core.Port; - -/** - * An {@link Exception} thrown when there is no data available to read from the {@link Port}. - */ -public class NoDataAvailableException extends RuntimeException { - private static final long serialVersionUID = 1L; - -} diff --git a/bms-daly-rs485/src/main/java/com/airepublic/bmstoinverter/bms/daly/rs485/DalyBmsRS485Processor.java b/bms-daly-rs485/src/main/java/com/airepublic/bmstoinverter/bms/daly/rs485/DalyBmsRS485Processor.java index a31332e5..6b97fdb8 100644 --- a/bms-daly-rs485/src/main/java/com/airepublic/bmstoinverter/bms/daly/rs485/DalyBmsRS485Processor.java +++ b/bms-daly-rs485/src/main/java/com/airepublic/bmstoinverter/bms/daly/rs485/DalyBmsRS485Processor.java @@ -13,10 +13,11 @@ import com.airepublic.bmstoinverter.bms.daly.common.AbstractDalyBmsProcessor; import com.airepublic.bmstoinverter.bms.daly.common.DalyCommand; import com.airepublic.bmstoinverter.bms.daly.common.DalyMessage; -import com.airepublic.bmstoinverter.bms.daly.common.NoDataAvailableException; +import com.airepublic.bmstoinverter.core.NoDataAvailableException; import com.airepublic.bmstoinverter.core.Port; import com.airepublic.bmstoinverter.core.PortType; import com.airepublic.bmstoinverter.core.Protocol; +import com.airepublic.bmstoinverter.core.TooManyInvalidFramesException; import com.airepublic.bmstoinverter.core.bms.data.EnergyStorage; import com.airepublic.bmstoinverter.core.protocol.rs485.RS485Port; @@ -41,7 +42,7 @@ public class DalyBmsRS485Processor extends AbstractDalyBmsProcessor { }; @Override - protected List sendMessage(final int bmsNo, final DalyCommand cmd, final byte[] data) throws IOException { + protected List sendMessage(final int bmsNo, final DalyCommand cmd, final byte[] data) throws IOException, TooManyInvalidFramesException, NoDataAvailableException { final int address = bmsNo + 0x40; final ByteBuffer sendBuffer = prepareSendFrame(address, cmd, data); int framesToBeReceived = getResponseFrameCount(cmd); @@ -49,27 +50,41 @@ protected List sendMessage(final int bmsNo, final DalyCommand cmd, f final List readBuffers = new ArrayList<>(); final RS485Port port = (RS485Port) energyStorage.getBatteryPack(bmsNo).port; int failureCount = 0; + int noDataReceived = 0; // read frames until the requested frame is read do { + + // send the request command frame port.sendFrame(sendBuffer); LOG.debug("SEND: {}", Port.printBuffer(sendBuffer)); + // read the expected response frame(s) for (int i = 0; i < frameCount; i++) { final ByteBuffer receiveBuffer = port.receiveFrame(validator); + // check if a valid frame was received or no bytes if (receiveBuffer == null || receiveBuffer.capacity() < port.getFrameLength()) { - failureCount++; + // did we receive an invalid frame length if (receiveBuffer != null && receiveBuffer.capacity() < port.getFrameLength()) { + // keep track of how often invalid frames were received + failureCount++; LOG.debug("Wrong number of bytes received! {}", Port.printBuffer(receiveBuffer)); - } else { - LOG.warn("No bytes received: " + failureCount + " times!"); - } - if (failureCount >= 10) { - LOG.error("Received wrong number of bytes too many times - start new reading round!"); - throw new NoDataAvailableException(); + if (failureCount >= 10) { + throw new TooManyInvalidFramesException(); + } + } else { // we received no bytes at all + // keep track of how often no bytes could be read + noDataReceived++; + LOG.warn("No bytes received: " + noDataReceived + " times!"); + + // if we received no bytes more than 10 times we stop and notify the handler + // to re-open the port + if (noDataReceived >= 10) { + throw new NoDataAvailableException(); + } } // try and wait for the next message to arrive @@ -79,25 +94,24 @@ protected List sendMessage(final int bmsNo, final DalyCommand cmd, f } catch (final InterruptedException e) { } + // try to receive the response again i--; + } else { + LOG.debug("RECEIVED: {}", Port.printBuffer(receiveBuffer)); - continue; - } - - LOG.debug("RECEIVED: {}", Port.printBuffer(receiveBuffer)); - - if (receiveBuffer.get(1) == (byte) (address - 0x40 + 1) && receiveBuffer.get(2) == (byte) cmd.id) { - framesToBeReceived--; - readBuffers.add(receiveBuffer); - } + if (receiveBuffer.get(1) == (byte) (address - 0x40 + 1) && receiveBuffer.get(2) == (byte) cmd.id) { + framesToBeReceived--; + readBuffers.add(receiveBuffer); + } - final DalyMessage dalyMsg = convertReceiveFrameToDalyMessage(receiveBuffer); + final DalyMessage dalyMsg = convertReceiveFrameToDalyMessage(receiveBuffer); - if (dalyMsg != null) { - getMessageHandler().handleMessage(dalyMsg); - } else { - LOG.warn("Message could not be interpreted " + Port.printBuffer(receiveBuffer)); - return readBuffers; + if (dalyMsg != null) { + getMessageHandler().handleMessage(dalyMsg); + } else { + LOG.warn("Message could not be interpreted " + Port.printBuffer(receiveBuffer)); + return readBuffers; + } } } } while (framesToBeReceived > 0); diff --git a/bms-jk-can/src/main/java/com/airepublic/bmstoinverter/bms/jk/can/JKBmsCANProcessor.java b/bms-jk-can/src/main/java/com/airepublic/bmstoinverter/bms/jk/can/JKBmsCANProcessor.java index 426f2fc6..57ab073d 100644 --- a/bms-jk-can/src/main/java/com/airepublic/bmstoinverter/bms/jk/can/JKBmsCANProcessor.java +++ b/bms-jk-can/src/main/java/com/airepublic/bmstoinverter/bms/jk/can/JKBmsCANProcessor.java @@ -6,7 +6,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.airepublic.bmstoinverter.core.Bms; +import com.airepublic.bmstoinverter.core.AbstractBMSProcessor; +import com.airepublic.bmstoinverter.core.BMS; import com.airepublic.bmstoinverter.core.Port; import com.airepublic.bmstoinverter.core.PortType; import com.airepublic.bmstoinverter.core.Protocol; @@ -16,10 +17,10 @@ import jakarta.inject.Inject; /** - * The class to handle {@link CAN} messages from a JK {@link Bms}. + * The class to handle {@link CAN} messages from a JK {@link BMS}. */ @PortType(Protocol.CAN) -public class JKBmsCANProcessor implements Bms { +public class JKBmsCANProcessor extends AbstractBMSProcessor { private final static Logger LOG = LoggerFactory.getLogger(JKBmsCANProcessor.class); @Inject private EnergyStorage energyStorage; @@ -30,40 +31,32 @@ public void initialize() { @Override - public void process(final Runnable callback) { - for (int bmsNo = 0; bmsNo < energyStorage.getBatteryPackCount(); bmsNo++) { - try { - final Port port = energyStorage.getBatteryPack(bmsNo).port; - final ByteBuffer frame = port.receiveFrame(null); - final int frameId = frame.getInt(); - final byte[] bytes = new byte[8]; - frame.get(bytes); - final ByteBuffer data = ByteBuffer.wrap(bytes); - - switch (frameId) { - case 0x2F4: - readBatteryStatus(bmsNo, data); - break; - case 0x4F4: - readCellVoltage(bmsNo, data); - break; - case 0x5F4: - readCellTemperature(bmsNo, data); - break; - case 0x7F4: - readAlarms(bmsNo, data); - break; - } - - } catch (final IOException e) { - LOG.error("Error receiving frame!", e); + public void collectData(final int bmsNo) { + try { + final Port port = energyStorage.getBatteryPack(bmsNo).port; + final ByteBuffer frame = port.receiveFrame(null); + final int frameId = frame.getInt(); + final byte[] bytes = new byte[8]; + frame.get(bytes); + final ByteBuffer data = ByteBuffer.wrap(bytes); + + switch (frameId) { + case 0x2F4: + readBatteryStatus(bmsNo, data); + break; + case 0x4F4: + readCellVoltage(bmsNo, data); + break; + case 0x5F4: + readCellTemperature(bmsNo, data); + break; + case 0x7F4: + readAlarms(bmsNo, data); + break; } - } - try { - callback.run(); - } catch (final Exception e) { - LOG.error("BMS process callback threw an exception!", e); + } catch (final IOException e) { + LOG.error("Error receiving frame!", e); } } diff --git a/bms-pylon-can/src/main/java/com/airepublic/bmstoinverter/bms/pylon/can/PylonBmsCANProcessor.java b/bms-pylon-can/src/main/java/com/airepublic/bmstoinverter/bms/pylon/can/PylonBmsCANProcessor.java index 81364bf8..099c3cb0 100644 --- a/bms-pylon-can/src/main/java/com/airepublic/bmstoinverter/bms/pylon/can/PylonBmsCANProcessor.java +++ b/bms-pylon-can/src/main/java/com/airepublic/bmstoinverter/bms/pylon/can/PylonBmsCANProcessor.java @@ -6,7 +6,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.airepublic.bmstoinverter.core.Bms; +import com.airepublic.bmstoinverter.core.AbstractBMSProcessor; +import com.airepublic.bmstoinverter.core.BMS; import com.airepublic.bmstoinverter.core.Port; import com.airepublic.bmstoinverter.core.PortType; import com.airepublic.bmstoinverter.core.Protocol; @@ -16,11 +17,11 @@ import jakarta.inject.Inject; /** - * The class to handle {@link CAN} messages from a Pylon {@link Bms}. + * The class to handle {@link CAN} messages from a Pylon {@link BMS}. */ @PortType(Protocol.CAN) -public class PylonBmsCANProcessor implements Bms { +public class PylonBmsCANProcessor extends AbstractBMSProcessor { private final static Logger LOG = LoggerFactory.getLogger(PylonBmsCANProcessor.class); @Inject private EnergyStorage energyStorage; @@ -31,51 +32,43 @@ public void initialize() { @Override - public void process(final Runnable callback) { - for (int bmsNo = 0; bmsNo < energyStorage.getBatteryPackCount(); bmsNo++) { - try { - final Port port = energyStorage.getBatteryPack(bmsNo).port; - final ByteBuffer frame = port.receiveFrame(null); - final int frameId = frame.getInt(); - final byte[] bytes = new byte[8]; - frame.get(bytes); - final ByteBuffer data = ByteBuffer.wrap(bytes); - - switch (frameId) { - case 0x351: - readChargeDischargeInfo(bmsNo, data); - break; - case 0x355: - readSOC(bmsNo, data); - break; - case 0x356: - readBatteryVoltage(bmsNo, data); - break; - case 0x35C: - requestChargeDischargeConfigChange(bmsNo, data); - break; - case 0x370: - readMinMaxTemperatureVoltage(bmsNo, data); - break; - case 0x371: - readTemperatureIds(bmsNo, data); - break; - case 0x35E: - readManufacturer(bmsNo, data); - break; - case 0x359: - readAlarms(bmsNo, data); - break; - } - } catch (final IOException e) { - LOG.error("Error receiving frame!", e); - } - } - + public void collectData(final int bmsNo) { try { - callback.run(); - } catch (final Exception e) { - LOG.error("BMS process callback threw an exception!", e); + final Port port = energyStorage.getBatteryPack(bmsNo).port; + final ByteBuffer frame = port.receiveFrame(null); + final int frameId = frame.getInt(); + final byte[] bytes = new byte[8]; + frame.get(bytes); + final ByteBuffer data = ByteBuffer.wrap(bytes); + + switch (frameId) { + case 0x351: + readChargeDischargeInfo(bmsNo, data); + break; + case 0x355: + readSOC(bmsNo, data); + break; + case 0x356: + readBatteryVoltage(bmsNo, data); + break; + case 0x35C: + requestChargeDischargeConfigChange(bmsNo, data); + break; + case 0x370: + readMinMaxTemperatureVoltage(bmsNo, data); + break; + case 0x371: + readTemperatureIds(bmsNo, data); + break; + case 0x35E: + readManufacturer(bmsNo, data); + break; + case 0x359: + readAlarms(bmsNo, data); + break; + } + } catch (final IOException e) { + LOG.error("Error receiving frame!", e); } } diff --git a/bms-seplos-can/src/main/java/com/airepublic/bmstoinverter/bms/seplos/can/SeplosBmsCANProcessor.java b/bms-seplos-can/src/main/java/com/airepublic/bmstoinverter/bms/seplos/can/SeplosBmsCANProcessor.java index caab6b64..475d7a01 100644 --- a/bms-seplos-can/src/main/java/com/airepublic/bmstoinverter/bms/seplos/can/SeplosBmsCANProcessor.java +++ b/bms-seplos-can/src/main/java/com/airepublic/bmstoinverter/bms/seplos/can/SeplosBmsCANProcessor.java @@ -6,7 +6,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.airepublic.bmstoinverter.core.Bms; +import com.airepublic.bmstoinverter.core.AbstractBMSProcessor; +import com.airepublic.bmstoinverter.core.BMS; import com.airepublic.bmstoinverter.core.Port; import com.airepublic.bmstoinverter.core.PortType; import com.airepublic.bmstoinverter.core.Protocol; @@ -16,10 +17,10 @@ import jakarta.inject.Inject; /** - * The class to handle {@link CAN} messages from a Seplos {@link Bms}. + * The class to handle {@link CAN} messages from a Seplos {@link BMS}. */ @PortType(Protocol.CAN) -public class SeplosBmsCANProcessor implements Bms { +public class SeplosBmsCANProcessor extends AbstractBMSProcessor { private final static Logger LOG = LoggerFactory.getLogger(SeplosBmsCANProcessor.class); @Inject private EnergyStorage energyStorage; @@ -30,51 +31,43 @@ public void initialize() { @Override - public void process(final Runnable callback) { - for (int bmsNo = 0; bmsNo < energyStorage.getBatteryPackCount(); bmsNo++) { - try { - final Port port = energyStorage.getBatteryPack(bmsNo).port; - final ByteBuffer frame = port.receiveFrame(null); - final int frameId = frame.getInt(); - final byte[] bytes = new byte[8]; - frame.get(bytes); - final ByteBuffer data = ByteBuffer.wrap(bytes); - - switch (frameId) { - case 0x351: - readChargeDischargeInfo(bmsNo, data); - break; - case 0x355: - readSOC(bmsNo, data); - break; - case 0x356: - readBatteryVoltage(bmsNo, data); - break; - case 0x35C: - requestChargeDischargeConfigChange(bmsNo, data); - break; - case 0x370: - readMinMaxTemperatureVoltage(bmsNo, data); - break; - case 0x371: - readTemperatureIds(bmsNo, data); - break; - case 0x35E: - readManufacturer(bmsNo, data); - break; - case 0x359: - readAlarms(bmsNo, data); - break; - } - } catch (final IOException e) { - LOG.error("Error receiving frame!", e); - } - } - + public void collectData(final int bmsNo) { try { - callback.run(); - } catch (final Exception e) { - LOG.error("BMS process callback threw an exception!", e); + final Port port = energyStorage.getBatteryPack(bmsNo).port; + final ByteBuffer frame = port.receiveFrame(null); + final int frameId = frame.getInt(); + final byte[] bytes = new byte[8]; + frame.get(bytes); + final ByteBuffer data = ByteBuffer.wrap(bytes); + + switch (frameId) { + case 0x351: + readChargeDischargeInfo(bmsNo, data); + break; + case 0x355: + readSOC(bmsNo, data); + break; + case 0x356: + readBatteryVoltage(bmsNo, data); + break; + case 0x35C: + requestChargeDischargeConfigChange(bmsNo, data); + break; + case 0x370: + readMinMaxTemperatureVoltage(bmsNo, data); + break; + case 0x371: + readTemperatureIds(bmsNo, data); + break; + case 0x35E: + readManufacturer(bmsNo, data); + break; + case 0x359: + readAlarms(bmsNo, data); + break; + } + } catch (final IOException e) { + LOG.error("Error receiving frame!", e); } } diff --git a/bms-to-inverter-main/src/main/java/com/airepublic/bmstoinverter/BmsToInverter.java b/bms-to-inverter-main/src/main/java/com/airepublic/bmstoinverter/BmsToInverter.java index 531e2bfe..2014cc90 100644 --- a/bms-to-inverter-main/src/main/java/com/airepublic/bmstoinverter/BmsToInverter.java +++ b/bms-to-inverter-main/src/main/java/com/airepublic/bmstoinverter/BmsToInverter.java @@ -13,7 +13,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.airepublic.bmstoinverter.core.Bms; +import com.airepublic.bmstoinverter.core.BMS; import com.airepublic.bmstoinverter.core.Inverter; import com.airepublic.bmstoinverter.core.bms.data.Alarms; import com.airepublic.bmstoinverter.core.bms.data.BatteryPack; @@ -32,7 +32,7 @@ /** * The main class to initiate communication between the configured BMS and the inverter. The - * {@link Bms} values are read and stored in the {@link EnergyStorage}. Once read these values are + * {@link BMS} values are read and stored in the {@link EnergyStorage}. Once read these values are * send to the (optional) MQTT Broker. Alarms and warnings will be analysed and (optionally) sent by * email if some occurred. The the data is sent to the {@link Inverter}. */ @@ -42,7 +42,7 @@ public class BmsToInverter implements AutoCloseable { @Inject private EnergyStorage energyStorage; @Inject - private Bms bms; + private BMS bms; @Inject private Inverter inverter; private final IMQTTBrokerService mqttBroker = ServiceLoader.load(IMQTTBrokerService.class).findFirst().orElse(null); diff --git a/core-api/src/main/java/com/airepublic/bmstoinverter/core/AbstractBMSProcessor.java b/core-api/src/main/java/com/airepublic/bmstoinverter/core/AbstractBMSProcessor.java new file mode 100644 index 00000000..5f908def --- /dev/null +++ b/core-api/src/main/java/com/airepublic/bmstoinverter/core/AbstractBMSProcessor.java @@ -0,0 +1,72 @@ +package com.airepublic.bmstoinverter.core; + +import java.io.IOException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.airepublic.bmstoinverter.core.bms.data.BatteryPack; +import com.airepublic.bmstoinverter.core.bms.data.EnergyStorage; + +import jakarta.inject.Inject; + +public abstract class AbstractBMSProcessor implements BMS { + private final static Logger LOG = LoggerFactory.getLogger(AbstractBMSProcessor.class); + @Inject + private EnergyStorage energyStorage; + + @Override + public void process(final Runnable callback) { + try { + LOG.info("---------------------------------> Thread " + Thread.currentThread().getId()); + clearBuffers(); + + for (int bmsNo = 0; bmsNo < energyStorage.getBatteryPackCount(); bmsNo++) { + final Port port = energyStorage.getBatteryPack(bmsNo).port; + + try { + port.ensureOpen(); + collectData(bmsNo); + } catch (final NoDataAvailableException e) { + LOG.error("Received no bytes too many times - trying to close and re-open port!"); + // try to close and re-open the port + port.close(); + port.open(); + } + } + // autoCalibrateSOC(); + } catch (final TooManyInvalidFramesException e) { + LOG.error("Received too many invalid frames - start new reading round!"); + return; + } catch (final Throwable e) { + LOG.error("Error requesting data!", e); + return; + } + + try { + callback.run(); + } catch (final Throwable e) { + LOG.error("BMS process callback threw an exception!", e); + } + } + + + protected abstract void collectData(int bmsNo) throws IOException, TooManyInvalidFramesException, NoDataAvailableException; + + + /** + * Clears any buffers or queues on all associated ports to restart communication. + */ + protected void clearBuffers() { + for (final BatteryPack pack : energyStorage.getBatteryPacks()) { + pack.port.clearBuffers(); + } + } + + + @Override + public void close() throws Exception { + energyStorage.close(); + } + +} diff --git a/core-api/src/main/java/com/airepublic/bmstoinverter/core/Bms.java b/core-api/src/main/java/com/airepublic/bmstoinverter/core/BMS.java similarity index 85% rename from core-api/src/main/java/com/airepublic/bmstoinverter/core/Bms.java rename to core-api/src/main/java/com/airepublic/bmstoinverter/core/BMS.java index 653d22fa..9d5e3b53 100644 --- a/core-api/src/main/java/com/airepublic/bmstoinverter/core/Bms.java +++ b/core-api/src/main/java/com/airepublic/bmstoinverter/core/BMS.java @@ -5,7 +5,7 @@ /** * The interface to identify a BMS. */ -public interface Bms { +public interface BMS extends AutoCloseable { /** * Any on-startup neccessary code should go here. @@ -14,7 +14,7 @@ public interface Bms { /** - * Process data received by the port and update the {@link EnergyStorage} for a {@link Bms}. + * Process data received by the port and update the {@link EnergyStorage} for a {@link BMS}. * * @param callback the code executed after successful processing */ diff --git a/core-api/src/main/java/com/airepublic/bmstoinverter/core/EnergyStorageProducer.java b/core-api/src/main/java/com/airepublic/bmstoinverter/core/EnergyStorageProducer.java index d00df9f0..dcd4e5e2 100644 --- a/core-api/src/main/java/com/airepublic/bmstoinverter/core/EnergyStorageProducer.java +++ b/core-api/src/main/java/com/airepublic/bmstoinverter/core/EnergyStorageProducer.java @@ -38,7 +38,7 @@ private void init() { /** * Search the implementing class for the {@link PortType} annotation and whether it resembles a - * {@link Bms} or {@link Inverter}. Corresponding to both annotations it will check the system + * {@link BMS} or {@link Inverter}. Corresponding to both annotations it will check the system * properties for the port names configuration defined in the config.properties * file. Port names can be defined, e.g. like
*
diff --git a/core-api/src/main/java/com/airepublic/bmstoinverter/core/NoDataAvailableException.java b/core-api/src/main/java/com/airepublic/bmstoinverter/core/NoDataAvailableException.java new file mode 100644 index 00000000..c8e415f3 --- /dev/null +++ b/core-api/src/main/java/com/airepublic/bmstoinverter/core/NoDataAvailableException.java @@ -0,0 +1,9 @@ +package com.airepublic.bmstoinverter.core; + +/** + * An {@link Exception} thrown when there is no data available to read from the {@link Port}. + */ +public class NoDataAvailableException extends Exception { + private static final long serialVersionUID = 1L; + +} diff --git a/core-api/src/main/java/com/airepublic/bmstoinverter/core/Port.java b/core-api/src/main/java/com/airepublic/bmstoinverter/core/Port.java index 5d69d71b..700712a4 100644 --- a/core-api/src/main/java/com/airepublic/bmstoinverter/core/Port.java +++ b/core-api/src/main/java/com/airepublic/bmstoinverter/core/Port.java @@ -4,10 +4,14 @@ import java.nio.ByteBuffer; import java.util.function.Predicate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * The definition of a communication port, e.g. for RS485, CAN, ModBus, etc. */ public abstract class Port implements AutoCloseable { + private final static Logger LOG = LoggerFactory.getLogger(Port.class); private String portname; /** @@ -72,6 +76,32 @@ public void setPortname(final String portname) { public abstract boolean isOpen(); + /** + * Ensures the port is open. If it is closed it will reopen the port. + * + * @return true if the port is or could be opened otherwise false + */ + public synchronized boolean ensureOpen() { + if (!isOpen()) { + // open port + try { + LOG.info("Opening " + getPortname() + " ..."); + open(); + LOG.info("Opening port {} SUCCESSFUL", getPortname()); + + } catch (final Throwable e) { + LOG.error("Opening port {} FAILED!", getPortname(), e); + } + } + + if (isOpen()) { + return true; + } + + return false; + } + + /** * Receives a frame from the ports stream. * diff --git a/core-api/src/main/java/com/airepublic/bmstoinverter/core/TooManyInvalidFramesException.java b/core-api/src/main/java/com/airepublic/bmstoinverter/core/TooManyInvalidFramesException.java new file mode 100644 index 00000000..f9bbf69a --- /dev/null +++ b/core-api/src/main/java/com/airepublic/bmstoinverter/core/TooManyInvalidFramesException.java @@ -0,0 +1,9 @@ +package com.airepublic.bmstoinverter.core; + +/** + * An {@link Exception} thrown when too many invalid frames were read from the {@link Port}. + */ +public class TooManyInvalidFramesException extends Exception { + private static final long serialVersionUID = 1L; + +} diff --git a/protocol-can/src/main/java/com/airepublic/bmstoinverter/protocol/can/JavaCANPort.java b/protocol-can/src/main/java/com/airepublic/bmstoinverter/protocol/can/JavaCANPort.java index 9c72fd6a..a2c98687 100644 --- a/protocol-can/src/main/java/com/airepublic/bmstoinverter/protocol/can/JavaCANPort.java +++ b/protocol-can/src/main/java/com/airepublic/bmstoinverter/protocol/can/JavaCANPort.java @@ -109,7 +109,7 @@ public void sendExtendedFrame(final ByteBuffer frame) throws IOException { @Override public void close() { // close old channel first - if (isOpen()) { + if (canChannel != null) { try { canChannel.close(); LOG.info("Shutting down port '{}'...OK", getPortname()); @@ -122,27 +122,6 @@ public void close() { } - private boolean ensureOpen() { - if (!isOpen()) { - // open port - try { - LOG.info("Opening " + getPortname() + " ..."); - open(); - LOG.info("Opening port {} SUCCESSFUL", getPortname()); - - } catch (final Throwable e) { - LOG.error("Opening port {} FAILED!", getPortname(), e); - } - } - - if (isOpen()) { - return true; - } - - return false; - } - - @Override public void clearBuffers() { } diff --git a/protocol-rs485/src/main/java/com/airepublic/bmstoinverter/protocol/rs485/JSerialCommPort.java b/protocol-rs485/src/main/java/com/airepublic/bmstoinverter/protocol/rs485/JSerialCommPort.java index 47e56634..c9022008 100644 --- a/protocol-rs485/src/main/java/com/airepublic/bmstoinverter/protocol/rs485/JSerialCommPort.java +++ b/protocol-rs485/src/main/java/com/airepublic/bmstoinverter/protocol/rs485/JSerialCommPort.java @@ -154,27 +154,6 @@ public void sendFrame(final ByteBuffer frame) throws IOException { } - private synchronized boolean ensureOpen() { - if (!isOpen()) { - // open port - try { - LOG.info("Opening " + getPortname() + " ..."); - open(); - LOG.info("Opening port {} SUCCESSFUL", getPortname()); - - } catch (final Throwable e) { - LOG.error("Opening port {} FAILED!", getPortname(), e); - } - } - - if (isOpen()) { - return true; - } - - return false; - } - - /** * Gets the queue of frames. *