/** * * Copyright (c) 2009-2016 Freedomotic team http://freedomotic.com * * This file is part of Freedomotic * * This Program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2, or (at your option) any later version. * * This Program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * Freedomotic; see the file COPYING. If not, see * <http://www.gnu.org/licenses/>. */ package com.freedomotic.plugins.devices.modbus; import com.serotonin.modbus4j.BatchRead; import com.serotonin.modbus4j.BatchResults; import com.serotonin.modbus4j.code.RegisterRange; import com.serotonin.modbus4j.locator.BaseLocator; import com.serotonin.modbus4j.locator.BinaryLocator; import com.serotonin.modbus4j.locator.NumericLocator; import com.freedomotic.events.GenericEvent; import com.freedomotic.events.ProtocolRead; import com.freedomotic.model.ds.Config; /** * This class is used to encapsulate the ModbusLocator used by Modbus4J and to * extend with Freedom specific functionality. The locators are the elements * that are used to configure the reads /writes on the modbus and to expose that * information in Freedom. * * @author gpt */ class FreedomoticModbusLocator { private BaseLocator modbusLocator; private final String name; private final int slaveId; private final int registerRange; private final int dataType; private final int offset; //private int numberOfRegisters; private final Byte bit; //private String characterEncoding; private final Double multiplier; private final Double additive; private final String eventName; private final String objectName; private final String objectClass; private final String objectBehavior; //used to store the lastvalue readed and only send if changes //TODO: to be used private Object lastValueReaded; /** * Constructor. * * @param configuration * @param i */ FreedomoticModbusLocator(Config configuration, int i) { if (i < 0) { name = configuration.getStringProperty("Name", "DefaultName"); slaveId = configuration.getIntProperty("SlaveId", 0); registerRange = parseRegisterRange(configuration.getStringProperty("RegisterRange", "COIL_STATUS")); dataType = parseDataType(configuration.getStringProperty("DataType", "BINARY")); offset = configuration.getIntProperty("Offset", 0); //protocol read link objectName = configuration.getStringProperty("objectName", ""); objectClass = configuration.getStringProperty("objectClass", ""); objectBehavior = configuration.getStringProperty("objectBehavior", ""); //TODO: The Modbus4j functionality must be extend to allow to read and combine several "bit" values from // the same register. For example bit1&bit2 generates a 4 four states value and there are Slaves that uses this format, // and should be abstracted. //we try to parse the bit value. bit = Byte.valueOf(configuration.getStringProperty("Bit", "-1")); //TODO: use the number of registers //numberOfRegisters= configuration.getTuples().getIntProperty(i,"NumberOfRegisters",1); //At this moment the characterEncoding is not necesary //characterEncoding=configuration.getTuples().getStringProperty(i,"CharacterEncoding","ASCII"); multiplier = configuration.getDoubleProperty("Multiplier", 1); additive = configuration.getDoubleProperty("Additive", 0); eventName = configuration.getStringProperty("EventName", "Event"); } else { name = configuration.getTuples().getStringProperty(i, "Name", "DefaultName"); slaveId = configuration.getTuples().getIntProperty(i, "SlaveId", 0); registerRange = parseRegisterRange(configuration.getTuples().getStringProperty(i, "RegisterRange", "COIL_STATUS")); dataType = parseDataType(configuration.getTuples().getStringProperty(i, "DataType", "BINARY")); offset = configuration.getTuples().getIntProperty(i, "Offset", 0); //protocol read link objectName = configuration.getTuples().getStringProperty(i, "objectName", ""); objectClass = configuration.getTuples().getStringProperty(i, "objectClass", ""); objectBehavior = configuration.getTuples().getStringProperty(i, "objectBehavior", ""); //TODO: The Modbus4j functionality must be extend to allow to read and combine several "bit" values from // the same register. For example bit1&bit2 generates a 4 four states value and there are Slaves that uses this format, // and should be abstracted. //we try to parse the bit value. bit = Byte.valueOf(configuration.getTuples().getStringProperty(i, "Bit", "-1")); //TODO: use the number of registers //numberOfRegisters= configuration.getTuples().getIntProperty(i,"NumberOfRegisters",1); //At this moment the characterEncoding is not necesary //characterEncoding=configuration.getTuples().getStringProperty(i,"CharacterEncoding","ASCII"); multiplier = configuration.getTuples().getDoubleProperty(i, "Multiplier", 1); additive = configuration.getTuples().getDoubleProperty(i, "Additive", 0); eventName = configuration.getTuples().getStringProperty(i, "EventName", "Event"); } if (bit != -1) { if (registerRange != RegisterRange.HOLDING_REGISTER && registerRange != RegisterRange.INPUT_REGISTER) { //throw a bad configuration exception. } else { modbusLocator = new BinaryLocator(slaveId, registerRange, offset, bit); //modbusLocator = new ModbusLocator(slaveId,registerRange,offset,dataType,bit); } } else if (registerRange == RegisterRange.COIL_STATUS) { modbusLocator = new BinaryLocator(slaveId, registerRange, offset); } else { modbusLocator = new NumericLocator(slaveId, registerRange, offset, dataType); } } private int parseRegisterRange(String stringProperty) { //TODO: Check that the RegisterRange is correct //TODO: use an enum? // Admited values: CoilStatus, InputStatus, HoldingRegister, InputRegister switch (stringProperty) { case "COIL_STATUS": return 1; case "INPUT_STATUS": return 2; case "HOLDING_REGISTER": return 3; case "INPUT_REGISTER": return 4; default: return -1; //TODO: Handle format error } } private int parseDataType(String stringProperty) { switch (stringProperty) { case "BINARY": return 1; case "TWO_BYTE_INT_UNSIGNED": return 2; case "TWO_BYTE_INT_SIGNED": return 3; case "FOUR_BYTE_INT_UNSIGNED": return 4; case "FOUR_BYTE_INT_SIGNED": return 5; case "FOUR_BYTE_INT_UNSIGNED_SWAPPED": return 6; case "FOUR_BYTE_INT_SIGNED_SWAPPED": return 7; case "FOUR_BYTE_FLOAT": return 8; case "FOUR_BYTE_FLOAT_SWAPPED": return 9; case "EIGHT_BYTE_INT_UNSIGNED": return 10; case "EIGHT_BYTE_INT_SIGNED": return 11; case "EIGHT_BYTE_INT_UNSIGNED_SWAPPED": return 12; case "EIGHT_BYTE_INT_SIGNED_SWAPPED": return 13; case "EIGHT_BYTE_FLOAT": return 14; case "EIGHT_BYTE_FLOAT_SWAPPED": return 15; case "TWO_BYTE_BCD": return 16; case "FOUR_BYTE_BCD": return 17; default: return -1; //TODO: Handle format error } } /** * Fills the BatchRead with the information of the ModbusLocators * * @param batchRead the batchRead that is going to be updated */ void updateBatchRead(BatchRead<String> batchRead) { //TODO: The Name is not a good id. batchRead.addLocator(name, getModbusLocator()); } /** * Uses the FreedomModbusLocator to fill an Event with the correct * information Used by the ModbusSensor * * @param results the readed values * @param event The event that is filled with the information */ void fillEvent(BatchResults<String> results, GenericEvent event) { //GenericEvent event = new GenericEvent(sensor); //TODO: We can use a switch over the eventName to send a more specialized event String value; if (bit != -1) //it's a bit value { value = results.getValue(name).toString(); } else if (dataType == parseDataType("BINARY")) { value = results.getValue(name).toString(); } else //it's a numeric value { value = Double.toString(getAdjustedValue(Double.parseDouble((results.getValue(name).toString())))); } event.addProperty(getName(), value); } void fillProtocolEvent(BatchResults<String> results, ProtocolRead event) { String value; //System.out.println("value: " + results.getValue(name)); if (bit != -1) //it's a bit value { value = results.getValue(name).toString(); } else if (dataType == parseDataType("BINARY")) { value = results.getValue(name).toString(); } else //it's a numeric value { value = Double.toString(getAdjustedValue(Double.parseDouble((results.getValue(name).toString())))); } event.addProperty("object.class", objectClass); event.addProperty("object.name", objectName); event.addProperty("behavior", objectBehavior); event.addProperty("behaviorValue", value); } /** * @return the modbusLocator */ protected BaseLocator getModbusLocator() { return modbusLocator; } /** * Transforms the value using the FreedomoticModbusLocator information to * translate from/to Freedomotic to Modbus scales * * @param value the value to transform * @return the transformed value */ private double getAdjustedValue(double value) { return value * multiplier + additive; } Object parseValue(Config properties, int i) { //TODO: use the DataType to parse the correct type String value = properties.getTuples().getStringProperty(0, "value", "0"); if (bit != -1) { return value; } else if (dataType == parseDataType("BINARY")) { return Boolean.parseBoolean(value); } else { return getAdjustedValue(Double.parseDouble(value)); } } /** * @return the name */ public String getName() { return name; } }