Java tutorial
/* * Copyright (c) 2014. FarrelltonSolar * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ca.farrelltonsolar.classic; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormat; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.TimerTask; import ca.farrelltonsolar.j2modlite.ModbusException; import ca.farrelltonsolar.j2modlite.ModbusIOException; import ca.farrelltonsolar.j2modlite.facade.ModbusTCPMaster; import ca.farrelltonsolar.j2modlite.msg.ReadFileTransferResponse; import ca.farrelltonsolar.j2modlite.procimg.Register; // Classic modbus table // new Register { Address = 4115, Label = "Average battery voltage", UnitOfMeasure = "Volts", Conversion = socketAddress => U16_OneDec(socketAddress)}, // new Register { Address = 4116, Label = "PV input voltage", UnitOfMeasure = "Volts", Conversion = socketAddress => U16_OneDec(socketAddress)}, // new Register { Address = 4117, Label = "Average battery current", UnitOfMeasure = "Amps", Conversion = socketAddress => U16_OneDec(socketAddress)}, // new Register { Address = 4118, Label = "Average energy to the battery", UnitOfMeasure = "kWh", Conversion = socketAddress => U16_OneDec(socketAddress)}, // new Register { Address = 4119, Label = "Average power to the battery", UnitOfMeasure = "Watts", Conversion = socketAddress => U16(socketAddress)}, // new Register { Address = 4120, Label = "Battery charge state", UnitOfMeasure = "", Conversion = socketAddress => ChargeState(socketAddress)}, // new Register { Address = 4121, Label = "Average PV inout current", UnitOfMeasure = "Amps", Conversion = socketAddress => U16_OneDec(socketAddress)}, // new Register { Address = 4122, Label = "PV VOC", UnitOfMeasure = "Volts", Conversion = socketAddress => U16_OneDec(socketAddress)}, // new Register { Address = 4125, Label = "Daily amp hours", UnitOfMeasure = "Amp hours", Conversion = socketAddress => U16_OneDec(socketAddress)}, // new Register { Address = 4126, Label = "Total kWhours", UnitOfMeasure = "kWh", Conversion = socketAddress => U32_OneDec(socketAddress)}, // new Register { Address = 4128, Label = "Total Amp hours", UnitOfMeasure = "Amp hours", Conversion = socketAddress => U32_OneDec(socketAddress)}, // new Register { Address = 4130, Label = "Info flag", UnitOfMeasure = "", Conversion = socketAddress => Info(socketAddress)} // new Register { Address = 4132, Label = "BATTemperature", UnitOfMeasure = "", Conversion = socketAddress => U16_OneDec(socketAddress)} // new Register { Address = 4133, Label = "FETTemperature", UnitOfMeasure = "", Conversion = socketAddress => U16_OneDec(socketAddress)} // new Register { Address = 4134, Label = "PCBTemperature", UnitOfMeasure = "", Conversion = socketAddress => U16_OneDec(socketAddress)} // new Tupple { Description = "(Off) No power, waiting for power source, battery voltage over set point.", Value = 0 }, // new Tupple { Description = "(Absorb) Regulating battery voltage at absorb set point until the batteries are charged.", Value = 3 }, // new Tupple { Description = "(Bulk) Max power point tracking until absorb voltage reached.", Value = 4 }, // new Tupple { Description = "(Float) Battery is full and regulating battery voltage at float set point.", Value = 5 }, // new Tupple { Description = "(Float) Max power point tracking. Seeking float set point voltage.", Value = 6 }, // new Tupple { Description = "(Equalize) Regulating battery voltage at equalize set point.", Value = 7 }, // new Tupple { Description = "(Error) Input voltage is above maximum classic operating voltage.", Value = 10 }, // new Tupple { Description = "(Equalizing) Max power point tracking. Seeking equalize set point voltage.", Value = 18 } // TriStar modbus table // new Register { Address = 1, Label = "V Scale", UnitOfMeasure = "", Conversion = socketAddress => U32(socketAddress)}, // new Register { Address = 3, Label = "A Scale", UnitOfMeasure = "", Conversion = socketAddress => U32(socketAddress)}, // new Register { Address = 25, Label = "Average battery voltage", UnitOfMeasure = "Volts", Conversion = socketAddress => VScale(socketAddress)}, // new Register { Address = 28, Label = "PV input voltage", UnitOfMeasure = "Volts", Conversion = socketAddress => VScale(socketAddress)}, // new Register { Address = 29, Label = "Average battery current", UnitOfMeasure = "Amps", Conversion = socketAddress => IScale(socketAddress)}, // new Register { Address = 30, Label = "Average PV current", UnitOfMeasure = "Amps", Conversion = socketAddress => IScale(socketAddress)}, // new Register { Address = 45, Label = "Info flag", UnitOfMeasure = "", Conversion = socketAddress => Info(socketAddress)}, // new Register { Address = 51, Label = "Battery charge state", UnitOfMeasure = "", Conversion = socketAddress => ChargeState(socketAddress)}, // new Register { Address = 58, Label = "Total kWhours", UnitOfMeasure = "kWh", Conversion = socketAddress => U16(socketAddress)}, // new Register { Address = 59, Label = "Average power to the battery", UnitOfMeasure = "Watts", Conversion = socketAddress => PScale(socketAddress)}, // new Register { Address = 69, Label = "Average energy to the battery", UnitOfMeasure = "kWh", Conversion = socketAddress => WHr(socketAddress)} // new Tupple { Description = "(Start) System startup.", Value = 0 }, // new Tupple { Description = "(Night check) No power, detecting nightfall.", Value = 1 }, // new Tupple { Description = "(Disconnected) No power.", Value = 2 }, // new Tupple { Description = "(Night) No power, waiting for power source.", Value = 3 }, // new Tupple { Description = "(Fault) Detected fault.", Value = 4 }, // new Tupple { Description = "(Bulk) Max power point tracking until absorb voltage reached.", Value = 5 }, // new Tupple { Description = "(Absorb) Regulating battery voltage at absorb set point until the batteries are charged.", Value = 6 }, // new Tupple { Description = "(Float) Max power point tracking. Seeking float set point voltage.", Value = 7 }, // new Tupple { Description = "(Equalize) Regulating battery voltage at equalize set point.", Value = 8 }, // new Tupple { Description = "(Slave) State set by master charge controller.", Value = 9 } /** * Created by Graham on 12/12/2014. */ public class ModbusTask extends TimerTask { final Object lock = new Object(); private Context context; private ModbusTCPMaster modbusMaster; private ChargeControllerInfo chargeControllerInfo; private int reference = 4100; //the reference; offset where to start reading from private Readings readings; private LogEntry dayLogEntry; private LogEntry minuteLogEntry; private float v_pu; private float i_pu; private boolean foundWhizBangJr = false; private boolean foundTriStar = false; private boolean initialReadingLoaded = false; public ModbusTask(ChargeControllerInfo cc, Context ctx) { chargeControllerInfo = cc; init(ctx); } private void init(Context ctx) { context = ctx; readings = new Readings(); dayLogEntry = new LogEntry(); minuteLogEntry = new LogEntry(); Thread.currentThread().setPriority(Thread.MIN_PRIORITY); Log.d(getClass().getName(), String.format("ModbusTask created thread is %s", Thread.currentThread().getName())); } public ChargeControllerInfo chargeController() { return chargeControllerInfo; } public boolean connect() throws UnknownHostException { boolean rVal = false; InetAddress inetAddress = InetAddress.getByName(chargeControllerInfo.deviceIpAddress()); Log.d(getClass().getName(), String.format("Connecting to %s (%s)", chargeControllerInfo.toString(), inetAddress.toString())); try { disconnect(); modbusMaster = new ModbusTCPMaster(chargeControllerInfo.deviceIpAddress(), chargeControllerInfo.port()); modbusMaster.connect(); if (modbusMaster.isConnected()) { rVal = true; } } catch (Exception e1) { Log.w(getClass().getName(), String.format("Could not connect to %s, ex: %s", chargeControllerInfo.toString(), e1)); e1.printStackTrace(); modbusMaster = null; MonitorApplication.chargeControllers().setReachable(chargeControllerInfo.deviceIpAddress(), chargeControllerInfo.port(), false); } return rVal; } public void disconnect() { boolean didDisconnect = false; if (isConnected()) { synchronized (lock) { if (modbusMaster != null) { modbusMaster.disconnect(); didDisconnect = true; } modbusMaster = null; } Log.d(getClass().getName(), didDisconnect ? String.format("Disconnected from %s", chargeControllerInfo.toString()) : String.format("Tried to Disconnect from %s but did not complete", chargeControllerInfo.toString())); } clearReadings(); } public boolean isConnected() { boolean rVal = false; if (modbusMaster != null) { rVal = modbusMaster.isConnected(); } return rVal; } @Override protected void finalize() throws Throwable { Log.d(getClass().getName(), "ModbusTask finalized"); super.finalize(); } @Override public boolean cancel() { disconnect(); Log.d(getClass().getName(), String.format("ModbusTask cancel thread is %s", Thread.currentThread().getName())); return super.cancel(); } @Override public void run() { // Log.d(getClass().getName(), String.format("ModbusTask begin run for %s on thread is %s", chargeControllerInfo.toString(), Thread.currentThread().getName())); try { synchronized (lock) { boolean connected = isConnected(); if (connected == false) { connected = connect(); } if (connected) { if (initialReadingLoaded == false) { initialReadingLoaded = true; MonitorApplication.chargeControllers().setReachable(chargeControllerInfo.deviceIpAddress(), chargeControllerInfo.port(), true); if (lookForTriStar() == DeviceType.Classic) { lookForWhizBangJr(); loadBoilerPlateInfo(); } } GetModbusReadings(); if (!foundTriStar) { if (dayLogEntry.isEmpty()) { loadDayLogs(); } else { DateTime logDate = dayLogEntry.getLogDate(); if (logDate.isBefore(DateTime.now().withTimeAtStartOfDay())) { // still good? loadDayLogs(); } } dayLogEntry.broadcastLogs(context, Constants.CA_FARRELLTONSOLAR_CLASSIC_DAY_LOGS); if (minuteLogEntry.isEmpty()) { loadMinuteLogs(); } else { DateTime logDate = minuteLogEntry.getLogDate(); if (logDate.isBefore(DateTime.now().minusHours(1))) { // still good? loadMinuteLogs(); } } minuteLogEntry.broadcastLogs(context, Constants.CA_FARRELLTONSOLAR_CLASSIC_MINUTE_LOGS); } } } } catch (Exception e1) { Log.w(getClass().getName(), String.format("Failed to run due to exception ex: %s", e1)); e1.printStackTrace(); disconnect(); } // Log.d(getClass().getName(), "end run"); } public void clearReadings() { readings.set(RegisterName.Power, 0.0f); readings.set(RegisterName.BatVoltage, 0.0f); readings.set(RegisterName.BatCurrent, 0.0f); readings.set(RegisterName.PVVoltage, 0.0f); readings.set(RegisterName.PVCurrent, 0.0f); readings.set(RegisterName.EnergyToday, 0.0f); readings.set(RegisterName.TotalEnergy, 0.0f); readings.set(RegisterName.ChargeState, -1); readings.set(RegisterName.ConnectionState, 0); readings.set(RegisterName.SOC, 0); readings.set(RegisterName.Aux1, false); readings.set(RegisterName.Aux2, false); readings.broadcastReadings(context, Constants.CA_FARRELLTONSOLAR_CLASSIC_READINGS); } private void GetModbusReadings() throws ModbusException { try { if (foundTriStar) { Register[] registers = modbusMaster.readMultipleRegisters(0, 80); if (registers != null && registers.length == 80) { readings.set(RegisterName.BatVoltage, VScale(registers[24].getValue())); readings.set(RegisterName.PVVoltage, VScale(registers[27].getValue())); readings.set(RegisterName.BatCurrent, IScale(registers[28].getValue())); readings.set(RegisterName.PVCurrent, IScale(registers[29].getValue())); readings.set(RegisterName.Power, PScale(registers[58].getValue())); readings.set(RegisterName.EnergyToday, WHr(registers[68].getValue())); readings.set(RegisterName.TotalEnergy, (float) registers[57].getValue()); } else { Log.w(getClass().getName(), String.format("Modbus readMultipleRegisters returned null")); throw new ModbusException("Failed to read data from modbus"); } } else { Register[] registers = modbusMaster.readMultipleRegisters(reference, 36); if (registers != null && registers.length == 36) { readings.set(RegisterName.BatCurrent, registers[16].getValue() / 10.0f); readings.set(RegisterName.Power, (float) registers[18].getValue()); readings.set(RegisterName.BatVoltage, registers[14].getValue() / 10.0f); readings.set(RegisterName.PVVoltage, registers[15].getValue() / 10.0f); readings.set(RegisterName.PVCurrent, registers[20].getValue() / 10.0f); readings.set(RegisterName.EnergyToday, registers[17].getValue() / 10.0f); readings.set(RegisterName.TotalEnergy, ((registers[26].getValue() << 16) + registers[25].getValue()) / 10.0f); readings.set(RegisterName.ChargeState, MSBFor(registers[19].getValue())); readings.set(RegisterName.InfoFlagsBits, ((registers[30].getValue() << 16) + registers[29].getValue())); readings.set(RegisterName.BatTemperature, (short) registers[31].getValue() / 10.0f); readings.set(RegisterName.FETTemperature, (short) registers[32].getValue() / 10.0f); readings.set(RegisterName.PCBTemperature, (short) registers[33].getValue() / 10.0f); int infoFlag = registers[29].getValue(); readings.set(RegisterName.Aux1, (infoFlag & 0x4000) != 0); readings.set(RegisterName.Aux2, (infoFlag & 0x8000) != 0); } else { Log.w(getClass().getName(), String.format("Modbus readMultipleRegisters returned null")); throw new ModbusException("Failed to read data from modbus"); } if (foundWhizBangJr) { Register[] registers2 = modbusMaster.readMultipleRegisters(4360, 16); if (registers2 != null && registers2.length == 16) { Register a = registers2[10]; readings.set(RegisterName.WhizbangBatCurrent, a.toShort() / 10.0f); Register soc = registers2[12]; short socVal = soc.toShort(); readings.set(RegisterName.SOC, socVal); } else { Log.w(getClass().getName(), String.format("Modbus readMultipleRegisters returned null")); throw new ModbusException("Failed to read data from modbus"); } } } readings.broadcastReadings(context, Constants.CA_FARRELLTONSOLAR_CLASSIC_READINGS); } catch (Exception all) { Log.w(getClass().getName(), String.format("GetModbusReadings Exception ex: %s", all)); throw new ModbusException(all.getMessage()); } } private void BroadcastToast(String message) { Intent intent2 = new Intent(Constants.CA_FARRELLTONSOLAR_CLASSIC_TOAST); intent2.putExtra("message", message); LocalBroadcastManager.getInstance(context).sendBroadcast(intent2); } public Bundle getChargeControllerInformation() throws ModbusException { Bundle result = new Bundle(); DeviceType deviceType = lookForTriStar(); result.putSerializable("DeviceType", deviceType); if (deviceType == DeviceType.Classic) { result.putString("UnitName", getUnitName()); result.putInt("UnitID", getUnitID()); result.putBoolean("FoundWhizbang", lookForWhizBangJr()); } else { result.putString("UnitName", "Tristar"); result.putInt("UnitID", 0); } return result; } private int getUnitID() throws ModbusException { int unitId = -1; Register[] registers = modbusMaster.readMultipleRegisters(4110, 4); if (registers != null && registers.length == 4) { unitId = (registers[1].getValue() << 16) + registers[0].getValue(); } return unitId; } private void loadBoilerPlateInfo() { try { Register[] registers = modbusMaster.readMultipleRegisters(4100, 32); if (registers != null && registers.length == 32) { short reg1 = (short) registers[0].getValue(); String model = String.format("Classic %d (rev %d)", reg1 & 0x00ff, reg1 >> 8); chargeControllerInfo.setModel(model); int buildYear = registers[1].getValue(); int buildMonthDay = registers[2].getValue(); DateTime buildDate = new DateTime(buildYear, (buildMonthDay >> 8), (buildMonthDay & 0x00ff), 0, 0); chargeControllerInfo.setBuildDate(DateTimeFormat.fullDate().print(buildDate)); short reg6 = registers[5].toShort(); short reg7 = registers[6].toShort(); short reg8 = registers[7].toShort(); String macAddress = String.format("%02x:%02x:%02x:%02x:%02x:%02x", reg8 >> 8, reg8 & 0x00ff, reg7 >> 8, reg7 & 0x00ff, reg6 >> 8, reg6 & 0x00ff); chargeControllerInfo.setMacAddress(macAddress); float reg22 = (float) registers[21].getValue(); chargeControllerInfo.setLastVOC(reg22 / 10); } registers = modbusMaster.readMultipleRegisters(4244, 2); if (registers != null && registers.length == 2) { short reg4245 = (short) registers[0].getValue(); chargeControllerInfo.setNominalBatteryVoltage(reg4245); } registers = modbusMaster.readMultipleRegisters(16386, 4); if (registers != null && registers.length == 4) { short reg16387 = registers[0].toShort(); short reg16388 = registers[1].toShort(); short reg16389 = registers[2].toShort(); short reg16390 = registers[3].toShort(); chargeControllerInfo.setAppVersion(String.format("%d", (reg16388 << 16) + reg16387)); chargeControllerInfo.setNetVersion(String.format("%d", (reg16390 << 16) + reg16389)); } } catch (Exception e) { Log.w(getClass().getName(), "loadBoilerPlateInfo failed ex: %s", e); } } private String getUnitName() throws ModbusException { Register[] registers = modbusMaster.readMultipleRegisters(4209, 4); if (registers != null && registers.length == 4) { byte[] v0 = registers[0].toBytes(); byte[] v1 = registers[1].toBytes(); byte[] v2 = registers[2].toBytes(); byte[] v3 = registers[3].toBytes(); byte[] temp = new byte[8]; temp[0] = v0[1]; temp[1] = v0[0]; temp[2] = v1[1]; temp[3] = v1[0]; temp[4] = v2[1]; temp[5] = v2[0]; temp[6] = v3[1]; temp[7] = v3[0]; String unitName = new String(temp); return unitName.trim(); } return ""; } private boolean lookForWhizBangJr() throws ModbusException { foundWhizBangJr = false; Register[] registers = modbusMaster.readMultipleRegisters(4360, 12); if (registers != null && registers.length == 12) { Register a = registers[10]; foundWhizBangJr = a.toShort() != 0; } return foundWhizBangJr; } private DeviceType lookForTriStar() { foundTriStar = false; try { Register[] registers = modbusMaster.readMultipleRegisters(4100, 4); // try classic first if (registers == null) { registers = modbusMaster.readMultipleRegisters(0, 4); // see if its a tristar if (registers != null && registers.length == 4) { foundTriStar = registers[0].toShort() != 0; if (foundTriStar) { float hi = registers[0].toShort(); float lo = registers[1].toShort(); lo = lo / 65536; v_pu = hi + lo; hi = (float) registers[2].toShort(); lo = (float) registers[3].toShort(); lo = lo / 65536; i_pu = hi + lo; reference = 0; } } } } catch (ModbusException e) { Log.d(getClass().getName(), "This is probably not a Tristar!"); } return foundTriStar ? DeviceType.TriStar : DeviceType.Classic; } private void loadDayLogs() throws ModbusException { String dayLogCacheName = chargeControllerInfo.dayLogCacheName(); try { Bundle dayLogs = BundleCache.getInstance(context).getBundle(dayLogCacheName); if (dayLogs != null && dayLogs.size() > 0) { dayLogEntry = new LogEntry(dayLogs); DateTime logDate = dayLogEntry.getLogDate(); if (logDate.isAfter(DateTime.now().withTimeAtStartOfDay())) { // still good? Log.d(getClass().getName(), "DayLog cache still good to go"); return; } Log.d(getClass().getName(), "DayLog cache stale, reload data from modbus"); } dayLogEntry.set(Constants.CLASSIC_KWHOUR_DAILY_CATEGORY, ReadLogs(365, Constants.CLASSIC_KWHOUR_DAILY_CATEGORY, Constants.MODBUS_FILE_DAILIES_LOG, 1)); dayLogEntry.set(Constants.CLASSIC_FLOAT_TIME_DAILY_CATEGORY, ReadLogs(365, Constants.CLASSIC_FLOAT_TIME_DAILY_CATEGORY, Constants.MODBUS_FILE_DAILIES_LOG, 1)); dayLogEntry.set(Constants.CLASSIC_HIGH_POWER_DAILY_CATEGORY, ReadLogs(365, Constants.CLASSIC_HIGH_POWER_DAILY_CATEGORY, Constants.MODBUS_FILE_DAILIES_LOG, 1)); dayLogEntry.set(Constants.CLASSIC_HIGH_TEMP_DAILY_CATEGORY, ReadLogs(365, Constants.CLASSIC_HIGH_TEMP_DAILY_CATEGORY, Constants.MODBUS_FILE_DAILIES_LOG, 1)); dayLogEntry.set(Constants.CLASSIC_HIGH_PV_VOLT_DAILY_CATEGORY, ReadLogs(365, Constants.CLASSIC_HIGH_PV_VOLT_DAILY_CATEGORY, Constants.MODBUS_FILE_DAILIES_LOG, 1)); dayLogEntry.set(Constants.CLASSIC_HIGH_BATTERY_VOLT_DAILY_CATEGORY, ReadLogs(365, Constants.CLASSIC_HIGH_BATTERY_VOLT_DAILY_CATEGORY, Constants.MODBUS_FILE_DAILIES_LOG, 1)); Log.d(getClass().getName(), "Completed reading Day logs"); dayLogEntry.setLogDate(DateTime.now()); BundleCache.getInstance(context).putBundle(dayLogCacheName, dayLogEntry.getLogs()); BroadcastToast(context.getString(R.string.toast_day_logs)); } catch (ModbusIOException ex) { if (ex.isEOF()) { Log.w(getClass().getName(), String.format("loadDayLogs reached EOF ex: %s", ex)); dayLogEntry.setLogDate(DateTime.now()); BundleCache.getInstance(context).putBundle(dayLogCacheName, dayLogEntry.getLogs()); BroadcastToast(context.getString(R.string.toast_day_logs)); return; } BundleCache.getInstance(context).clearCache(dayLogCacheName); dayLogEntry = new LogEntry(); dayLogEntry.setLogDate(DateTime.now().plusMinutes(5)); // try it again later Log.w(getClass().getName(), String.format("loadDayLogs failed ex: %s", ex)); } catch (Exception ex) { BundleCache.getInstance(context).clearCache(dayLogCacheName); dayLogEntry = new LogEntry(); dayLogEntry.setLogDate(DateTime.now().plusMinutes(5)); // try it again later Log.w(getClass().getName(), String.format("loadDayLogs failed ex: %s", ex)); } } private void loadMinuteLogs() throws ModbusException { String minuteLogCacheName = chargeControllerInfo.minuteLogCacheName(); try { Bundle minuteLog = BundleCache.getInstance(context).getBundle(minuteLogCacheName); if (minuteLog != null && minuteLog.size() > 0) { minuteLogEntry = new LogEntry(minuteLog); DateTime logDate = minuteLogEntry.getLogDate(); if (logDate.isAfter(DateTime.now().minusHours(1))) { // still good? Log.d(getClass().getName(), "MinuteLog cache still good to go"); return; } Log.d(getClass().getName(), "MinuteLog cache stale, reload data from modbus"); } int requiredEntries = ReadMinuteLogTimestamps(); // sum of minutes log up to 24 hours minuteLogEntry.set(Constants.CLASSIC_POWER_HOURLY_CATEGORY, ReadLogs(requiredEntries, Constants.CLASSIC_POWER_HOURLY_CATEGORY, Constants.MODBUS_FILE_MINUTES_LOG, 1)); minuteLogEntry.set(Constants.CLASSIC_INPUT_VOLTAGE_HOURLY_CATEGORY, ReadLogs(requiredEntries, Constants.CLASSIC_INPUT_VOLTAGE_HOURLY_CATEGORY, Constants.MODBUS_FILE_MINUTES_LOG, 10)); minuteLogEntry.set(Constants.CLASSIC_BATTERY_VOLTAGE_HOURLY_CATEGORY, ReadLogs(requiredEntries, Constants.CLASSIC_BATTERY_VOLTAGE_HOURLY_CATEGORY, Constants.MODBUS_FILE_MINUTES_LOG, 10)); minuteLogEntry.set(Constants.CLASSIC_OUTPUT_CURRENT_HOURLY_CATEGORY, ReadLogs(requiredEntries, Constants.CLASSIC_OUTPUT_CURRENT_HOURLY_CATEGORY, Constants.MODBUS_FILE_MINUTES_LOG, 10)); minuteLogEntry.set(Constants.CLASSIC_ENERGY_HOURLY_CATEGORY, ReadLogs(requiredEntries, Constants.CLASSIC_ENERGY_HOURLY_CATEGORY, Constants.MODBUS_FILE_MINUTES_LOG, 10)); minuteLogEntry.set(Constants.CLASSIC_CHARGE_STATE_HOURLY_CATEGORY, ReadLogs(requiredEntries, Constants.CLASSIC_CHARGE_STATE_HOURLY_CATEGORY, Constants.MODBUS_FILE_MINUTES_LOG, 256)); Log.d(getClass().getName(), "Completed reading minute logs"); minuteLogEntry.setLogDate(DateTime.now()); BundleCache.getInstance(context).putBundle(minuteLogCacheName, minuteLogEntry.getLogs()); BroadcastToast(context.getString(R.string.toast_minute_logs)); } catch (ModbusIOException ex) { if (ex.isEOF()) { Log.w(getClass().getName(), String.format("loadMinuteLogs reached EOF ex: %s", ex)); minuteLogEntry.setLogDate(DateTime.now()); BundleCache.getInstance(context).putBundle(minuteLogCacheName, minuteLogEntry.getLogs()); BroadcastToast(context.getString(R.string.toast_minute_logs)); return; } BundleCache.getInstance(context).clearCache(minuteLogCacheName); minuteLogEntry = new LogEntry(); minuteLogEntry.setLogDate(DateTime.now().plusMinutes(5)); // try it again later Log.w(getClass().getName(), String.format("loadDayLogs failed ex: %s", ex)); } catch (Exception ex) { BundleCache.getInstance(context).clearCache(minuteLogCacheName); minuteLogEntry = new LogEntry(); minuteLogEntry.setLogDate(DateTime.now().plusMinutes(5)); // try it again later Log.w(getClass().getName(), String.format("LoadMinuteLogs failed ex: %s", ex)); } } private float[] ReadLogs(int requiredEntries, int category, int device, int factor) throws ModbusException { int index = 0; float[] buffer = new float[requiredEntries]; while (index < requiredEntries) { ReadFileTransferResponse regRes = modbusMaster.readFileTransfer(index, category, device); if (regRes != null) { int count = regRes.getWordCount(); if (count > 0) { int j = count - 1; for (int i = 0; i < count; i++, j--) { if (i + index > requiredEntries - 1) { break; } float value = (float) registerToShort(regRes.getRegister(j).toBytes()); buffer[i + index] = value / factor; } index += count; } } else { Log.w(getClass().getName(), String.format("Modbus ReadLogs failed to get category: %d", category)); throw new ModbusException("Failed to read File Transfer data from modbus"); } } return buffer; } private int ReadMinuteLogTimestamps() throws ModbusException { final int bufferSize = 1440; // assume max of one entry per minute for 20 hrs int requiredEntries = 0; int index = 0; short lasMinute; short currentMinute = -1; short minuteSum = 0; short[] buffer = new short[bufferSize]; try { while (index < bufferSize) { ReadFileTransferResponse regRes = modbusMaster.readFileTransfer(index, Constants.CLASSIC_TIMESTAMP_HIGH_HOURLY_CATEGORY, Constants.MODBUS_FILE_MINUTES_LOG); if (regRes != null) { int count = regRes.getWordCount(); if (count > 0) { int j = count - 1; for (int i = 0; i < count; i++, j--) { lasMinute = currentMinute; if (i + index > bufferSize - 1) { break; } short val = registerToShort(regRes.getRegister(j).toBytes()); short min = (short) (val & 0x003f); short hour = (short) ((val >> 6) & 0x001f); currentMinute = (short) (min + hour * 60); if (lasMinute != -1) { if (currentMinute > lasMinute) { lasMinute += 1440; // roll over midnight } minuteSum += lasMinute - currentMinute; buffer[i + index] = minuteSum; if (minuteSum > 1440) { //minutes in 24 hours requiredEntries = i + index; // output buffer size required index = bufferSize; //exit while break; //exit for } } } index += count; } } else { Log.w(getClass().getName(), String.format("Modbus ReadLogs failed to get timestamps")); throw new ModbusException("Failed to read File Transfer data from modbus"); } } } catch (ModbusIOException ex) { if (ex.isEOF()) { if (requiredEntries == 0) { throw new ModbusException("Could not load Minute Log Timestamps"); } } } short[] output = new short[requiredEntries]; System.arraycopy(buffer, 0, output, 0, requiredEntries); minuteLogEntry.set(Constants.CLASSIC_TIMESTAMP_HIGH_HOURLY_CATEGORY, output); return requiredEntries; } private static short registerToShort(byte[] bytes) { return (short) ((bytes[1] << 8) | (bytes[0] & 0xff)); } private int MSBFor(int val) { return val >> 8; } private float WHr(float val) { val /= 1000; return val; } private float PScale(float val) { val = val * v_pu * i_pu; val /= 131072; return val; } private float VScale(float val) { val = val * v_pu; val /= 32768; return val; } private float IScale(float val) { val = val * i_pu; val /= 32768; return val; } // END Tristar code... }